こんにちは! 健史です。
PLSQLで、バルクでのコントロールブレイク処理を作成しました。
非構造型です。
プログラム
create or replace PROCEDURE PROC_BULK2 AS
--1.バルク サイズ定義
BULK_SIZE CONSTANT PLS_INTEGER := 2000; -- コンスタントでなくても良い
--2.カーソル定義
CURSOR CUR1 IS
SELECT * FROM 売上履歴
ORDER BY キー1,キー2,連番 ;
--3.バルク テーブル等定義
-- バルク FETCH用テーブル定義
TYPE IN1TBL IS TABLE OF 売上履歴%ROWTYPE INDEX BY BINARY_INTEGER;
IN1REC IN1TBL;
-- バルク INSERT用テーブル定義
TYPE OT1TBL IS TABLE OF 売上履歴集計1%ROWTYPE INDEX BY BINARY_INTEGER;
OT1REC OT1TBL;
-- バルク INSERT用テーブル定義
TYPE OT2TBL IS TABLE OF 売上履歴集計2%ROWTYPE INDEX BY BINARY_INTEGER;
OT2REC OT2TBL;
-- バルク エラーハンドラ用 ORA-24381: DML配列にエラーがあります。
vBulkErrors PLS_INTEGER := 0;
eBulkProcessNotComplete EXCEPTION;
PRAGMA EXCEPTION_INIT(eBulkProcessNotComplete, -24381);
--4.ワークを定義
入力件数 NUMBER(10);
出力件数1 NUMBER(10);
出力件数2 NUMBER(10);
WK_TIMESTAMP TIMESTAMP;
IXO1 NUMBER(6);
IXO2 NUMBER(6);
SV_KEY1 VARCHAR2(100);
SV_KEY2 VARCHAR2(100);
SV1REC 売上履歴集計1%ROWTYPE;
SV2REC 売上履歴集計2%ROWTYPE;
DUMMY CHAR(1);
--5.FUNCTION定義 必要に応じて定義
--5.INSERT1
FUNCTION FUNC_INSERT1
RETURN VARCHAR2 IS
BEGIN
FORALL i in 1..OT1REC.COUNT SAVE EXCEPTIONS -- ← エラーが発生しても継続
INSERT INTO 売上履歴集計1 VALUES OT1REC(i);
COMMIT;
IXO1 := 0;
OT1REC.DELETE;
RETURN(NULL);
-- バルク PROC_BULK_ERROR_HANDLER;
EXCEPTION
WHEN eBulkProcessNotComplete THEN
vBulkErrors := vBulkErrors + SQL%BULK_EXCEPTIONS.COUNT;
RETURN(NULL);
END;
--5.INSERT2
FUNCTION FUNC_INSERT2
RETURN VARCHAR2 IS
BEGIN
FORALL i in 1..OT2REC.COUNT SAVE EXCEPTIONS -- ← エラーが発生しても継続
INSERT INTO 売上履歴集計2 VALUES OT2REC(i);
COMMIT;
IXO2 := 0;
OT2REC.DELETE;
RETURN(NULL);
-- バルク PROC_BULK_ERROR_HANDLER;
EXCEPTION
WHEN eBulkProcessNotComplete THEN
vBulkErrors := vBulkErrors + SQL%BULK_EXCEPTIONS.COUNT;
RETURN(NULL);
END;
--5.FUNC_INIT_OT_SET1
FUNCTION FUNC_INIT_OT_SET1(i IN NUMBER)
RETURN VARCHAR2 IS
BEGIN
SV1REC.キー1 := IN1REC(i).キー1;
SV1REC.キー2 := IN1REC(i).キー2;
SV1REC.キー3 := IN1REC(i).キー3;
SV1REC.連番 := IN1REC(i).連番;
SV1REC.数量 := IN1REC(i).数量;
SV1REC.単価 := IN1REC(i).単価;
SV1REC.団体コード := IN1REC(i).団体コード;
SV1REC.件数 := 0;
RETURN(NULL);
END;
--5.FUNC_INIT_OT_SET2
FUNCTION FUNC_INIT_OT_SET2(i IN NUMBER)
RETURN VARCHAR2 IS
BEGIN
SV2REC.キー1 := IN1REC(i).キー1;
SV2REC.キー2 := IN1REC(i).キー2;
SV2REC.キー3 := IN1REC(i).キー3;
SV2REC.連番 := IN1REC(i).連番;
SV2REC.数量 := IN1REC(i).数量;
SV2REC.単価 := IN1REC(i).単価;
SV2REC.団体コード := IN1REC(i).団体コード;
SV2REC.件数 := 0;
RETURN(NULL);
END;
--5.OUT-RECORD-SET1
FUNCTION FUNC_OT_SET1
RETURN VARCHAR2 IS
BEGIN
IXO1 := IXO1 + 1 ;
OT1REC(IXO1) := SV1REC ;
出力件数1 := 出力件数1 + 1;
RETURN(NULL);
END;
--5.OUT1-RECORD-SET2
FUNCTION FUNC_OT_SET2
RETURN VARCHAR2 IS
BEGIN
IXO2 := IXO2 + 1 ;
OT2REC(IXO2) := SV2REC ;
出力件数2 := 出力件数2 + 1;
RETURN(NULL);
END;
--6.メイン処理
BEGIN
--6-1.初期処理
OPEN CUR1;
EXECUTE IMMEDIATE 'TRUNCATE TABLE 売上履歴集計1';
EXECUTE IMMEDIATE 'TRUNCATE TABLE 売上履歴集計2';
入力件数 := 0;
出力件数1 := 0;
出力件数2 := 0;
SELECT TO_CHAR(SYSTIMESTAMP,'yyyy/mm/dd hh24:mi:ss.ff5') INTO WK_TIMESTAMP FROM DUAL;
DBMS_OUTPUT.PUT_LINE('開始時刻:' || WK_TIMESTAMP);
-- バルク FETCH,INSERT用テーブル初期クリア
OT1REC.DELETE;
OT2REC.DELETE;
SV_KEY1 := ' ';
IXO1 := 0;
IXO2 := 0;
LOOP
-- バルク FETCH
IN1REC.DELETE;
FETCH CUR1 BULK COLLECT INTO IN1REC LIMIT BULK_SIZE;
EXIT WHEN IN1REC.COUNT = 0;
--
FOR i in 1 .. IN1REC.COUNT LOOP
IF IN1REC(i).キー1 != SV_KEY1 OR
IN1REC(i).キー2 != SV_KEY2 THEN
IF SV_KEY1 != ' ' THEN
DUMMY := FUNC_OT_SET2();
END IF;
SV_KEY2 := IN1REC(i).キー2 ;
DUMMY := FUNC_INIT_OT_SET2(i);
END IF;
IF (IN1REC(i).キー1 != SV_KEY1) THEN
IF SV_KEY1 != ' ' THEN
DUMMY := FUNC_OT_SET1();
END IF;
SV_KEY1 := IN1REC(i).キー1 ;
DUMMY := FUNC_INIT_OT_SET1(i);
END IF;
-- 集計処理
入力件数 := 入力件数 + 1;
SV1REC.件数 := SV1REC.件数 + 1;
SV2REC.件数 := SV2REC.件数 + 1;
END LOOP;
IF IXO1 > 0 THEN
DUMMY := FUNC_INSERT1();
IXO1 := 0;
END IF;
IF IXO2 > 0 THEN
DUMMY := FUNC_INSERT2();
IXO2 := 0;
END IF;
END LOOP;
IF 入力件数 > 0 THEN
DUMMY := FUNC_OT_SET1();
DUMMY := FUNC_INSERT1();
DUMMY := FUNC_OT_SET2();
DUMMY := FUNC_INSERT2();
END IF;
COMMIT;
DBMS_OUTPUT.PUT_LINE('入力件数 :=' || 入力件数);
DBMS_OUTPUT.PUT_LINE('出力件数1 :=' || 出力件数1);
DBMS_OUTPUT.PUT_LINE('出力件数2 :=' || 出力件数2);
DBMS_OUTPUT.PUT_LINE('エラー数:' || vBulkErrors);
SELECT TO_CHAR(SYSTIMESTAMP,'yyyy/mm/dd hh24:mi:ss.ff5') INTO WK_TIMESTAMP FROM DUAL;
DBMS_OUTPUT.PUT_LINE('終了時刻:' || WK_TIMESTAMP);
CLOSE CUR1;
END PROC_BULK2;
補足
・データ量は「キー1 > キー2」の関係
キー1が大分類、キー2が中分類(小分類)といった関係です。
キー1がブレイクしたら、売上履歴集計1に出力します。
キー1・キー2がブレイクしたら、売上履歴集計2に出力します。
・[FUNCTION]については、本来の使用方法とは異なっている
PL/SQLの関数[FUNCTION]は、値を返すことが前提です。
今回、同じコーディングをしたくないために、複数個所から実行される処理をFUNCTIONで定義し、返すものがないので、DUMMYという項目を定義し、値を返す形にしています。
・終了処理での[IF 入力件数 > 0 THEN]について
IF 入力件数 > 0 THEN
DUMMY := FUNC_OT_SET1();
DUMMY := FUNC_INSERT1();
DUMMY := FUNC_OT_SET2();
DUMMY := FUNC_INSERT2();
END IF;
これがないと、最後のブレイクキーデータが処理されません。
理由は、以下の記事を参照頂ければと思います。

コンロトールブレイク キーブレイクのロジックを徹底解説!VBAで確認
コントロールブレイク・キーブレイク処理をこれから作成する、作成中だけど正確に動かない、作成したけど正確性を確認したいなど思われている方へ、チェックポイントを含め詳細を紐解いて解説します。基本中の基本であるロジックをマスターしちゃいましょう!
処理時間の比較
・2,000件 0:09
・ 1件 0:38
開始時刻:25-05-25 06:14:04.953000
入力件数 :=1500000
出力件数1 :=15000
出力件数2 :=150000
エラー数:0
終了時刻:25-05-25 06:14:15.545000
開始時刻:25-05-25 06:15:03.636000
入力件数 :=1500000
出力件数1 :=15000
出力件数2 :=150000
エラー数:0
終了時刻:25-05-25 06:15:41.429000



コメント