Efficiently selecting multiple values in trigger - firebird2.5

I'd like to improve upon the following code because I don't care to have all these select statements if I can avoid it for this trigger. I require default values to be placed for a few fields during an INSERT. The code works, I'm just looking for other suggestions. Thanks!
SET TERM || ;
CREATE TRIGGER populate_defaults FOR units
ACTIVE
BEFORE INSERT
AS
BEGIN
new.value1 = (select value1 from defaults);
new.value2 = (select value2 from defaults);
new.value3 = (select value3 from defaults);
new.value4 = (select value4 from defaults);
new.value5 = (select value5 from defaults);
new.value6 = (select value6 from defaults);
new.value7 = (select value7 from defaults);
new.value8 = (select value8 from defaults);
new.value9 = (select value9 from defaults);
END ||
SET TERM ; ||

You can do a single select; try this:
SET TERM || ;
CREATE TRIGGER populate_defaults FOR units
ACTIVE
BEFORE INSERT
AS
BEGIN
select value1, value2, value3, value4
, value5, value6, value7, value8
, value9
from defaults
into new.value1, new.value2, new.value3, new.value4
, new.value5, new.value6, new.value7, new.value8
, new.value9
END ||
SET TERM ; ||
I just can't remember if the new have to be prefixed by a colon or not, that exercise is up to the reader.

Related

Create master view from array of tables passed as input to stored procedure Snowflake

I want to create a master view combining all the tables passed as input to Snowflake Stored procedure. Please help on how the code can be framed for this.
create or replace procedure TEST_PROC("SRC_DB" VARCHAR(30),
"SRC_SCHEMA" VARCHAR(30), "TGT_DB" VARCHAR(30), "TGT_SCHEMA"
VARCHAR(30))
RETURNS varchar
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
as
$$
var result = '';
var tab = 'TABLE1,TABLE2'
var get_tables = `
with cte as(select value from table(SPLIT_TO_TABLE
(('${tab}'),','))
) select value from cte;`
var tables_name_master=snowflake.execute ({sqlText: get_tables});
var lcols_agg = '';
while(tables_name_master.next()){
var table_value = tables_name_master.getColumnValue(1);
var column_list = `
WITH cte2 as (select COLUMN_NAME , listagg(TABLE_NAME, ', ')
within group (order by COLUMN_NAME) A
from ${SRC_DB}.information_schema.COLUMNS
where TABLE_SCHEMA= '${SRC_SCHEMA}' and TABLE_NAME in (select
value from table(SPLIT_TO_TABLE (('${tab}'),',')))
group by COLUMN_NAME order by COLUMN_NAME
),
cte3 as (select 'x' x, COLUMN_NAME,iff(contains(A,'${table_value}'),COLUMN_NAME,CONCAT('NULL AS \"',COLUMN_NAME,'\"')) valuess from cte2 order by COLUMN_NAME
)select listagg(valuess,',') final FROM cte3 GROUP BY x
`;
var rs = snowflake.execute({ sqlText: column_list });
while(rs.next()){
lcols_agg += "SELECT " + rs.getColumnValue(1) + " FROM "+ SRC_DB+"."+SRC_SCHEMA+"."+tables_name_master.getColumnValue(1) + "\n" +"UNION " +"\n"
}
}
var count1 = 0 ;
count1 = lcols_agg.length
result = lcols_agg.substring(0,(count1-7));
const create_union_view = `
create or replace view abcd AS ${result}
;`
var view_create = snowflake.execute({ sqlText: create_union_view });
view_create.next()
return result
$$
;
call SP_TEST('ABC','DEF','PQR','STU');
THis generates my final view statement as
CREATE OR REPLACE VIEW ABCD AS
COL1,COL2,COL3
UNION
NULL AS "COL2",NULL AS "COL3",COL1
Now due to mismatch of order od columns in union the view is throwing error while we do select * from abcd, any way we can have the columns of both tables in same order or any other work around?
You need to ORDER BY the function listagg after the cte3 expression:
instead of
select listagg(valuess,',') final FROM cte3 GROUP BY x
change it to:
select listagg(valuess,',') within group (order by valuess) final FROM cte3 GROUP BY x
See if that helps to resolve the order issue.

Dynamic Creation of External Tables in Synapse

I have a json string which is the result of Get Metadata activity in data factory in the following format: {"structure": [{"name": "Id","type": "String"},{"name": "IsDeleted","type": "Boolean"},{"name": "RowVersion","type": "Byte[]"}, {"name": "ModelId","type": "String"}]
This json is showing the schema of parquet file. I pass this json along with the parquet filename and uctnow() to a stored procedure to create a table of schema for that file with the following code
CREATE OR ALTER PROCEDURE [CreateExternalTables] (
#schema NVARCHAR (MAX), #tableName NVARCHAR(MAX), #ExecTime NVARCHAR(MAX)
) AS
BEGIN
IF OBJECT_ID('tempdb..#tables_to_create', 'U') IS NOT NULL
DROP TABLE #tables_to_create
CREATE TABLE #tables_to_create (
tableName NVARCHAR (MAX),
fieldOrder NVARCHAR (MAX),
fieldName NVARCHAR (MAX),
fieldType NVARCHAR (MAX),
translatedType NVARCHAR (MAX),
executeTime NVARCHAR (MAX)
)
BEGIN
WITH Fields (fieldOrder, fieldName, fieldType) AS (
SELECT
[key] AS fieldOrder,
JSON_VALUE([value], '$.name') AS fieldName,
JSON_VALUE([value], '$.type') AS fieldType
FROM
OPENJSON(#schema)
)
INSERT INTO
#tables_to_create(
tableName,
fieldOrder,
fieldName,
fieldType,
translatedType,
executeTime
)
SELECT
#tableName,
fieldOrder,
fieldName,
fieldType,
CASE
WHEN fieldType = 'Single' THEN 'real'
WHEN fieldType = 'Boolean' THEN 'bit'
WHEN fieldType = 'Double' THEN 'float'
WHEN fieldType = 'Int64' THEN 'bigint'
ELSE NULL
END AS translatedType,
#ExecTime
FROM
Fields
END
END;
The overall setup is like below:
But I receive the following error which I'm not sure why:
What I would like to do is to create this temo table in stored procedure in order to create external tables in a automatic way.
To be precise I'm following this link to do it:
create-external-dataset
Any help is highly appreciated.
UPDATE
After solving the first problem with #wBob 's help I follow the rest of the link to create the external tables with the following which I inserted into my stored procedure:
FROM
Fields
END
Declare #sqlCommand nvarchar(max);
Declare #folderPath nvarchar(max);
SET
#sqlCommand = 'IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[dbo].[' + #tableName + ']'') AND type in (N''U''))
CREATE EXTERNAL TABLE [dbo].[' + #tableName + '] ('
WHILE((SELECT COUNT(*) FROM #tables_to_create) > 0)
BEGIN
DECLARE #key int
SELECT
#key = MIN(fieldOrder)
FROM
#tables_to_create
WHERE
executeTime = #ExecTime
DECLARE #fieldName VARCHAR(50)
DECLARE #translatedType VARCHAR(50)
SELECT
#fieldName = fieldName,
#translatedType = translatedType
FROM
#tables_to_create
WHERE
fieldOrder = #key
AND executeTime = #ExecTime
SET
#sqlCommand = #sqlCommand + '
[' + #fieldName + '] [' + #translatedType + '] NULL'
DELETE FROM
#tables_to_create
WHERE
fieldOrder = #key
AND executeTime = #ExecTime
IF((SELECT COUNT(*) FROM #tables_to_create WHERE executeTime = #ExecTime) > 0)
SET
#sqlCommand = #sqlCommand + ', '
END
SET
#sqlCommand = #sqlCommand + '
)
WITH
(
LOCATION = ''/' + /main/json/ + ''',
DATA_SOURCE = DataLakeStaged,
FILE_FORMAT = StagedParquet
)'
EXEC(#sqlCommand)
END;
However I receive the following error:
As per the error message, queries are not supported in the conditions of WHILE statement in Azure Synapse Analytics. Dedicated SQL pools are MPP systems that have some slight differences from box-product SQL Server and Azure SQL DB and more generally, loops are a pattern that don't translate that well. You should consider creating a stored procedure that does not loop and leave any looping required to the For Each activity in Azure Data Factory (ADF) / Synapse Pipelines which can loop in parallel.
If you do require a loop, there are multiple examples of doing loops in dedicated SQL pools online, but there is a good example in the official documentation, reproduced here in the event the link moves:
-- First, create a temporary table containing a unique row number used to identify the individual statements:
CREATE TABLE #tbl
WITH
( DISTRIBUTION = ROUND_ROBIN
)
AS
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Sequence
, [name]
, 'UPDATE STATISTICS '+QUOTENAME([name]) AS sql_code
FROM sys.tables
;
-- Second, initialize the variables required to perform the loop:
DECLARE #nbr_statements INT = (SELECT COUNT(*) FROM #tbl)
, #i INT = 1
;
-- Now loop over statements executing them one at a time:
WHILE #i <= #nbr_statements
BEGIN
DECLARE #sql_code NVARCHAR(4000) = (SELECT sql_code FROM #tbl WHERE Sequence = #i);
EXEC sp_executesql #sql_code;
SET #i +=1;
END

Firebird and stored procedures: if exists then else

I'm trying to create a stored procedure for firebird 2.1 (this is the version which is to be used)
But am getting a bit stuck, so any help is appreciated.
The final version should compare 4 values agains the table, and retreive either the primaryid if the value exists, or create the new entry in the table, and return the new primaryid.
But I get stuck with only one value lookup, and it's not even using the variable yet.
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( A Varchar(64) )
RETURNS
( RESULT Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string')) then
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string' into :primaryid;
result = PRIMARYID;
ELSE
INSERT INTO TABLENAME (FIELD) VALUES ('Some string');
result = gen_id(GEN_TABLEID, 0);
END^
SET TERM ; ^
I get a "Token unknown" for the Else command.
Update after responses:
Now I want to use the 4 variables and return the 4 results.
I think I need a for loop to do so, but with firebird, the for function means something else.
So what would be the way to go?
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( value1 Varchar(64) )
RETURNS
( RESULT1 Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID FROM TABLENAME WHERE FIELD = :value1)) then
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = value1 into :result1;
ELSE BEGIN
result1 = gen_id(GEN_TABLEID, 1);
INSERT INTO TABLENAME (PRIMARYID, FIELD) VALUES (:result1, :value1);
END
suspend;
END^
SET TERM ; ^
As Tico already answered you have to use begin / end to group multiple statements in then / else part. The error abut column PRIMARYID being unknown is because you reference to it without having declared a local variable for it. Try this:
CREATE PROCEDURE TESTSP ( A Varchar(64) )
RETURNS ( RESULT Integer )
AS
BEGIN
-- initialize the result
Result = NULL;
-- check is the string already in table
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string' into :Result;
IF (Result is NULL) then
INSERT INTO TABLENAME(PRIMARYID, FIELD) VALUES(gen_id(GEN_TABLEID, 1), 'Some string') RETURNING PRIMARYID INTO :Result;
END^
I think your stored procedure should look like this:
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( A Varchar(64) )
RETURNS ( result Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID
FROM TABLENAME
WHERE FIELD = 'Some string')) then
SELECT PRIMARYID
FROM TABLENAME
WHERE FIELD = 'Some string'
into :result;
ELSE BEGIN
result = gen_id(GEN_TABLEID, 1);
INSERT INTO TABLENAME
(PRIMARYID, FIELD)
VALUES (:result, 'Some string');
END
END^
SET TERM ; ^
If you have multiple instruction for then and/ or else clause you must use BEGIN ... END-block!
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( A Varchar(64) )
RETURNS
( RESULT Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string')) then
BEGIN
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string' into :primaryid;
result = PRIMARYID;
END
ELSE
BEGIN
INSERT INTO TABLENAME (FIELD) VALUES ('Some string');
result = gen_id(GEN_TABLEID, 0);
END
END^
SET TERM ; ^

MySQL Procedure to retrieve missing auto increment ID of a table?

I create a procedure to find out missing id from a table, But it does not retrieve any result, below are the procedure
DELIMITER ||
DROP PROCEDURE IF EXISTS proc_rpt ||
CREATE PROCEDURE proc_rpt()
BEGIN
SET #minID = (SELECT MIN(`id`) FROM `tbl_film` WHERE `user_id`=13);
SET #maxID = (SELECT MAX(`id`) FROM `tbl_film` WHERE `user_id`=13);
REPEAT
SET #tableID = (SELECT `id` FROM `tbl_film` WHERE `id` = #minID);
IF (#tableID IS NULL) THEN
INSERT INTO temp_missing(`missing_id`) VALUES (#tableID);
END IF;
SET #minID = #minID + 1;
UNTIL (#minID <= #maxID)
END REPEAT; // earlier I was missing this line which was giving error in Procedure.
END; ||
Then i call above
CALL rpt_proc();
But there are no result on tbl_missing while i can see there are many missing values of film_id in tbl_film. Will someone please tell me what I am doing wrong?
Have a try with this code.
DELIMITER ||
DROP PROCEDURE IF EXISTS proc_rpt ||
CREATE PROCEDURE proc_rpt()
BEGIN
DECLARE maxId int;
DECLARE minId int;
DECLARE curId int;
SELECT MIN(`id`) INTO maxId FROM `tbl_film` WHERE `user_id`=13;
SELECT MAX(`id`) INTO minId FROM `tbl_film` WHERE `user_id`=13;
REPEAT
SELECT `id` INTO curId FROM `tbl_film` WHERE `id` = #minID);
IF (curId IS NULL) THEN
INSERT INTO temp_missing(`missing_id`) VALUES (curId);
END IF;
SET minId = minId + 1;
UNTIL (minId <= maxId)
END REPEAT; // earlier I was missing this line which was giving error in Procedure.
END; ||

Advantage Database Server: slow stored procedure performance

I have a question about a performance of stored procedures in the ADS. I created a simple database with the following structure:
CREATE TABLE MainTable
(
Id INTEGER PRIMARY KEY,
Name VARCHAR(50),
Value INTEGER
);
CREATE UNIQUE INDEX MainTableName_UIX ON MainTable ( Name );
CREATE TABLE SubTable
(
Id INTEGER PRIMARY KEY,
MainId INTEGER,
Name VARCHAR(50),
Value INTEGER
);
CREATE INDEX SubTableMainId_UIX ON SubTable ( MainId );
CREATE UNIQUE INDEX SubTableName_UIX ON SubTable ( Name );
CREATE PROCEDURE CreateItems
(
MainName VARCHAR ( 20 ),
SubName VARCHAR ( 20 ),
MainValue INTEGER,
SubValue INTEGER,
MainId INTEGER OUTPUT,
SubId INTEGER OUTPUT
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
DECLARE #SubName VARCHAR ( 20 );
DECLARE #MainValue INTEGER;
DECLARE #SubValue INTEGER;
DECLARE #MainId INTEGER;
DECLARE #SubId INTEGER;
#MainName = (SELECT MainName FROM __input);
#SubName = (SELECT SubName FROM __input);
#MainValue = (SELECT MainValue FROM __input);
#SubValue = (SELECT SubValue FROM __input);
#MainId = (SELECT MAX(Id)+1 FROM MainTable);
#SubId = (SELECT MAX(Id)+1 FROM SubTable );
INSERT INTO MainTable (Id, Name, Value) VALUES (#MainId, #MainName, #MainValue);
INSERT INTO SubTable (Id, Name, MainId, Value) VALUES (#SubId, #SubName, #MainId, #SubValue);
INSERT INTO __output SELECT #MainId, #SubId FROM system.iota;
END;
CREATE PROCEDURE UpdateItems
(
MainName VARCHAR ( 20 ),
MainValue INTEGER,
SubValue INTEGER
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
DECLARE #MainValue INTEGER;
DECLARE #SubValue INTEGER;
DECLARE #MainId INTEGER;
#MainName = (SELECT MainName FROM __input);
#MainValue = (SELECT MainValue FROM __input);
#SubValue = (SELECT SubValue FROM __input);
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = #MainName);
UPDATE MainTable SET Value = #MainValue WHERE Id = #MainId;
UPDATE SubTable SET Value = #SubValue WHERE MainId = #MainId;
END;
CREATE PROCEDURE SelectItems
(
MainName VARCHAR ( 20 ),
CalculatedValue INTEGER OUTPUT
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
#MainName = (SELECT MainName FROM __input);
INSERT INTO __output SELECT m.Value * s.Value FROM MainTable m INNER JOIN SubTable s ON m.Id = s.MainId WHERE m.Name = #MainName;
END;
CREATE PROCEDURE DeleteItems
(
MainName VARCHAR ( 20 )
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
DECLARE #MainId INTEGER;
#MainName = (SELECT MainName FROM __input);
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = #MainName);
DELETE FROM SubTable WHERE MainId = #MainId;
DELETE FROM MainTable WHERE Id = #MainId;
END;
Actually, the problem I had - even so light stored procedures work very-very slow (about 50-150 ms) relatively to plain queries (0-5ms). To test the performance, I created a simple test (in F# using ADS ADO.NET provider):
open System;
open System.Data;
open System.Diagnostics;
open Advantage.Data.Provider;
let mainName = "main name #";
let subName = "sub name #";
// INSERT
let cmdTextScriptInsert = "
DECLARE #MainId INTEGER;
DECLARE #SubId INTEGER;
#MainId = (SELECT MAX(Id)+1 FROM MainTable);
#SubId = (SELECT MAX(Id)+1 FROM SubTable );
INSERT INTO MainTable (Id, Name, Value) VALUES (#MainId, :MainName, :MainValue);
INSERT INTO SubTable (Id, Name, MainId, Value) VALUES (#SubId, :SubName, #MainId, :SubValue);
SELECT #MainId, #SubId FROM system.iota;";
let cmdTextProcedureInsert = "CreateItems";
// UPDATE
let cmdTextScriptUpdate = "
DECLARE #MainId INTEGER;
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = :MainName);
UPDATE MainTable SET Value = :MainValue WHERE Id = #MainId;
UPDATE SubTable SET Value = :SubValue WHERE MainId = #MainId;";
let cmdTextProcedureUpdate = "UpdateItems";
// SELECT
let cmdTextScriptSelect = "
SELECT m.Value * s.Value FROM MainTable m INNER JOIN SubTable s ON m.Id = s.MainId WHERE m.Name = :MainName;";
let cmdTextProcedureSelect = "SelectItems";
// DELETE
let cmdTextScriptDelete = "
DECLARE #MainId INTEGER;
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = :MainName);
DELETE FROM SubTable WHERE MainId = #MainId;
DELETE FROM MainTable WHERE Id = #MainId;";
let cmdTextProcedureDelete = "DeleteItems";
let cnnStr = #"data source=D:\DB\test.add; ServerType=local; user id=adssys; password=***;";
let cnn = new AdsConnection(cnnStr);
try
cnn.Open();
let cmd = cnn.CreateCommand();
let parametrize ix prms =
cmd.Parameters.Clear();
let addParam = function
| "MainName" -> cmd.Parameters.Add(":MainName" , mainName + ix.ToString()) |> ignore;
| "SubName" -> cmd.Parameters.Add(":SubName" , subName + ix.ToString() ) |> ignore;
| "MainValue" -> cmd.Parameters.Add(":MainValue", ix * 3 ) |> ignore;
| "SubValue" -> cmd.Parameters.Add(":SubValue" , ix * 7 ) |> ignore;
| _ -> ()
prms |> List.iter addParam;
let runTest testData =
let (cmdType, cmdName, cmdText, cmdParams) = testData;
let toPrefix cmdType cmdName =
let prefix = match cmdType with
| CommandType.StoredProcedure -> "Procedure-"
| CommandType.Text -> "Script -"
| _ -> "Unknown -"
in prefix + cmdName;
let stopWatch = new Stopwatch();
let runStep ix prms =
parametrize ix prms;
stopWatch.Start();
cmd.ExecuteNonQuery() |> ignore;
stopWatch.Stop();
cmd.CommandText <- cmdText;
cmd.CommandType <- cmdType;
let startId = 1500;
let count = 10;
for id in startId .. startId+count do
runStep id cmdParams;
let elapsed = stopWatch.Elapsed;
Console.WriteLine("Test '{0}' - total: {1}; per call: {2}ms", toPrefix cmdType cmdName, elapsed, Convert.ToInt32(elapsed.TotalMilliseconds)/count);
let lst = [
(CommandType.Text, "Insert", cmdTextScriptInsert, ["MainName"; "SubName"; "MainValue"; "SubValue"]);
(CommandType.Text, "Update", cmdTextScriptUpdate, ["MainName"; "MainValue"; "SubValue"]);
(CommandType.Text, "Select", cmdTextScriptSelect, ["MainName"]);
(CommandType.Text, "Delete", cmdTextScriptDelete, ["MainName"])
(CommandType.StoredProcedure, "Insert", cmdTextProcedureInsert, ["MainName"; "SubName"; "MainValue"; "SubValue"]);
(CommandType.StoredProcedure, "Update", cmdTextProcedureUpdate, ["MainName"; "MainValue"; "SubValue"]);
(CommandType.StoredProcedure, "Select", cmdTextProcedureSelect, ["MainName"]);
(CommandType.StoredProcedure, "Delete", cmdTextProcedureDelete, ["MainName"])];
lst |> List.iter runTest;
finally
cnn.Close();
And I'm getting the following results:
Test 'Script -Insert' - total: 00:00:00.0292841; per call: 2ms
Test 'Script -Update' - total: 00:00:00.0056296; per call: 0ms
Test 'Script -Select' - total: 00:00:00.0051738; per call: 0ms
Test 'Script -Delete' - total: 00:00:00.0059258; per call: 0ms
Test 'Procedure-Insert' - total: 00:00:01.2567146; per call: 125ms
Test 'Procedure-Update' - total: 00:00:00.7442440; per call: 74ms
Test 'Procedure-Select' - total: 00:00:00.5120446; per call: 51ms
Test 'Procedure-Delete' - total: 00:00:01.0619165; per call: 106ms
The situation with the remote server is much better, but still a great gap between plaqin queries and stored procedures:
Test 'Script -Insert' - total: 00:00:00.0709299; per call: 7ms
Test 'Script -Update' - total: 00:00:00.0161777; per call: 1ms
Test 'Script -Select' - total: 00:00:00.0258113; per call: 2ms
Test 'Script -Delete' - total: 00:00:00.0166242; per call: 1ms
Test 'Procedure-Insert' - total: 00:00:00.5116138; per call: 51ms
Test 'Procedure-Update' - total: 00:00:00.3802251; per call: 38ms
Test 'Procedure-Select' - total: 00:00:00.1241245; per call: 12ms
Test 'Procedure-Delete' - total: 00:00:00.4336334; per call: 43ms
Is it any chance to improve the SP performance? Please advice.
ADO.NET driver version - 9.10.2.9
Server version - 9.10.0.9 (ANSI - GERMAN, OEM - GERMAN)
Thanks!
The Advantage v10 beta includes a variety of performance improvements directly targeting stored procedure performance. Here are some things to consider with the current shipping version, however:
In your CreateItems procedure it would likely be more efficient to replace
#MainName = (SELECT MainName FROM __input);
#SubName = (SELECT SubName FROM __input);
#MainValue = (SELECT MainValue FROM __input);
#SubValue = (SELECT SubValue FROM __input);
with the use of a single cursor to retrieve all parameters:
DECLARE input CURSOR;
OPEN input as SELECT * from __input;
FETCH input;
#MainName = input.MainName;
#SubName = input.SubName;
#MainValue = input.MainValue;
#SubValue = input.SubValue;
CLOSE input;
That will avoid 3 statement parse/semantic/optimize/execute operations just to retrieve the input parameters (I know, we really need to eliminate the __input table altogether).
The SelectItems procedure is rarely ever going to be as fast as a select from the client, especially in this case where it really isn't doing anything except abstracting a parameter value (which can easily be done on the client). Remember that because it is a JOIN, the SELECT to fill the __output table is going to be a static cursor (meaning an internal temporary file for the server to create and fill), but now in addition you have the __output table which is yet another temporary file for the server, plus you have additional overhead to populate this __output table with data that has already been place in the static cursor temp table, just for the sake of duplicating it (server could do a better job of detecting this and replacing __output with the existing static cursor reference, but it currently doesn't).
I will try to make some time to try your procedures on version 10. If you have the test tables you used in your testing feel free to zip them up and send them to Advantage#iAnywhere.com and put attn:JD in the subject.
There is one change that would help with the CreateItems procedure. Change the following two statements:
#MainId = (SELECT MAX(Id)+1 FROM MainTable);
#SubId = (SELECT MAX(Id)+1 FROM SubTable );
To this:
#MainId = (SELECT MAX(Id) FROM MainTable);
#MainId = #MainId + 1;
#SubId = (SELECT MAX(Id) FROM SubTable );
#SubId = #SubId + 1;
I looked at the query plan information (in Advantage Data Architect) for the first version of that statement. It looks like the optimizer does not break that MAX(id)+1 into the component pieces. The statement select max(id) from maintable can be optimized using the index on the ID field. It appears that max(id)+1 is not optimized. So making that change would be fairly significant particularly as the table grows.
Another thing that might help is to add a CACHE PREPARE ON; statement to the top of each script. This can help with certain procedures when running them multiple times.
Edit The Advantage v10 beta was released today. So I ran your CreateItems procedure with both v9.1 and the new beta version. I ran 1000 iterations against the remote server. The speed difference was significant:
v9.1: 101 seconds
v10 beta: 2.2 seconds
Note that I ran a version with the select max(id) change I described above. This testing was on my fairly old development PC.

Resources