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

Posted

技术标签:

【中文标题】在 ibm db2 上的游标中立即执行【英文标题】:Execute Immediate in cursor on ibm db2 【发布时间】:2015-03-26 21:06:08 【问题描述】:

我在创建一个 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】:

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

其次,您不能在 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的执行之间添加了更高值的记录。

【讨论】:

谢谢,它工作得很好。我只期待我做错了什么的详细信息,而不是完整的代码。但我想我做错了很多,这很有帮助,谢谢。 很高兴我能帮上忙。实际上,我过去也做过同样的任务。但我使用 RPG 和系统 API 来确定列名。因此,与纯 SQL 解决方案进行比较很有趣。

以上是关于在 ibm db2 上的游标中立即执行的主要内容,如果未能解决你的问题,请参考以下文章

DB2 - 在光标中聚合大小写

如何在 IBM Data Studio 中查看 DB2 存储过程的解释计划?

db2 update dbm cfg 立即

如何在 ibm_cloud 上的 db2 中编写 sql 查询

十进制字段在 IBM AS400 的窗口上显示为负数

IBM DB2 V9 License 安装