我有以下代码:
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
语法?
而不是
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>
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 |