在 ibm db2 上的游标中立即执行

Execute Immediate in cursor on ibm db2

我在创建一个 SP 时遇到困难,我在其中传递了一个 table 的名称并查询 SYS2 库以查明它是否具有自动递增字段。如果确实如此,我会在 table 中查询该字段的最大值,然后更改 table,以便下一个使用的值是该结果加 1。这在将生产数据迁移到开发时使用.

我不确定是否可以使用 "Execute Immediate" 作为游标声明的一部分。总的来说,我对 db2 还是很陌生,更不用说 IBM 了。因此,我们将不胜感激任何帮助。如果游标声明中不允许使用 "Execute Immediate",我该怎么做?

我在 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' ) ;

首先,您不需要动态准备第一个语句。

其次,您不能在 RESTART WITH 中使用 SELECT,您必须使用 2 个语句

第三,如果您使用 VARCHAR 而不是 CHAR,则不需要使用任何 TRIM()s

最后,使用 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 之前以独占模式锁定 table;否则有可能在执行 STMT2 和 STMT3 之间添加了具有更高值的记录。