DMLステートメントの影響を受ける行をフェッチするOracle関数(8i用)を作成し、PostgreSQLからのRETURNING*の動作をエミュレートしました。典型的な関数呼び出しは次のようになります。
SELECT tablename_dml('UPDATE tablename SET foo = ''bar''') FROM dual;
この関数はテーブルごとに自動的に作成され、動的SQLを使用して引数として渡されたクエリを実行します。さらに、クエリを動的に実行するステートメントもBEGIN..ENDブロックでラップされます。
EXECUTE IMMEDIATE 'BEGIN'||query||' RETURNING col1, col2 BULK COLLECT INTO :1, :2;END;' USING OUT col1_t, col2_t;
この奇妙な構造の背後にある理由は、複数の行に影響を与えるDMLステートメントから値を取得する唯一の方法のように思われるためです。col1_tとcol2_tはどちらも、テーブルの列に対応するタイプのコレクションとして宣言されています。
最後に、問題に。渡されたクエリに副選択が含まれている場合、関数を実行すると構文エラーが発生します。以下は、これを説明するための簡単な例です。
CREATE TABLE xy(id number, name varchar2(80));
CREATE OR REPLACE FUNCTION xy_fn(query VARCHAR2) RETURN NUMBER IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'BEGIN '||query||'; END;';
ROLLBACK;
RETURN 5;
END;
SELECT xy_fn('update xy set id = id + (SELECT min(id) FROM xy)') FROM DUAL;
最後のステートメントは次のエラーを生成します:(そこに記載されているSELECTはSELECTmin(id)です)
ORA-06550:1行目、32列目:PLS-00103:次のいずれかが必要な場合に記号「SELECT」が発生しました:(-+ mod not null others avg count current presents max min previous sql stddev sum Variance execute foralltimeタイムスタンプ間隔日にち
この問題は8i(8.1.6)で発生しますが、10gでは発生しません。BEGIN .. ENDブロックが削除されると、問題は解消されます。クエリの副選択が他の何か、つまり定数に置き換えられると、問題は解消されます。
残念ながら、私は8iで立ち往生しており、BEGIN .. ENDを削除することはできません(上記の説明を参照してください)。
ここで、特定のOracle 8iの制限がありますか?動的SQLでそれを克服することは可能ですか?