使用DUP_VAL_ON_INDEX的PL / SQL更新值

时间:2018-02-08 05:04:50

标签: plsql oracle11g plsqldeveloper

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

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分支使用不同的值来轻松查看第三次运行中发生的情况。