在ibm db2上的游标中执行Immediate

时间:2015-03-26 21:06:08

标签: cursor db2 db2-400 execute-immediate

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

1 个答案:

答案 0 :(得分:0)

首先,您不需要动态准备第一个声明。

其次,你不能在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之前以独占模式锁定表;否则,在执行STMT2和STMT3之间可能会添加一个具有更高值的记录。