代码之家  ›  专栏  ›  技术社区  ›  4est

比较来自SYS\u REFCURSOR的集合结果

  •  0
  • 4est  · 技术社区  · 7 年前

    我试图与SYS\u REFCURSOR中的集合结果进行比较:

    declare
     v_RC sys_refcursor;
     v_RC_union sys_refcursor;
     v_REC userA.table1%rowtype;
     v_REC_union userB.table2%rowtype;
     i number := 0;
     j number := 0;
     z number := 0;
    begin         
      open v_RC for select * from userA.table1;
       open v_RC_union for select * from userB.table2;
        loop fetch v_RC into v_REC;
         exit when v_RC%notfound;
         i := i+1;
    
          loop fetch v_RC_union into v_REC_union;--
           exit when v_RC_union%notfound;
           j := j+1;
    
                If v_REC_union.id= v_REC.id then                
                 z :=z+1;                
                End if;
    
           end loop;
    
        end loop;
       close v_RC;
      close v_RC_union;
    
    dbms_output.put_line(z);--too small
    dbms_output.put_line('v_RC: '||i||', v_REC_union: '||j);
    end;
    

    我知道两个游标都得到正确的行数(I&j是可以的),但相等行数(z)的结果是错误的(太小)。

    2 回复  |  直到 7 年前
        1
  •  1
  •   Alex Poole    7 年前

    你正在消耗 v_RC_union 在外循环的第一次迭代期间。您似乎期望每次循环时都能看到所有这些行,但是第二个ref光标并没有重置为开始位置(而且不能重置)。

    如果将调试添加到代码中,您可以看到正在发生这种情况;我已经创建了两个小的虚拟表,每个表中有三个匹配的ID和一个不匹配的ID:

    create table table1 (id, dummy) as select level + 1, 'x' from dual connect by level <= 4;
    create table table2 (id, dummy) as select level, 'x' from dual connect by level <= 4;
    
    declare
    ...
    begin
      open v_RC for select * from table1;
      open v_RC_union for select * from table2;
      loop
        fetch v_RC into v_REC;
        exit when v_RC%notfound;
        i := i+1;
        dbms_output.put_line('i: ' || i || ' table1 id ' || v_REC.id);
        loop
          fetch v_RC_union into v_REC_union;--
          exit when v_RC_union%notfound;
          j := j+1;
          dbms_output.put_line('i: ' || i || ' j: ' || j || ' table1 id ' || v_REC_union.id);
          If v_REC_union.id= v_REC.id then
            z :=z+1;
          end if;
        end loop;
      end loop;
      close v_RC;
      close v_RC_union;
      dbms_output.put_line('z: ' || z);--too small
      dbms_output.put_line('v_RC: '||i||', v_REC_union: '||j);
    end;
    /
    

    其输出为:

    i: 1 table1 id 2
    i: 1 j: 1 table1 id 1
    i: 1 j: 2 table1 id 2
    i: 1 j: 3 table1 id 3
    i: 1 j: 4 table1 id 4
    i: 2 table1 id 3
    i: 3 table1 id 4
    i: 4 table1 id 5
    z: 1
    v_RC: 4, v_REC_union: 4
    

    在第一次迭代中,当 i 是1,执行内部循环并从 工会 ,仅当 notfound . 假设其中一个和第一个匹配 v_RC 行的ID z 是递增的,所以在第一个外部循环之后是1或0。

    在第二次迭代中,当 为2,内环在第一次取出后立即退出,因为您已经用完了。 工会 结果集。您在第一次迭代中获取了它的所有行,因此没有任何内容可供获取。所以,不是那一秒 钢筋混凝土 row的ID从未与任何内容进行过比较,并且 z轴 别碰它,因为它不会走那么远。

    对于外部循环的所有其他迭代,依此类推。内循环总是立即退出,不做任何有用的事情。

    你可以再问 table2 对于每次在答案中显示的特定ID,尽管如果只计算它们而不使用任何列值,那么实际上不需要open/loop/fetch,只需将该查询更改为一个count到一个标量变量。不过,即使是逐行处理,这似乎也不是很有效。

    如果要坚持使用整个表查询,可以使用集合:

    declare
      type t_table1 is table of table1%rowtype;
      type t_table2 is table of table2%rowtype;
      v_table1 t_table1;
      v_table2 t_table2;
      i number := 0;
      j number := 0;
      z number := 0;
    begin
      select * bulk collect into v_table1 from table1;
      select * bulk collect into v_table2 from table2;
      for r1 in v_table1.first..v_table1.last loop
        i := i+1;
        dbms_output.put_line('i: ' || i || ' table1 id ' || v_table1(r1).id);
        j := 0;
        for r2 in v_table2.first..v_table2.last loop
          j := j+1;
          dbms_output.put_line('i: ' || i || ' j: ' || j || ' table2 id ' || v_table2(r2).id);
          if v_table2(r2).id = v_table1(r1).id then
            z := z+1;
          end if;
        end loop;
      end loop;
    
      dbms_output.put_line('z: ' || z);
      dbms_output.put_line('v_RC: '||i||', v_REC_union: '||j);
    end;
    /
    

    通过循环遍历两个集合 迭代看到 j 行并可以比较ID。

    i: 1 table1 id 2
    i: 1 j: 1 table2 id 1
    i: 1 j: 2 table2 id 2
    i: 1 j: 3 table2 id 3
    i: 1 j: 4 table2 id 4
    i: 2 table1 id 3
    i: 2 j: 1 table2 id 1
    i: 2 j: 2 table2 id 2
    i: 2 j: 3 table2 id 3
    i: 2 j: 4 table2 id 4
    i: 3 table1 id 4
    i: 3 j: 1 table2 id 1
    i: 3 j: 2 table2 id 2
    i: 3 j: 3 table2 id 3
    i: 3 j: 4 table2 id 4
    i: 4 table1 id 5
    i: 4 j: 1 table2 id 1
    i: 4 j: 2 table2 id 2
    i: 4 j: 3 table2 id 3
    i: 4 j: 4 table2 id 4
    z: 3
    v_RC: 4, v_REC_union: 4
    

    当您可以使用set运算符直接在普通SQL中进行计数和比较时,这似乎仍然需要做很多工作,例如:

    select 
      (select count(*) from table1) as i,
      (select count(*) from table2) as j,
      (select count(*) from (select id from table1 intersect select id from table2)) as z
    from dual;
    
             I          J          Z
    ---------- ---------- ----------
             4          4          3
    

    在这里使用PL/SQL似乎并没有太大的好处;SQL版本可能会做更多的工作,因为它查询两个表两次(虽然可能会碰到缓存),但是无论如何,您可以使用cte来避免这一点,而且总体上可能比使用PL/SQL快。

        2
  •  0
  •   4est    7 年前

    我改变了顺序,现在开始工作,好的

    open v_RC for select * from select * from userA.table1;  
     loop fetch v_RC into v_REC;    
      exit when v_RC%notfound;
    
           open v_RC_union for select * from select * from userB.table2 where id = v_REC.id;     
            loop fetch v_RC_union into v_REC_union;
             exit when v_RC_union%notfound;
              if  v_REC.id = v_REC_union.id then
               z :=z+1;
               dbms_output.put_line(v_REC.id ||'='|| v_REC_union.id);
              end if;
            end loop; 
           close v_RC_union;   
    
     end loop;
    close v_RC;
    

    但我还是不知道怎么了?