(これはすべて Oracle 10g です):
CREATE OR REPLACE FUNCTION bar(...)
IS
v_first_type VARCHAR2(100) ;
v_second_type VARCHAR2(100);
CURSOR cur IS SELECT a,b FROM source_table ;
v_a int;
v_b char;
BEGIN
OPEN cur;
<<l_next>> --10G doesn't have the continue statement.
LOOP
FETCH cur INTO v_a, v_b ;
EXIT WHEN cur%NOTFOUND ;
--Ignore Record Case: ignore the record entirely
IF a == -1 THEN
-- do something
GOTO l_next ; --10g doesn't have the continue statement.
ELSE
-- do something else
v_first := 'SUCCESS' ;
END IF;
-- Transform Case:
IF b == 'z' THEN
-- do something
v_second := 'something';
ELSE
-- do something
v_second := 'something else';
END IF;
INSERT INTO report_table VALUES (v_first, v_second);
END LOOP;
CLOSE cur;
EXCEPTION
...
END;
私は大学を出て初めての仕事に就いています。上記の一般的なフレームワークのように見えるいくつかのレガシーコードを調べています(ただし、数百行の長さであり、はるかに複雑な処理を使用します(セットベースのソリューションは不可能です))。
1 つのテーブルからカーソルに多数の行をプルし、カーソルをループして、カーソルを変換し、結果をレポート テーブルに挿入します。カーソルはすべてのレコードを挿入するわけではありません。レコードに問題がある場合、または何らかの理由で気に入らない場合は、レコードを挿入せずにスキップします (GOTO ステートメントを参照)。
問題 1: 挿入は、ループの外側で最後に FORALL を実行するのではなく、ループの内側で 1 つずつ発生しています。
問題 2: カーソルが BULK COLLECT を使用していません。
これに加えて、BULK COLLECT を使用しないカーソルを持つストアド プロシージャがあり、カーソル内のレコードをループしながらこの関数を発行します。ループされた各レコードの最後に 1 つのコミットが発行されます。ここで書いている関数にはコミットはありません。
コードを次のように書き直したいと思います。
CREATE OR REPLACE FUNCTION bar(...)
IS
CURSOR cur IS SELECT a,b FROM source_table ;
TYPE t_source IS TABLE OF cur%ROWTYPE INDEX BY PLS_INTEGER;
TYPE t_report IS TABLE OF destination_table%ROWTYPE INDEX BY PLS_INTEGER;
v_sources t_source;
v_reports t_report
v_report_inx INT := 0; -- To Prevent Sparse Collection
BEGIN
OPEN cur;
<<l_next>> --10G doesn't have the continue statement.
LOOP
FETCH cur BULK COLLECT INTO v_sources LIMIT 100 ;
EXIT WHEN v_sources.count = 0 ;
FOR i IN 1 .. v_sources LOOP
--Ignore Record Case: ignore the record entirely
IF v_sources(i).a == -1 THEN
-- do something
GOTO l_next ; --10g doesn't have the continue statement.
ELSE
-- do something else
v_reports(v_report_inx).first := 'SUCCESS' ;
END IF;
-- Transform Case:
IF v_sources(i).b == 'z' THEN
-- do something
v_reports(v_report_inx).second := 'something';
ELSE
-- do something
v_reports(v_report_inx).second := 'something else';
END IF;
v_report_inx := v_report_inx + 1;
END LOOP;
END LOOP;
FORALL i in 1 .. v_reports.count
INSERT INTO report_table (first, second) VALUES (v_reports(i).first, v_reports(i).v_second);
CLOSE cur;
EXCEPTION
...
END;
重要な変更点は、1) 連想配列への BULK COLLECT の使用、および 2) 別の連想配列からの FORALL の使用です。
2 つの質問があります。
1) 最初のスニペットで提供したフレームワークに基づいて、私の変更はそれを行うための最も優れた方法ですか? 別の方法でやりますか?
2) 誰かが BULK COLLECT と FORALL を使用しない理由を私が考えていない理由はありますか? おそらく、レガシー コードではまだ気付いていない複雑な処理でしょうか。このコードはもともと 2002 年に作成されました (したがって、8i または 9i であると推測されます) が、その後更新されています。9i には一括バインディングがありました。8i にも一括バインディングがありました。どちらも連想配列を持っていました。ですから、一括バインディングを使用していなかったのには理由があるに違いないと思います。