PL/SQLで大量データ移行する際のメモ
数十万~数億件のデータ移行でバルクFETCHからのINSERTが有効です。
(ダイレクトパスINSERTはまたの機会に)
BULK FETCH から INSERT / UPDATE をなるべく簡単に書く方法をすっかり忘れていたのでメモメモ。
これを忘れてしまい検索に手間取ったUPDATEの書式。「ROW=」のところがミソ。
これを使うとレコード毎UPDATEできて便利。
BULK_UPDATE
UPDATE <テーブル名>
SET ROW = <レコード型コレクション>
WHERE <主キー>;
INSERTもこんな感じで簡単に書ける。
BULK_INSERT
INSERT INTO <テーブル名> VALUE <レコード型コレクション>;
BULK FETCH からの INSERT / UPDATE を行うとこんな感じになる
1万件をガバッ と読んで、 変更データをコレクションにため込み 一気に UPDATE/INSERT して効率を高めます。
(便宜上、中でSELECT文を入れていますが UPDATEも例に入れたかったので^^;)
BULK_FETCH_SAMPLE
CREATE OR REPLACE PROCEDURE BULK_FETCH AS
BULK_SIZE CONSTANT BINARY_INTEGER := 10000;
CURSOR curEMP(p_dept_id VARCHAR2) IS
SELECT EMP_ID, DEPT_ID, EMP_NAME
FROM EMP
WHERE DEPT_ID = p_dept_id
ORDER BY EMP_ID ASC;
TYPE typCurEmp IS TABLE OF curEMP%ROWTYPE ;
TYPE typEmp2 IS TABLE OF emp2%ROWTYPE ;
rdEmp typCurEmp;
insEmp typEmp2 := typEmp2();
updEmp typEmp2 := typEmp2();
num number(10);
BEGIN
OPEN curEMP('1000');
LOOP
FETCH curEMP BULK COLLECT INTO rdEmp LIMIT BULK_SIZE;
EXIT WHEN rdEmp.COUNT = 0;
insEmp.delete;
updEmp.delete;
FOR i in 1..rdEmp.COUNT LOOP
SELECT count(1)
INTO num
FROM emp2
WHERE emp_id = rdEmp(i).emp_id;
IF num > 0 THEN
updEmp.EXTEND;
updEmp(updEmp.COUNT).emp_id := rdEmp(i).emp_id;
updEmp(updEmp.COUNT).dept_id := rdEmp(i).dept_id;
updEmp(updEmp.COUNT).emp_name := rdEmp(i).emp_name;
ELSE
insEmp.EXTEND;
insEmp(insEmp.COUNT).emp_id := rdEmp(i).emp_id;
insEmp(insEmp.COUNT).dept_id := rdEmp(i).dept_id;
insEmp(insEmp.COUNT).emp_name := rdEmp(i).emp_name;
END IF;
END LOOP;
FORALL i in 1..insEmp.COUNT
INSERT INTO emp2 values insEmp(i);
FORALL i in 1..updEmp.COUNT
UPDATE emp2 SET ROW = updEmp(i) Where emp_id = updEmp(i).emp_id;
COMMIT;
END LOOP;
CLOSE curEMP;
END BULK_FETCH;
これで大量データの移行データも楽ちんチン!