我必须通过连接一组大表来选择一组值,然后从所选值更新另一个大表。我现在遵循以下方法。但我看到了性能上的缺陷。有哪些替代方法可以进行上述工作?
数据库服务器:Oracle EE
DECLARE
CURSOR c1
IS
SELECT update_data FOR UPDATE OF abc;
BEGIN
FOR update_data IN c1
LOOP
UPDATE klm
SET klm.xyz = update_data.cdf
WHERE update_data.abc = klm.abc;
END LOOP;
COMMIT;
END;
答案 0 :(得分:1)
最简单的更新很可能会表现得更好。
您可以尝试:
update klm t1
set xyz = ( select cdf from update_data t2 where t2.abc = t1.abc )
where exists ( select 1 from update_data t2 where t2.abc = t2.abc );
commit;
或者如果可能(在update_data.abc上有PK或唯一索引)
update ( select t1.xyz, t2.cdf from klm t1, update_data t2 where t1.abc = t2.abc )
) set xyz = cdf;
commit;
答案 1 :(得分:0)
如果在每个记录上循环时存在性能问题,但是对于单个更新而言表太大,您可以考虑使用BULK INTO ... LIMIT和FORALL批量更新。
CREATE TABLE klm (abc INTEGER, xyz INTEGER);
CREATE TABLE update_data (abc INTEGER, cdf INTEGER);
-- Have pairs of numbers (1000 rows)
INSERT INTO klm SELECT rownum, rownum FROM dual CONNECT BY level <= 1000;
-- Update every second row with 9999
INSERT INTO update_data SELECT rownum * 2, 9999 FROM dual CONNECT BY level <= 500;
DECLARE
CURSOR c1
IS
-- Select the key to be updated and the new value
SELECT abc, cdf FROM update_data;
-- Table type and table variable to store rows fetched from the cursor
TYPE t_update IS TABLE OF c1%rowtype;
update_tab t_update;
BEGIN
OPEN c1;
LOOP
-- Fetch next 30 rows into update table
FETCH c1 BULK COLLECT INTO update_tab LIMIT 30;
-- Exit when there were no more rows fetched
EXIT WHEN update_tab.count = 0;
-- This is the key point; uses update_tab to bulk-bind UPDATE statement
-- and run it for 30 rows in a single context switch
FORALL i IN 1..update_tab.count
UPDATE klm
SET klm.xyz = update_tab(i).cdf
WHERE update_tab(i).abc = klm.abc;
COMMIT;
END LOOP;
CLOSE c1;
END;
/
这背后的基本原理是Oracle实际上有单独的引擎运行SQL语句和PL / SQL程序。每当过程遇到SQL语句时,它都会将其交给SQL引擎执行。这称为“上下文切换”并占用大量时间,尤其是在循环中完成时。
批量绑定旨在通过每个[批量大小]记录仅执行一次上下文切换来减少此开销。同样,这肯定不如单个DML操作有效,但对于大型表或复杂查询,它可能是最好的可行解决方案。
我使用上面的方法来更新100M-500M记录,批量大小为10K-100K的表,并且工作正常。但您需要在环境中尝试批量大小以获得最佳性能。