Oracle - 使用 CURSOR LOOP 进行批量更新
Posted
技术标签:
【中文标题】Oracle - 使用 CURSOR LOOP 进行批量更新【英文标题】:Oracle - BULK UPDATE with CURSOR LOOP 【发布时间】:2018-10-05 13:12:54 【问题描述】:我想在这样的表上对一个巨大的表执行更新(我现在不是最佳做法):
TARGET_TABLE (
TICKET_ID number,
product_id number,
NET number(15,2),
VAT number(15,2));
http://sqlfiddle.com/#!4/d39ed/3
目标:UPDATE TARGET_TABLE set NET=VAT, VAT=NET
我想出了一个 BULK UPDATE,但我在第 43 行得到一个 ORA-00913: "To many values",我无法解释。另外,我不知道如何在该变体中一次更新两行。 有人可以帮忙吗?
DECLARE
-- new data
CURSOR new_data_cur IS
select
a.rowid,
a.TICKET_ID,
a.product_id,
b.NET,
b.VAT
from TARGET_TABLE a
join TARGET_TABLE_COPY b
on ( a.TICKET_ID=b.TICKET_ID AND a.product_id =b.product_id ) ;
TYPE new_data_type IS TABLE OF new_data_cur%rowtype INDEX BY PLS_INTEGER;
new_data_tab new_data_type;
TYPE row_id_type IS TABLE OF ROWID INDEX BY PLS_INTEGER;
row_id_tab row_id_type;
TYPE rt_update_cols IS RECORD (
NET TARGET_TABLE.NET%TYPE
-- VAT TARGET_TABLE.VAT%TYPE
);
TYPE update_cols_type IS
TABLE OF rt_update_cols INDEX BY PLS_INTEGER;
update_cols_tab1 update_cols_type;
--update_cols_tab2 update_cols_type;
dml_errors EXCEPTION;
PRAGMA exception_init ( dml_errors,-24381 );
BEGIN
OPEN new_data_cur;
LOOP
FETCH new_data_cur BULK COLLECT INTO new_data_tab LIMIT 50000;
EXIT WHEN new_data_tab.count=0;
FOR i IN new_data_tab.first..new_data_tab.last LOOP
row_id_tab(i) := new_data_tab(i).rowid;
update_cols_tab1(i).NET := new_data_tab(i).VAT;
-- update_cols_tab2(i).VAT := new_data_tab(i).NET;
END LOOP;
FORALL i IN new_data_tab.first..new_data_tab.last SAVE EXCEPTIONS # ORA-00913: To many values
UPDATE TARGET_TABLE
-- SET row = update_cols_tab(i)
SET row = update_cols_tab1(i)
-- row = update_cols_tab2(i)
WHERE ROWID = row_id_tab(i);
COMMIT;
EXIT WHEN new_data_tab.count=0;
END LOOP;
COMMIT;
CLOSE new_data_cur;
EXCEPTION
WHEN dml_errors THEN
FOR i IN 1..SQL%bulk_exceptions.count LOOP
dbms_output.put_line('Some error occured');
END LOOP;
END;
【问题讨论】:
你为什么要这样做,而你可以这样做UPDATE TARGET_TABLE set NET=VAT, VAT=NET;
?
>500.000.000 条记录
所以?在单个更新语句中更新表仍然比批量更新更快。
或者,您可以尝试重命名列(net -> net_vat,vat -> net,net_vat -> vat)。不过,这假设 select *
或在没有指定在生产代码中插入的列列表的情况下插入 target_table 没有任何作用。但是,如果您绝对需要,您可以使用 dbms_redefinition 对列重新排序。
或者您可以暂时添加一列UP2DATE INTEGER DEFAULT 0
,然后执行UPDATE TARGET_TABLE set NET=VAT, VAT=NET, UP2DATE = 1 WHERE UP2DATE = 0 AND ROWNUM < 10000
以明智地更新数据块...
【参考方案1】:
我相信您在交换值时不需要额外的光标
FOR i IN new_data_tab.first..new_data_tab.last LOOP
row_id_tab(i) := new_data_tab(i).rowid;
update_cols_tab1(i).NET := new_data_tab(i).VAT;
-- update_cols_tab2(i).VAT := new_data_tab(i).NET;
END LOOP;
因此您的代码将在批量更新中使用这些值
DECLARE
-- new data
CURSOR new_data_cur IS
select
a.rowid,
a.TICKET_ID,
a.product_id,
b.NET,
b.VAT
from TARGET_TABLE a
join TARGET_TABLE_COPY b
on ( a.TICKET_ID=b.TICKET_ID AND a.product_id =b.product_id ) ;
TYPE new_data_type IS TABLE OF new_data_cur%rowtype INDEX BY PLS_INTEGER;
new_data_tab new_data_type;
TYPE row_id_type IS TABLE OF ROWID INDEX BY PLS_INTEGER;
row_id_tab row_id_type;
TYPE rt_update_cols IS RECORD (
NET TARGET_TABLE.NET%TYPE
-- VAT TARGET_TABLE.VAT%TYPE
);
TYPE update_cols_type IS
TABLE OF rt_update_cols INDEX BY PLS_INTEGER;
update_cols_tab1 update_cols_type;
--update_cols_tab2 update_cols_type;
dml_errors EXCEPTION;
PRAGMA exception_init ( dml_errors,-24381 );
BEGIN
OPEN new_data_cur;
LOOP
FETCH new_data_cur BULK COLLECT INTO new_data_tab LIMIT 50000;
EXIT WHEN new_data_tab.count=0;
FORALL i IN new_data_tab.first..new_data_tab.last SAVE EXCEPTIONS # ORA-00913: To many values
UPDATE TARGET_TABLE
-- SET row = update_cols_tab(i)
-- SET row = update_cols_tab1(i)
-- row = update_cols_tab2(i)
NET = update_cols_tab1(i).VAT
VAT = update_cols_tab1(i).NET
WHERE ROWID = row_id_tab(i);
COMMIT;
EXIT WHEN new_data_tab.count=0;
END LOOP;
COMMIT;
CLOSE new_data_cur;
EXCEPTION
WHEN dml_errors THEN
FOR i IN 1..SQL%bulk_exceptions.count LOOP
dbms_output.put_line('Some error occured');
END LOOP;
END;
【讨论】:
以上是关于Oracle - 使用 CURSOR LOOP 进行批量更新的主要内容,如果未能解决你的问题,请参考以下文章