DECLARE
ins NUMBER := 0;
upd NUMBER := 0;
CURSOR c1 IS
SELECT cid
FROM tbl_cust
WHERE cid
IN ('1','2','3','4');
BEGIN
FOR rec IN c1 LOOP
INSERT INTO tbl2 (id_tbl2, name_tbl2)
VALUES(rec.cid, DECODE(rec.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D'));
ins := ins + 1;
END LOOP;
dbms_output.put_line('Updated: ' || ins);
dbms_output.put_line('Inserted: ' || upd);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
FOR rec IN c1 LOOP
UPDATE tbl2 set name_tbl2 = DECODE(rec.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D')
WHERE cust_cust_code = rec.cid;
upd := upd + 1;
END LOOP;
dbms_output.put_line('Updated: ' || upd);
dbms_output.put_line('Inserted: ' || ins);
END;
第一次运行此代码并且tbl_cust中没有与tbl2相同的值时,输出如下:
更新:0
已插入:4
这是正确的。在第二轮中,输出如下:
更新:4
已插入:0
这也是正确的。 但是当我从数据库中删除例如cid'1'和'3'时,结果是这样的:
更新:4
已插入:2
删除cid'1'和'3'之后,我想要的输出是这样的:
更新:2
已插入:2
答案 0 :(得分:1)
在更新的情况下,无需循环遍历所有记录。 这就是为什么你得到4次更新而不是预期的2次。
相反,您应该仅在DUP_VAL_ON_INDEX
异常的情况下更新,并且仅在导致异常的行中更新。
尝试这样的事情。
DECLARE
ins NUMBER := 0;
upd NUMBER := 0;
CURSOR c1 IS
SELECT cid
FROM tbl_cust
WHERE cid
IN ('1','2','3','4');
BEGIN
FOR rec IN c1 LOOP
begin
INSERT INTO tbl2 (id_tbl2, name_tbl2)
VALUES(rec.cid, DECODE(rec.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D'));
ins := ins + 1;
EXCEPTION WHEN DUP_VAL_ON_INDEX THEN
UPDATE tbl2 set name_tbl2 = DECODE(rec.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D'));
WHERE cust_cust_code = rec.cid;
upd := upd + 1;
continue;
end;
END LOOP;
dbms_output.put_line('Updated: ' || upd);
dbms_output.put_line('Inserted: ' || ins);
END;
答案 1 :(得分:1)
你的逻辑存在缺陷。当一次插入失败时,您的代码将进入异常处理程序,启动一个新游标并更新所有记录。程序然后在第一次失败后终止。所以你的第三次运行就是这样:
Inserted: 1 -- insert where CID=1 succeeded
Updated: 4 -- insert where CID=2 failed
除了实际更新只有三个TBL2记录,因为您计算循环中的行数而不是更新了多少行。你应该做的是upd := update + sql%count;
,它只在实际更新行时递增计数器。
你想做的是:
DECLARE
ins NUMBER := 0;
upd NUMBER := 0;
CURSOR c1 IS
SELECT cid
FROM tbl_cust
WHERE cid
IN ('1','2','3','4');
BEGIN
FOR rec IN c1 LOOP
begin
INSERT INTO tbl2 (id_tbl2, name_tbl2)
VALUES(rec.cid, DECODE(rec.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D'));
ins := ins + sql%rowcount;
exception
WHEN DUP_VAL_ON_INDEX THEN
UPDATE tbl2 set name_tbl2 = DECODE(rec.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D')
WHERE id_tbl2 = rec.cid;
upd := upd + sql%rowcount;
end;
END LOOP;
dbms_output.put_line('Inserted: ' || ins);
dbms_output.put_line('Updated: ' || upd);
END;
/
但应该做什么是使用MERGE来实现这个逻辑:
merge into tbl2
using ( select * from tbl_cust ) q
on (q.cid = tbl2.id_tbl2)
when not matched then
insert (id_tbl2, name_tbl2)
values (q.cid, DECODE(q.cid, '1', 'A',
'2', 'B',
'3', 'C',
'4', 'D'))
when matched then
update
set tbl2.name_tbl2 = DECODE(q.cid, '1', 'W',
'2', 'X',
'3', 'Y',
'4', 'Z')
/
注意:WHEN MATCHED分支使用不同的值来轻松查看第三次运行中发生的情况。