使用游标在 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 测试数据,这两个例程都没有经过测试,但是它们在语法上是有效的。