使用游标在 Oracle SQL 中执行 2 个 for 循环
Execute 2 for loops in Oracle SQL with cursors
我正在使用一个过程从一个名为 transactions 的 table 中获取数据。 table 有 1000 行,我必须一次读取游标中的 50 条记录。对于每条记录,我需要打印输出。然后该函数应该从 table 中获取接下来的 50 条记录并执行相同的操作。
我明白了逻辑,但输出不正确。并非两个 for 循环都有效。你能找到下面的代码并帮助我吗?
create or replace PROCEDURE TWO_CURSOR IS
startC number := 1;
endc number := 3 ;
cursor v_count is
select (count(*) /50) from transactions;
cursor transc is
SELECT transactions_id, product_id
FROM transactions
where rownum >= startC
and rownum <= endc;
v_productName masterdata.product_name %TYPE;
v_supplierId masterdata.supplier_id %type;
v_supplierName masterdata.supplier_name %type;
v_price masterdata.price %type;
begin
for i in v_count
LOOP
for j in transc
loop
select product_name into v_productName from masterdata m where product_id=j.product_id;
dbms_output.put_line( j.product_id || ' - ' || j.transactions_id || ' - ' || v_productName);
end loop;
startC := startC +3;
endc := endc+3 ;
end loop;
END TWO_CURSOR;
> Blockquote
This is the output that I'm getting
[output][1]
[1]: https://i.stack.imgur.com/a2wGX.png
你什么都说了;我只是对其进行编码(以我理解问题的方式)。注意第 18 - 21 行。
SQL> create or replace procedure two_cursor is
2 startc integer := 1;
3 endc integer := startc + 49;
4 endcount integer;
5
6 cursor transc is
7 select transactions_id, product_id
8 from transactions
9 where transactions_id between startc and endc
10 order by transactions_id;
11
12 v_productname masterdata.product_name %type;
13 v_supplierid masterdata.supplier_id %type;
14 v_suppliername masterdata.supplier_name %type;
15 v_price masterdata.price %type;
16
17 begin
18 -- calculate endCount value
19 select round(count(*) / 50)
20 into endcount
21 from masterdata;
22
23 for i in 1..endcount
24 loop
25 dbms_output.put_line( i );
26 for j in transc
27 loop
28 select product_name into v_productname
29 from masterdata
30 where product_id = j.product_id;
31 dbms_output.put_line( j.product_id || ' - ' || j.transactions_id
32 || ' - ' || v_productname);
33 end loop;
34 startc := startc + 50;
35 endc := endc + 50;
36 end loop;
37 end two_cursor;
38 /
None 的 posted 解决方案实际上产生了您描述的内容。由于谓词
,您的初始尝试失败
where rownum >= startC
这在第一次迭代中有效,因为变量 "startC" 的值为 1,但在所有后续迭代中失败,因为 "startC" 已递增。伪列 rownum 不是累加的,每次执行 Select 都会将其初始化为 1;当一行被 selected 时,它会在以后递增。但是要 selected 谓词 "rownum >= startC" 必须为真。所以第一行没有 selected,并且 rownum 没有递增(它仍然是 1)所以它对所有后续行都失败了。结果,没有 select 行。
其他 2 个解决方案 "fail" 都取决于 transactions_id 中没有间隙的假设。虽然只要该假设成立,两者都可以工作,但这不是一个现实的现实假设,除非您经过大量编码以保持无缝隙。
就动态提供 endCount 而言,基本上有两种方法。创建存储过程并传递参数,或创建匿名并提供绑定变量或替换变量。此外,您可以使用单个游标和批量收集,这样就不需要为每个交易+产品组合单独 select。
create or replace procedure product_list_by_n_elements(
group_size in integer default 50
, max_groups in integer default 20
) is
cursor c_prod_trans is
select m.product_id
, m.product_name
, t.transactions_id
from masterdata m
join transactions t
on t.product_id = m.product_id
order by t.transactions_id, m.product_id;
type c_prod_trans_list_t is
table of c_prod_trans%rowtype;
prod_trans_list c_prod_trans_list_t;
processing_group integer := 0;
first_entry integer := 0;
last_entry integer := 0;
begin
open c_prod_trans;
loop
processing_group := processing_group + 1;
fetch c_prod_trans
bulk collect
into prod_trans_list
limit group_size;
first_entry := last_entry + 1;
last_entry := last_entry + prod_trans_list.count;
dbms_output.put_line( 'Group: ' || to_number(processing_group)
|| ' Entries:' || to_number(first_entry)
|| ' - ' || to_number(last_entry )
);
for prod_trans_entry in 1 .. prod_trans_list.count
loop
dbms_output.put_line( ' ' || to_number(prod_trans_list(prod_trans_entry).product_id)
|| ' - ' || to_number(prod_trans_list(prod_trans_entry).transactions_id)
|| ' - ' || prod_trans_list(prod_trans_entry).product_name
);
end loop;
exit when prod_trans_list.count < group_size
or processing_group >= max_groups;
end loop;
end product_list_by_n_elements;
或者作为带有替换参数的匿名块。
declare
group_size integer := &Group_Size;
max_groups integer := &endCount;
cursor c_prod_trans is
select m.product_id
, m.product_name
, t.transactions_id
from masterdata m
join transactions t
on t.product_id = m.product_id
order by t.transactions_id, m.product_id;
type c_prod_trans_list_t is
table of c_prod_trans%rowtype;
prod_trans_list c_prod_trans_list_t;
processing_group integer := 0;
first_entry integer := 0;
last_entry integer := 0;
begin
open c_prod_trans;
loop
processing_group := processing_group + 1;
fetch c_prod_trans
bulk collect
into prod_trans_list
limit group_size;
first_entry := last_entry + 1;
last_entry := last_entry + prod_trans_list.count;
dbms_output.put_line( 'Group: ' || to_number(processing_group)
|| ' Entries:' || to_number(first_entry)
|| ' - ' || to_number(last_entry )
);
for prod_trans_entry in 1 .. prod_trans_list.count
loop
dbms_output.put_line( ' ' || to_number(prod_trans_list(prod_trans_entry).product_id)
|| ' - ' || to_number(prod_trans_list(prod_trans_entry).transactions_id)
|| ' - ' || prod_trans_list(prod_trans_entry).product_name
);
end loop;
exit when prod_trans_list.count < group_size
or processing_group >= max_groups;
end loop;
end;
免责声明。由于您没有 post 测试数据,这两个例程都没有经过测试,但是它们在语法上是有效的。
我正在使用一个过程从一个名为 transactions 的 table 中获取数据。 table 有 1000 行,我必须一次读取游标中的 50 条记录。对于每条记录,我需要打印输出。然后该函数应该从 table 中获取接下来的 50 条记录并执行相同的操作。
我明白了逻辑,但输出不正确。并非两个 for 循环都有效。你能找到下面的代码并帮助我吗?
create or replace PROCEDURE TWO_CURSOR IS
startC number := 1;
endc number := 3 ;
cursor v_count is
select (count(*) /50) from transactions;
cursor transc is
SELECT transactions_id, product_id
FROM transactions
where rownum >= startC
and rownum <= endc;
v_productName masterdata.product_name %TYPE;
v_supplierId masterdata.supplier_id %type;
v_supplierName masterdata.supplier_name %type;
v_price masterdata.price %type;
begin
for i in v_count
LOOP
for j in transc
loop
select product_name into v_productName from masterdata m where product_id=j.product_id;
dbms_output.put_line( j.product_id || ' - ' || j.transactions_id || ' - ' || v_productName);
end loop;
startC := startC +3;
endc := endc+3 ;
end loop;
END TWO_CURSOR;
> Blockquote
This is the output that I'm getting
[output][1]
[1]: https://i.stack.imgur.com/a2wGX.png
你什么都说了;我只是对其进行编码(以我理解问题的方式)。注意第 18 - 21 行。
SQL> create or replace procedure two_cursor is
2 startc integer := 1;
3 endc integer := startc + 49;
4 endcount integer;
5
6 cursor transc is
7 select transactions_id, product_id
8 from transactions
9 where transactions_id between startc and endc
10 order by transactions_id;
11
12 v_productname masterdata.product_name %type;
13 v_supplierid masterdata.supplier_id %type;
14 v_suppliername masterdata.supplier_name %type;
15 v_price masterdata.price %type;
16
17 begin
18 -- calculate endCount value
19 select round(count(*) / 50)
20 into endcount
21 from masterdata;
22
23 for i in 1..endcount
24 loop
25 dbms_output.put_line( i );
26 for j in transc
27 loop
28 select product_name into v_productname
29 from masterdata
30 where product_id = j.product_id;
31 dbms_output.put_line( j.product_id || ' - ' || j.transactions_id
32 || ' - ' || v_productname);
33 end loop;
34 startc := startc + 50;
35 endc := endc + 50;
36 end loop;
37 end two_cursor;
38 /
None 的 posted 解决方案实际上产生了您描述的内容。由于谓词
,您的初始尝试失败where rownum >= startC
这在第一次迭代中有效,因为变量 "startC" 的值为 1,但在所有后续迭代中失败,因为 "startC" 已递增。伪列 rownum 不是累加的,每次执行 Select 都会将其初始化为 1;当一行被 selected 时,它会在以后递增。但是要 selected 谓词 "rownum >= startC" 必须为真。所以第一行没有 selected,并且 rownum 没有递增(它仍然是 1)所以它对所有后续行都失败了。结果,没有 select 行。
其他 2 个解决方案 "fail" 都取决于 transactions_id 中没有间隙的假设。虽然只要该假设成立,两者都可以工作,但这不是一个现实的现实假设,除非您经过大量编码以保持无缝隙。
就动态提供 endCount 而言,基本上有两种方法。创建存储过程并传递参数,或创建匿名并提供绑定变量或替换变量。此外,您可以使用单个游标和批量收集,这样就不需要为每个交易+产品组合单独 select。
create or replace procedure product_list_by_n_elements(
group_size in integer default 50
, max_groups in integer default 20
) is
cursor c_prod_trans is
select m.product_id
, m.product_name
, t.transactions_id
from masterdata m
join transactions t
on t.product_id = m.product_id
order by t.transactions_id, m.product_id;
type c_prod_trans_list_t is
table of c_prod_trans%rowtype;
prod_trans_list c_prod_trans_list_t;
processing_group integer := 0;
first_entry integer := 0;
last_entry integer := 0;
begin
open c_prod_trans;
loop
processing_group := processing_group + 1;
fetch c_prod_trans
bulk collect
into prod_trans_list
limit group_size;
first_entry := last_entry + 1;
last_entry := last_entry + prod_trans_list.count;
dbms_output.put_line( 'Group: ' || to_number(processing_group)
|| ' Entries:' || to_number(first_entry)
|| ' - ' || to_number(last_entry )
);
for prod_trans_entry in 1 .. prod_trans_list.count
loop
dbms_output.put_line( ' ' || to_number(prod_trans_list(prod_trans_entry).product_id)
|| ' - ' || to_number(prod_trans_list(prod_trans_entry).transactions_id)
|| ' - ' || prod_trans_list(prod_trans_entry).product_name
);
end loop;
exit when prod_trans_list.count < group_size
or processing_group >= max_groups;
end loop;
end product_list_by_n_elements;
或者作为带有替换参数的匿名块。
declare
group_size integer := &Group_Size;
max_groups integer := &endCount;
cursor c_prod_trans is
select m.product_id
, m.product_name
, t.transactions_id
from masterdata m
join transactions t
on t.product_id = m.product_id
order by t.transactions_id, m.product_id;
type c_prod_trans_list_t is
table of c_prod_trans%rowtype;
prod_trans_list c_prod_trans_list_t;
processing_group integer := 0;
first_entry integer := 0;
last_entry integer := 0;
begin
open c_prod_trans;
loop
processing_group := processing_group + 1;
fetch c_prod_trans
bulk collect
into prod_trans_list
limit group_size;
first_entry := last_entry + 1;
last_entry := last_entry + prod_trans_list.count;
dbms_output.put_line( 'Group: ' || to_number(processing_group)
|| ' Entries:' || to_number(first_entry)
|| ' - ' || to_number(last_entry )
);
for prod_trans_entry in 1 .. prod_trans_list.count
loop
dbms_output.put_line( ' ' || to_number(prod_trans_list(prod_trans_entry).product_id)
|| ' - ' || to_number(prod_trans_list(prod_trans_entry).transactions_id)
|| ' - ' || prod_trans_list(prod_trans_entry).product_name
);
end loop;
exit when prod_trans_list.count < group_size
or processing_group >= max_groups;
end loop;
end;
免责声明。由于您没有 post 测试数据,这两个例程都没有经过测试,但是它们在语法上是有效的。