我在创建一个SP时遇到困难,在该SP中我传入表的名称,并查询SYS2库以确定它是否有自动递增字段。如果是,我会查询表中该字段的最大值,然后更改表,使下一个使用的值是该结果加1。这是在将生产数据迁移到开发时使用的。
我不确定是否可以使用"立即执行"作为游标声明的一部分。一般来说,我对db2还是相当陌生的,更不用说对IBM了。因此,如有任何协助,我们将不胜感激。如果游标声明中不允许使用"立即执行",我该如何执行?
我在Cursor声明中得到了错误(第10行),但这里是我得到的确切错误代码:
SQL State: 42601
Vendor Code: -199
Message: [SQL0199] Keyword IMMEDIATE not expected. Valid tokens: <END-OF-STATEMENT>. Cause . . . . . : The keyword IMMEDIATE was not expected here. A syntax error was detected at keyword IMMEDIATE. The partial list of valid tokens is <END-OF-STATEMENT>. This list assumes that the statement is correct up to the unexpected keyword. The error may be earlier in the statement but the syntax of the statement seems to be valid up to this point. Recovery . . . : Examine the SQL statement in the area of the specified keyword. A colon or SQL delimiter may be missing. SQL requires reserved words to be delimited when they are used as a name. Correct the SQL statement and try the request again.
最后是我的SP
/* Creating procedure DLLIB.SETNXTINC@ */
CREATE OR REPLACE PROCEDURE DLLIB.SETNXTINC@(IN TABLE CHARACTER (10) ) LANGUAGE SQL CONTAINS SQL PROGRAM TYPE SUB CONCURRENT ACCESS RESOLUTION DEFAULT DYNAMIC RESULT SETS 0 OLD SAVEPOINT LEVEL COMMIT ON RETURN NO
SET @STMT1 = 'SELECT COLUMN_NAME ' ||
'FROM QSYS2.SYSCOLUMNS ' ||
'WHERE TABLE_SCHEMA =''DLLIB'' and table_name = ''' || TRIM(TABLE) || '''' ||
'AND HAS_DEFAULT = ''I'' ' ||
'OR HAS_DEFAULT = ''J'';';
DECLARE cursor1 CURSOR FOR
EXECUTE IMMEDIATE @STMT1;
OPEN cursor1;
WHILE (sqlcode == 0){
FETCH cursor1 INTO field;
SET @STMT2 = 'ALTER TABLE DLLIB.' || TRIM(TABLE) || ''' ' ||
'ALTER COLUMN ' || TRIM(field) || ' RESTART WITH ( ' ||
'SELECT MAX(' || TRIM(field) || ') ' ||
'FROM DLLIB.' || TRIM(TABLE) || ');';
EXECUTE IMMEDIATE @STMT2;
};;
/* Setting label text for DLLIB.SETNXTINC@ */
LABEL ON ROUTINE DLLIB.SETNXTINC@ ( CHAR() ) IS 'Set the next auto-increment';
/* Setting comment text for DLLIB.SETNXTINC@ */
COMMENT ON PARAMETER ROUTINE DLLIB.SETNXTINC@ ( CHAR() ) (TABLE IS 'Table from DLLIB' ) ;
首先,您不需要动态准备第一条语句。
其次,您不能在RESTARTWITH中使用SELECT,您必须使用2条语句
第三,如果使用VARCHAR而不是CHAR,则不需要使用任何TRIM()的
最后,使用TABLE作为参数名称是不好的做法,因为它是一个保留字。
你想要这样的
CREATE OR REPLACE PROCEDURE QGPL.SETNXTINC@(IN MYTABLE VARCHAR (128) )
LANGUAGE SQL
MODIFIES SQL DATA
PROGRAM TYPE SUB
CONCURRENT ACCESS RESOLUTION DEFAULT
DYNAMIC RESULT SETS 0
OLD SAVEPOINT LEVEL
COMMIT ON RETURN NO
BEGIN
declare mycolumn varchar(128);
declare stmt2 varchar(1000);
declare stmt3 varchar(1000);
declare mymaxvalue integer;
-- Table known at runtime, a static statement is all we need
SELECT COLUMN_NAME INTO mycolumn
FROM QSYS2.SYSCOLUMNS
WHERE TABLE_SCHEMA = 'DLLIB'
AND TABLE_NAME = mytable
AND HAS_DEFAULT = 'I'
OR HAS_DEFAULT = 'J';
-- Need to use a dynamic statement here
-- as the affected table is not known till runtime
-- need VALUES INTO as SELECT INTO can not be used dynamically
SET STMT2 = 'VALUES (SELECT MAX(' || mycolumn || ') ' ||
'FROM DLLIB.' || mytable || ')' || 'INTO ?';
PREPARE S2 from stmt2;
EXECUTE S2 using mymaxvalue;
-- we want to restart with a value 1 more than the current max
SET mymaxvalue = mymaxvalue + 1;
-- Need to use a dynamic statement here
-- as the affected table is not known till runtime
SET STMT3 = 'ALTER TABLE DLLIB.' || mytable || ' ALTER COLUMN '
|| mycolumn || ' RESTART WITH ' || char(mymaxvalue);
EXECUTE IMMEDIATE STMT3;
END;
还有一件事需要考虑,您可能希望在运行STMT2之前以独占模式锁定表;否则有可能在STMT2和STMT3的执行之间添加了具有更高值的记录。