如何使用带条件的批量收集 for 循环

How to use bulk collect for loop with conditions

提问人:Confused 提问时间:10/22/2023 最后编辑:Thorsten KettnerConfused 更新时间:10/22/2023 访问量:38

问:

我有一个要求,其中源表中的每一行的标题都是重复的。我必须创建plsql过程,将它们插入到具有通用标识符的单独的标题和行表中。我目前正在使用带有 if else 条件的简单 for 循环构造来维护标题和行表中的唯一标识 (invoiceid)

例如:源表

因夫纳姆 线型 行号
123 项目 100 1
123 20 2
446 项目 100 1
446 项目 100 2
446 20 3

标题表

InvId InvNum (英语) Amt(阿姆特酒店)
100 123 120
101 446 220

线表

InvId 线型 行号
100 项目 100 1
100 20 2
101 项目 100 1
101 项目 100 2
101 20 3

我尝试了带有 if else 条件的简单 forloop 构造。但是有没有更好的方法可以做到这一点

SQL Oracle PLSQL

评论

0赞 MT0 10/22/2023
编辑问题并解释:您的示例数据是什么;预期输出是多少;并用英语(而不是代码)描述用于从示例数据到预期输出的逻辑。目前,我不清楚问题出在哪里,以及您是从 1、2 还是 3 个数据表开始,以及这些表中的哪一个是您的预期输出。此外,您共享的所有表都没有您在初始段落中提到的列。invoiceid
0赞 Thorsten Kettner 10/22/2023
尽管该请求正确地标记了 Oracle 和 PL/SQL,但我添加了 SQL 标记,因为这可以单独使用 SQL 语句来解决。如果需要 PL/SQL,则始终可以将 SQL 包装在 PL/SQL 中。

答:

0赞 Littlefoot 10/22/2023 #1

按照我的理解,你需要一个连接,而不是一个循环。

示例数据:

SQL> with
  2  source (invnum, linetype, amount, linenumber) as
  3    (select 123, 'ITEM', 100, 1 from dual union all
  4     select 123, 'TAX' ,  20, 2 from dual union all
  5     select 446, 'ITEM', 100, 1 from dual union all
  6     select 446, 'ITEM', 100, 2 from dual union all
  7     select 446, 'TAX' ,  20, 3 from dual
  8    ),
  9  header (invid, invnum, amt) as
 10    (select 100, 123, 120 from dual union all
 11     select 101, 446, 220 from dual
 12    )

生成该结果的查询:

 13  select h.invid, s.linetype, h.amt, s.linenumber
 14  from header h join source s on s.invnum = h.invnum
 15  order by h.invid, s.linenumber;

     INVID LINE        AMT LINENUMBER
---------- ---- ---------- ----------
       100 ITEM        120          1
       100 TAX         120          2
       101 ITEM        220          1
       101 ITEM        220          2
       101 TAX         220          3

SQL>

如果它必须是 PL/SQL 过程,只需将该段代码移入其中;例如:

create or replace procedure p_line is
begin
  insert into line_table (invid, line, amt, linenumber)
  select h.invid, s.linetype, h.amt, s.linenumber
  from header h join source s on s.invnum = h.invnum;
end;
/

现在,您可以修改它并添加参数(“仅插入所需的行”)等。invid

0赞 Thorsten Kettner 10/22/2023 #2

可以使用两个插入件。由于两者都查询相同的数据集,因此它们会获得相同的 invnum,您可以从中安全地创建 invid(甚至当您聚合每个 invnum 时)。DENSE_RANKROW_NUMBER

insert into header_table (invid, invnum, amt)
select
  row_number() over (order by invnum) + 99,
  invnum,
  sum(amount)
from source_table
group by invnum;

insert into line_table (invid, linetype, amount, linenumber)
select
  dense_rank() over (order by invnum) + 99,
  linetype,
  amount,
  linenumber
from source_table;

但是,冗余存储数据并不是一个好主意。标题表中不应有总金额,因为可以从行表条目中选择此金额。

演示:https://dbfiddle.uk/_b78_f_S