比较来自 SYS_REFCURSOR 的集合结果
Compare Set result from SYS_REFCURSOR
我尝试与 SYS_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) 的结果是错误的(太小)。
我改变了顺序,现在工作正常
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;
但我还是不知道哪里出了问题?
您在外循环的第一次迭代期间消耗了 v_RC_union
中的所有行。您似乎期望每次循环时都能看到所有这些行,但第二个引用游标未重置为开头(也不可能)。
如果您在代码中添加调试,您会看到这种情况发生;我创建了两个小虚拟 table,每个都有三个匹配的 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 时,您执行内部循环并从 v_RC_union
获取所有行,仅在 notfound
时停止。假设其中一个确实匹配第一个 v_RC
行的 ID,然后 z
递增,因此在第一个外循环之后它是 1 或 0。
在第二次迭代中,当 i
为 2 时,内部循环会在第一次获取后立即退出,因为您已经用完了 v_RC_union
结果集。您在第一次迭代中获取了它的所有行,因此没有任何内容可获取。所以,没有第二个 v_RC
行的 ID 永远不会与任何东西进行比较,并且 z
不会被触及,因为它没有那么远。
外循环的所有其他迭代依此类推。内部循环总是立即退出并且不做任何有用的事情。
您可以每次在回答中显示时重新查询 table2
以获取特定 ID,但如果您只是计算它们而不使用任何列值,则您并不真正需要 open/loop/fetch 并且可以将该查询更改为对标量变量的计数。这似乎不是很有效,即使对于逐行处理也是如此。
如果您想坚持整个 table 查询,您可以使用集合:
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;
/
通过遍历两个集合,每次 i
迭代查看每个 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
当您可以使用集合运算符直接在普通 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 版本可能会做更多的工作,因为它会查询两个 table 两次(尽管很可能会命中缓存),但是无论如何您都可以使用 CTE 避免这种情况,并且总体上它可能比使用 PL/SQL.
我尝试与 SYS_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) 的结果是错误的(太小)。
我改变了顺序,现在工作正常
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;
但我还是不知道哪里出了问题?
您在外循环的第一次迭代期间消耗了 v_RC_union
中的所有行。您似乎期望每次循环时都能看到所有这些行,但第二个引用游标未重置为开头(也不可能)。
如果您在代码中添加调试,您会看到这种情况发生;我创建了两个小虚拟 table,每个都有三个匹配的 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 时,您执行内部循环并从 v_RC_union
获取所有行,仅在 notfound
时停止。假设其中一个确实匹配第一个 v_RC
行的 ID,然后 z
递增,因此在第一个外循环之后它是 1 或 0。
在第二次迭代中,当 i
为 2 时,内部循环会在第一次获取后立即退出,因为您已经用完了 v_RC_union
结果集。您在第一次迭代中获取了它的所有行,因此没有任何内容可获取。所以,没有第二个 v_RC
行的 ID 永远不会与任何东西进行比较,并且 z
不会被触及,因为它没有那么远。
外循环的所有其他迭代依此类推。内部循环总是立即退出并且不做任何有用的事情。
您可以每次在回答中显示时重新查询 table2
以获取特定 ID,但如果您只是计算它们而不使用任何列值,则您并不真正需要 open/loop/fetch 并且可以将该查询更改为对标量变量的计数。这似乎不是很有效,即使对于逐行处理也是如此。
如果您想坚持整个 table 查询,您可以使用集合:
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;
/
通过遍历两个集合,每次 i
迭代查看每个 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
当您可以使用集合运算符直接在普通 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 版本可能会做更多的工作,因为它会查询两个 table 两次(尽管很可能会命中缓存),但是无论如何您都可以使用 CTE 避免这种情况,并且总体上它可能比使用 PL/SQL.