填充临时表并使用数据进行更新和插入

问题描述 投票:0回答:2

我有以下代码:

DECLARE
    TYPE table_invoice IS RECORD
                           (
                               invoiceId  number(15),
                               status     number(10),
                           );
    TYPE invoice_data_table IS TABLE OF table_invoice;
    invoice_data invoice_data_table;
BEGIN

select inv."InvoiceId",
       inv."Status",
           bulk collect
    into invoice_data
    from "Invoice" inv join "Company" c on inv."CompanyId" = c."Id"
    where c."CompanyType" = 1

这里我存储选择数据,稍后用于更新和插入。 不幸的是,批量更新不起作用。

Update "Invoice"
set "Status"                 = 200,
    "LastModifiedDate"       = (select sysdate from dual)
where "InvoiceId" in (select invoiceId from invoice_data); -- this select doesn't work, so the entire update will fail, why?

FOR i IN 1 .. invoice_data.count 
LOOP
    insert into "InvoiceStatusChange" ("Date", "NewStatus", "InvoiceId", "CompanyId")
    select inv."InvoiceDate", inv."Status", inv."InvoiceId", inv."CompanyId" from "Invoice"
    inv where "InvoiceId" = invoice_data(i).InvoiceId;
END LOOP;
END;

现在更新不起作用。仅当我移动到 for 循环并逐条更新记录时,它才有效,就像我对插入所做的一样。 有没有办法一次性做到这一点?类似地,我如何尝试更新发票,通过从缓存的invoice_data中传递ID,使用“Invoice”表中的属性从选择语句插入

“Invoice”
表? 像这样的东西:

insert into "InvoiceStatusChange" ("Date", "NewStatus", "InvoiceId", "CompanyId")
select inv."InvoiceDate", inv."Status", inv."InvoiceId", inv."CompanyId" from "Invoice"
inv where "InvoiceId" in (select invoiceId from invoice_data);

所以基本上,通过传递 ID 列表而不是一个接一个地使用

insert into select
语法?

sql oracle plsql
2个回答
1
投票

而不是

update
,尝试
merge
。这是一个基于 Scott 模式的示例;我在这里没有做任何smart,只是为了说明如何做。

在 SQL 级别创建类型:

SQL> create or replace type table_invoice is object (empno number, job varchar2(20));
  2  /

Type created.

SQL> create or replace type invoice_data_table is table of table_invoice;
  2  /

Type created.

这是初始表格内容;我会更新

JOB
列的值:

SQL> select * from emp where deptno = 20;

     EMPNO ENAME      JOB              MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 17.12.80        840                    20
      7566 JONES      MANAGER         7839 02.04.81       2975                    20
      7788 SCOTT      ANALYST         7566 09.12.82       3000                    20
      7876 ADAMS      CLERK           7788 12.01.83       1100                    20
      7902 FORD       ANALYST         7566 03.12.81       3000                    20

这是程序;检查

using
子句(第 10 行和第 11 行):

SQL> declare
  2    invoice_data invoice_data_table;
  3  begin
  4    select table_invoice(e.empno, e.job)
  5      bulk collect into invoice_data
  6      from emp e
  7      where e.deptno = 20;
  8
  9    merge into emp a
 10      using (select empno, job
 11             from table(cast(invoice_data as invoice_data_table))
 12            ) x
 13      on (a.empno = x.empno)
 14      when matched then update set
 15        a.job = 'x' || x.job;
 16  end;
 17  /

PL/SQL procedure successfully completed.

结果:

SQL> select * From emp where deptno = 20;

     EMPNO ENAME      JOB              MGR HIREDATE        SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
      7369 SMITH      xCLERK          7902 17.12.80        840                    20
      7566 JONES      xMANAGER        7839 02.04.81       2975                    20
      7788 SCOTT      xANALYST        7566 09.12.82       3000                    20
      7876 ADAMS      xCLERK          7788 12.01.83       1100                    20
      7902 FORD       xANALYST        7566 03.12.81       3000                    20

SQL>

0
投票

您可以使用

FORALL
statement 从 PL/SQL 批处理 DML。它不需要新的模式级对象。而且它不是
loop
的方便快捷方式:它是纯批处理绑定。

设置:

create table main_table
as
select
  level as id,
  'status' || mod(level, 3) as status,
  trunc(level/5) as ref_id,
  lpad(level, 4, '0') as some_data
from dual
connect by level < 25
/

create table ref_tab
as
select
  level as id,
  mod(level, 2) as type_
from dual
connect by level < 4
/

create table main_table_status (
  dt date default sysdate,
  id int,
  old_ varchar2(20),
  new_ varchar2(20)
)

(批量)收集 ID 和将要更新的之前的值,然后执行

forall
更新和插入:

declare
  type tr_main_table is record (
    id main_table.id%type,
    status main_table.status%type
  );
  type tt_main_table is table of tr_main_table;
  lt_main_table tt_main_table;
  c_status constant varchar2(5) := 'upd';
begin
  /*id to update*/
  select
    id, status

    bulk collect into lt_main_table
  from main_table m
  where m.ref_id in (
    select flt.id
    from ref_tab flt
    where flt.type_ = 1
  );

  dbms_output.put_line(lt_main_table.count());

  /*update*/
  forall i in lt_main_table.first..lt_main_table.last
    update main_table
    set status = c_status
    where id = lt_main_table(i).id;

  /*log status*/
  forall i in lt_main_table.first..lt_main_table.last
    insert into main_table_status(id, old_, new_)
    values(
      lt_main_table(i).id,
      lt_main_table(i).status,
      c_status
    );
end;/
1 rows affected

dbms_output:
10

这导致:

select *
from main_table_status
order by id
DT ID 老_ NEW_
2023-05-20 00:47:15 5 状态2 更新
2023-05-20 00:47:15 6 状态0 更新
2023-05-20 00:47:15 7 状态1 更新
2023-05-20 00:47:15 8 状态2 更新
2023-05-20 00:47:15 9 状态0 更新
2023-05-20 00:47:15 15 状态0 更新
2023-05-20 00:47:15 16 状态1 更新
2023-05-20 00:47:15 17 状态2 更新
2023-05-20 00:47:15 18 状态0 更新
2023-05-20 00:47:15 19 状态1 更新
select *
from main_table
where status = 'upd'
order by 1
ID 状态 REF_ID 一些数据
5 更新 1 0005
6 更新 1 0006
7 更新 1 0007
8 更新 1 0008
9 更新 1 0009
15 更新 3 0015
16 更新 3 0016
17 更新 3 0017
18 更新 3 0018
19 更新 3 0019

但是你可以优化访问。由于您仅使用附加表进行过滤,因此值得通过

where
中的子查询明确地执行此操作:
in
exists
。 Join 是为了添加新信息,过滤是它的一个副作用:当唯一的要求是过滤时,您显然不想关心键列和处理可能由 join 产生的重复行。

因此,将过滤移动到

where
中,您可以直接在具有
update
子句的
returning
语句中使用它,您也可以
bulk collect
这。它将节省对同一个表的一次额外访问:从
update
检索值而不是初步的
select
.

然后做同样的

forall
插入到你的附加表中。

declare
  type tr_main_table is record (
    id main_table.id%type,
    status main_table.status%type
  );
  type tt_main_table is table of tr_main_table;
  lt_main_table tt_main_table;
  c_status constant varchar2(5) := 'upd_2';
begin
  dbms_session.sleep(2);

  /*update*/
  update (
    /*subquery to generate a column
    with before value*/
    select
      m.*,
      /*scalar subquery to unlink the value
      from the original column name*/
      (select m.status from dual) as old_status
    from main_table m
    where m.ref_id in (
      select flt.id
      from ref_tab flt
      where flt.type_ = 1
    )
  )
  set status = c_status
  returning id, old_status

    bulk collect into lt_main_table
  ;

  dbms_output.put_line(lt_main_table.count());

  /*log status*/
  forall i in lt_main_table.first..lt_main_table.last
    insert into main_table_status(id, old_, new_)
    values(
      lt_main_table(i).id,
      lt_main_table(i).status,
      c_status
    );
end;/
1 rows affected

dbms_output:
10

这导致日志表的内容:

select *
from main_table_status
order by dt, id
DT ID 老_ NEW_
2023-05-20 00:47:15 5 状态2 更新
2023-05-20 00:47:15 6 状态0 更新
2023-05-20 00:47:15 7 状态1 更新
2023-05-20 00:47:15 8 状态2 更新
2023-05-20 00:47:15 9 状态0 更新
2023-05-20 00:47:15 15 状态0 更新
2023-05-20 00:47:15 16 状态1 更新
2023-05-20 00:47:15 17 状态2 更新
2023-05-20 00:47:15 18 状态0 更新
2023-05-20 00:47:15 19 状态1 更新
2023-05-20 00:47:17 5 更新 upd_2
2023-05-20 00:47:17 6 更新 upd_2
2023-05-20 00:47:17 7 更新 upd_2
2023-05-20 00:47:17 8 更新 upd_2
2023-05-20 00:47:17 9 更新 upd_2
2023-05-20 00:47:17 15 更新 upd_2
2023-05-20 00:47:17 16 更新 upd_2
2023-05-20 00:47:17 17 更新 upd_2
2023-05-20 00:47:17 18 更新 upd_2
2023-05-20 00:47:17 19 更新 upd_2

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.