各レコードのループ処理にパフォーマンス上の問題があり、1 回の更新ではテーブルが大きすぎる場合は、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 エンジンに渡して実行します。これは「コンテキストの切り替え」と呼ばれ、特にループで行われる場合、かなりの時間がかかります。
バルク バインディングは、[バルク サイズ] レコードごとに 1 回だけコンテキスト スイッチを実行することで、このオーバーヘッドを削減することを目的としています。繰り返しになりますが、これは確かに単一の DML 操作ほど効果的ではありませんが、大きなテーブルや複雑なクエリの場合は、実行可能な最良のソリューションになる可能性があります。
上記の方法を使用して、バッチサイズが 10K-100K の 100M-500M レコードでテーブルを更新しましたが、うまくいきました。ただし、最高のパフォーマンスを得るには、環境でバッチ サイズを試す必要があります。