Loop 中的 DB2 更新不执行
DB2 update in Loop does not execute
我有以下代码:
BEGIN
DECLARE cmd VARCHAR(1024); DECLARE attr CHAR(10);
DECLARE attr2 CHAR(10);
DECLARE at_end INTEGER DEFAULT 0;
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE updstr1 VARCHAR(1024);
DECLARE updstr2 VARCHAR(1024);
DECLARE c1 CURSOR FOR SELECT cmd, attr, attr2
FROM commandtbl2;
DECLARE CONTINUE HANDLER FOR not_found SET at_end = 1;
SET updstr1 = 'update commandtbl3 t2 set t2.XXXX = attr where t2.cmd=cmd';
SET updstr2 = 'update commandtbl3 t2 set t2.XXXX= attr2 where t2.cmd=cmd';
OPEN c1;
ins_loop: LOOP
FETCH c1 INTO cmd, attr, attr2;
IF at_end = 1 THEN
LEAVE ins_loop;
ELSEIF cmd != '' THEN
ITERATE ins_loop;
END IF;
set updstr1= REPLACE(updstr1,'XXXX','attr');
set updstr2= REPLACE(updstr2,'XXXX','attr2');
EXECUTE IMMEDIATE updstr1;
EXECUTE IMMEDIATE updstr2;
END LOOP;
CLOSE c1;
END@
它全部运行没有错误,但是当我检查 commandtbl3 时,没有任何更新。所有值均为 NULL,但应被替换。两个表中有 7 行,其中 5 行具有匹配的 cmd 值,因此应该更新这 5 行。
我不确定是替换不起作用还是立即执行。
通过调试我发现,updstr1 和 2 变量在替换后是空的...但是为什么?
你能帮帮我吗?
谢谢,
流浪汉
此处要求的是 commandtbl3 和 2 的定义:
CREATE TABLE "TEST"."COMMANDTBL3" (
"ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 2 NO ORDER ),
"CMD" VARCHAR(1024 OCTETS) NOT NULL,
"ATTR" CHAR(10 OCTETS),
"ATTR2" CHAR(10 OCTETS),
CONSTRAINT "CC1455789123996" PRIMARY KEY
("ID")
)
CREATE TABLE "TEST"."COMMANDTBL2" (
"ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 2 NO ORDER ),
"CMD" VARCHAR(1024 OCTETS) NOT NULL,
"ATTR" CHAR(10 OCTETS),
"ATTR2" CHAR(10 OCTETS),
CONSTRAINT "CC1455789123996" PRIMARY KEY
("ID")
)
COmmandtbl3 的 CMD 等于 commandtbl2 除了 2 行(总共 7 行)并且 attr 和 attr2 在 commandtbl3 中是 NMULL。我希望通过更新将 commandtbl2 attr 和 attr2 的值写入 commandtbl3 并替换命令,这样我就可以使用占位符 XXXX
这是工作代码:
如果有人遇到同样的问题,这里是工作代码,即使从一开始就改变了(现在 while 循环等):
BEGIN
DECLARE EOF INTEGER DEFAULT 0;
DECLARE cmd VARCHAR(1024); DECLARE attr CHAR(10);
DECLARE attr2 CHAR(10);
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE updstr1 VARCHAR(1024);
DECLARE updstr2 VARCHAR(1024);
DECLARE stmt1 STATEMENT;
DECLARE stmt2 STATEMENT;
DECLARE c1 CURSOR FOR SELECT cmd, attr, attr2
FROM commandtbl2 order by cmd;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET EOF = 1;
SET updstr1 = 'update commandtbl3 t2 set t2.XXXX = ? where t2.cmd= ?';
SET updstr2 = 'update commandtbl3 t2 set t2.XXXX= ? where t2.cmd= ?';
set updstr1= REPLACE(updstr1,'XXXX','attr');
set updstr2= REPLACE(updstr2,'XXXX','attr2');
insert into temptbl(text1,text2) values(updstr1,updstr2);
prepare stmt1 from updstr1;
prepare stmt2 from updstr2;
OPEN c1;
WHILE EOF = 0 DO
FETCH from c1 INTO cmd, attr, attr2;
EXECUTE stmt1 using attr, cmd;
EXECUTE stmt2 using attr2, cmd;
END WHILE;
CLOSE c1;
END@
您的代码中存在一些逻辑错误。
在你的循环中:
IF at_end = 1 THEN
LEAVE ins_loop;
ELSEIF cmd != '' THEN
ITERATE ins_loop;
END IF;
ELSEIF
语句告诉 DB2,如果 cmd
(来自 commandtbl2
)是任何东西 但 是一个空字符串,它应该跳过本次循环迭代中的剩余步骤(即什么都不做,只跳回到循环开始时的 FETCH
语句)。
这可能就是您没有看到任何事情发生的原因。
正如@mustaccio 在上面的评论中提到的,看起来您假设 DB2 会在您执行 EXECUTE IMMEDIATE
语句时神奇地用它们的值替换变量。您的声明:
update commandtbl3 t2 set t2.XXXX = attr where t2.cmd=cmd
您的代码将替换 XXXX,但确实 看起来 就像您假设 = attr
和 =cmd
也将替换为在 FETCH
语句中设置的值。事实并非如此。
看这个例子:
DECLARE GLOBAL TEMPORARY TABLE t1 (c1 int)
ON COMMIT PRESERVE ROWS WITH REPLACE
@
BEGIN
DECLARE v1 INT;
DECLARE vSQL VARCHAR(128);
SET vSQL = 'INSERT INTO SESSION.T1 values (v1)';
SET v1 = 12;
EXECUTE IMMEDIATE vSQL;
END
@
当数据库到达EXECUTE IMMEDIATE
语句时,它不会在运行时将v1替换为v1的值。它将执行确切的语句 INSERT INTO SESSION.T1 values (v1)
。这当然会失败,因为 v1 对数据库没有任何意义。
在您的情况下,EXECUTE IMMEDIATE
语句不会 失败 因为 attr
和 cmd
确实对数据库有意义 – 它们是 table commandtbl3
中的列名。因此,您执行的更新语句将是(在 REPLACE 语句之后):
update commandtbl3 t2 set t2.attr = attr where t2.cmd=cmd
update commandtbl3 t2 set t2.attr2 = attr2 where t2.cmd=cmd
这些基本上是 no-ops - 就像 UPDATE T1 SET C1 = C1 WHERE C2 = C2
.
这是一个很好的例子,说明了为什么不应使用与数据库中的列相同的名称来声明变量。它增加了混乱并可以隐藏逻辑错误。
我有以下代码:
BEGIN
DECLARE cmd VARCHAR(1024); DECLARE attr CHAR(10);
DECLARE attr2 CHAR(10);
DECLARE at_end INTEGER DEFAULT 0;
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE updstr1 VARCHAR(1024);
DECLARE updstr2 VARCHAR(1024);
DECLARE c1 CURSOR FOR SELECT cmd, attr, attr2
FROM commandtbl2;
DECLARE CONTINUE HANDLER FOR not_found SET at_end = 1;
SET updstr1 = 'update commandtbl3 t2 set t2.XXXX = attr where t2.cmd=cmd';
SET updstr2 = 'update commandtbl3 t2 set t2.XXXX= attr2 where t2.cmd=cmd';
OPEN c1;
ins_loop: LOOP
FETCH c1 INTO cmd, attr, attr2;
IF at_end = 1 THEN
LEAVE ins_loop;
ELSEIF cmd != '' THEN
ITERATE ins_loop;
END IF;
set updstr1= REPLACE(updstr1,'XXXX','attr');
set updstr2= REPLACE(updstr2,'XXXX','attr2');
EXECUTE IMMEDIATE updstr1;
EXECUTE IMMEDIATE updstr2;
END LOOP;
CLOSE c1;
END@
它全部运行没有错误,但是当我检查 commandtbl3 时,没有任何更新。所有值均为 NULL,但应被替换。两个表中有 7 行,其中 5 行具有匹配的 cmd 值,因此应该更新这 5 行。
我不确定是替换不起作用还是立即执行。
通过调试我发现,updstr1 和 2 变量在替换后是空的...但是为什么?
你能帮帮我吗? 谢谢, 流浪汉
此处要求的是 commandtbl3 和 2 的定义:
CREATE TABLE "TEST"."COMMANDTBL3" (
"ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 2 NO ORDER ),
"CMD" VARCHAR(1024 OCTETS) NOT NULL,
"ATTR" CHAR(10 OCTETS),
"ATTR2" CHAR(10 OCTETS),
CONSTRAINT "CC1455789123996" PRIMARY KEY
("ID")
)
CREATE TABLE "TEST"."COMMANDTBL2" (
"ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 2 NO ORDER ),
"CMD" VARCHAR(1024 OCTETS) NOT NULL,
"ATTR" CHAR(10 OCTETS),
"ATTR2" CHAR(10 OCTETS),
CONSTRAINT "CC1455789123996" PRIMARY KEY
("ID")
)
COmmandtbl3 的 CMD 等于 commandtbl2 除了 2 行(总共 7 行)并且 attr 和 attr2 在 commandtbl3 中是 NMULL。我希望通过更新将 commandtbl2 attr 和 attr2 的值写入 commandtbl3 并替换命令,这样我就可以使用占位符 XXXX
这是工作代码:
如果有人遇到同样的问题,这里是工作代码,即使从一开始就改变了(现在 while 循环等):
BEGIN
DECLARE EOF INTEGER DEFAULT 0;
DECLARE cmd VARCHAR(1024); DECLARE attr CHAR(10);
DECLARE attr2 CHAR(10);
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE updstr1 VARCHAR(1024);
DECLARE updstr2 VARCHAR(1024);
DECLARE stmt1 STATEMENT;
DECLARE stmt2 STATEMENT;
DECLARE c1 CURSOR FOR SELECT cmd, attr, attr2
FROM commandtbl2 order by cmd;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET EOF = 1;
SET updstr1 = 'update commandtbl3 t2 set t2.XXXX = ? where t2.cmd= ?';
SET updstr2 = 'update commandtbl3 t2 set t2.XXXX= ? where t2.cmd= ?';
set updstr1= REPLACE(updstr1,'XXXX','attr');
set updstr2= REPLACE(updstr2,'XXXX','attr2');
insert into temptbl(text1,text2) values(updstr1,updstr2);
prepare stmt1 from updstr1;
prepare stmt2 from updstr2;
OPEN c1;
WHILE EOF = 0 DO
FETCH from c1 INTO cmd, attr, attr2;
EXECUTE stmt1 using attr, cmd;
EXECUTE stmt2 using attr2, cmd;
END WHILE;
CLOSE c1;
END@
您的代码中存在一些逻辑错误。
在你的循环中:
IF at_end = 1 THEN LEAVE ins_loop; ELSEIF cmd != '' THEN ITERATE ins_loop; END IF;
ELSEIF
语句告诉 DB2,如果cmd
(来自commandtbl2
)是任何东西 但 是一个空字符串,它应该跳过本次循环迭代中的剩余步骤(即什么都不做,只跳回到循环开始时的FETCH
语句)。这可能就是您没有看到任何事情发生的原因。
正如@mustaccio 在上面的评论中提到的,看起来您假设 DB2 会在您执行
EXECUTE IMMEDIATE
语句时神奇地用它们的值替换变量。您的声明:update commandtbl3 t2 set t2.XXXX = attr where t2.cmd=cmd
您的代码将替换 XXXX,但确实 看起来 就像您假设
= attr
和=cmd
也将替换为在FETCH
语句中设置的值。事实并非如此。看这个例子:
DECLARE GLOBAL TEMPORARY TABLE t1 (c1 int) ON COMMIT PRESERVE ROWS WITH REPLACE @ BEGIN DECLARE v1 INT; DECLARE vSQL VARCHAR(128); SET vSQL = 'INSERT INTO SESSION.T1 values (v1)'; SET v1 = 12; EXECUTE IMMEDIATE vSQL; END @
当数据库到达
EXECUTE IMMEDIATE
语句时,它不会在运行时将v1替换为v1的值。它将执行确切的语句INSERT INTO SESSION.T1 values (v1)
。这当然会失败,因为 v1 对数据库没有任何意义。在您的情况下,
EXECUTE IMMEDIATE
语句不会 失败 因为attr
和cmd
确实对数据库有意义 – 它们是 tablecommandtbl3
中的列名。因此,您执行的更新语句将是(在 REPLACE 语句之后):update commandtbl3 t2 set t2.attr = attr where t2.cmd=cmd update commandtbl3 t2 set t2.attr2 = attr2 where t2.cmd=cmd
这些基本上是 no-ops - 就像
UPDATE T1 SET C1 = C1 WHERE C2 = C2
.这是一个很好的例子,说明了为什么不应使用与数据库中的列相同的名称来声明变量。它增加了混乱并可以隐藏逻辑错误。