与参数化游标嵌套的for循环正在插入重复记录

时间:2019-06-02 18:43:55

标签: sql oracle plsql sqlplus

我们使用2个参数化的游标,其中第二个游标需要第一个游标的值来相应地获取数据,但是它正在从两个游标中获取值。而是应该仅从第二个游标获取。

这里有2个光标。从第一个游标,它需要获取属性1的值,并将其作为参数化游标传递给cursor2。但是在插入时,它会同时插入第一和第二光标值。

实际和预期结果均在代码段中给出。

/* Table Creation script:*/       
    create table tab1 (order_no number,order_item varchar2(40),header_id number)        
    /        
    create table tab2 (header_id number,line_id number,attribute1 number)        
    /        
    create table final_tab(order_no number, order_item varchar2(40), line_id number)        
    /

    /* Insertion script:*/         
    insert into tab1 values (1,'ABC',12345)
    /
    insert into tab1 values (11,'DEF',34567)
    /
    insert into tab2 values (12345,56789,11)
    /
    insert into tab2 values (12345,23489,11)
    /
    insert into tab2 values (34567,32156,null)
    /
    insert into tab2 values (34567,12534,null)
    /
    commit
    /

    /* Anonymous Block: */
    DECLARE
        CURSOR c1
        IS
            SELECT a.order_no,
                   a.order_item,
                   b.attribute1 end_ord_no,
                   a.header_id,
                   b.line_id
              FROM tab1 a, tab2 b
             WHERE a.header_id = b.header_id AND a.order_no = 1;
        CURSOR c2 (i_ord_no NUMBER)
        IS
            SELECT a.order_no,
                   a.order_item,
                   a.header_id,
                   b.line_id
             FROM tab1 a, tab2 b
             WHERE a.header_id = b.header_id AND a.order_no = i_ord_no;
    BEGIN
        FOR c1_rec IN c1
        LOOP
            FOR c2_rec IN c2 (c1_rec.end_ord_no)
            LOOP
                INSERT INTO final_tab (order_no, order_item, line_id)
                     VALUES (c2_rec.order_no, c2_rec.order_item, c2_rec.line_id);
            END LOOP;
        END LOOP;
        COMMIT;
    END;

/* Actual Result:*/

    Order_NO | Order_Item | Line_id
    11              |  DEF           | 32156
    11              |  DEF           | 12534
    11              |  DEF           | 32156
    11              |  DEF           | 12534

/*Expected Result:*/ 

    Order_NO | Order_Item | Line_id
    11              |  DEF           | 32156
    11              |  DEF           | 12534

1 个答案:

答案 0 :(得分:3)

问题是您的第一个光标返回两行,两行的值const useStyles = makeStyles({ imageIcon: { height: '100%' }, iconRoot: { textAlign: 'center' } }); 均为11。对于第一个游标返回的两行中的每一行都将执行第二个游标,并且第二次游标的每次执行将返回两行,并将其中的值适当地插入到END_ORD_NO中。

要解决此问题,您应该将两个光标合并为一个光标:

FINAL_TAB

将代码块减少到

SELECT a.order_no,
       a.order_item,
       a.header_id,
       b.line_id
  FROM tab1 a, tab2 b
  WHERE a.header_id = b.header_id AND
        a.order_no IN (SELECT b.attribute1
                         FROM tab1 a, tab2 b
                         WHERE a.header_id = b.header_id AND
                               a.order_no = 1)

这不仅会提供您想要的结果,而且还将通过简化代码来减少出错的机会,并通过消除嵌套循环来减少运行时间。

但是,甚至可以进一步简化为

DECLARE
  CURSOR cc IS
    SELECT a.order_no,
           a.order_item,
           a.header_id,
           b.line_id
      FROM tab1 a, tab2 b
      WHERE a.header_id = b.header_id AND
            a.order_no IN (SELECT b.attribute1
                             FROM tab1 a, tab2 b
                             WHERE a.header_id = b.header_id AND
                                   a.order_no = 1);
BEGIN
  FOR rec IN cc LOOP
    INSERT INTO final_tab (order_no, order_item, line_id)
      VALUES (rec.order_no, rec.order_item, rec.line_id);
  END LOOP;

  COMMIT;
END;

这样做的优点是它无需使用任何循环就可以完成整个任务,并且速度更快,因为整个操作都是由数据库执行的,而不必减慢与外部过程的接口。

dbfiddle here

好运。


编辑

如果必须同时使用两个游标,则可以使用MERGE语句,如下所示:

INSERT INTO final_tab (order_no, order_item, line_id)
  SELECT a.order_no,
         a.order_item,
         b.line_id
    FROM tab1 a, tab2 b
    WHERE a.header_id = b.header_id AND
          a.order_no IN (SELECT b.attribute1
                           FROM tab1 a, tab2 b
                           WHERE a.header_id = b.header_id AND
                                 a.order_no = 1)