0

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でそれを克服することは可能ですか?

4

2 に答える 2

2

なぜこのすべての作業を行う必要があるのか​​わかりません。Oracle 8iは、一括収集によるRETURNINGINTOをサポートしていました。 詳細をご覧ください

したがって、このステートメントは非動的SQLで実行できるはずです。このようなもの:

UPDATE tablename 
SET foo = 'bar' 
returning  col1, col2 bulk collect into col1_t, col2_t;
于 2012-04-02T16:09:57.247 に答える
1

すべての無関係性を取り除いて、あなたの質問は単純だと思います。

この更新ステートメントはSQLで実行されます。

update xy set id = id + (SELECT min(id) FROM xy);

そして、この匿名ブロックも実行されます。

begin
    update xy set id = id + 100;
end;

しかし、2つを組み合わせても機能しません。

begin
    update xy set id = id + (SELECT min(id) FROM xy);
end;

おそらく、古いOracleの制限に遭遇したことでしょう。9iより前は、SQLエンジンとPL /SQLSQLエンジンは常に同期していませんでした。そのため、SQLでサポートされている最新の機能は、PL/SQLではサポートされていないことがよくあります。あなたはそれらの1つを持っているようです。

9i Oracleは、2つのエンジンの同期を維持するよう努めてきたため、SQLでは機能するがPL/SQLでは機能しないものを見つけることは非常にまれです。

タスクの性質を考えると、Oracleのバージョンをアップグレードすることはできません。したがって、私が提案できるのは、2つのプロシージャがあることです。1つはサブクエリ構文をサポートします(このようなサブクエリの必要性を回避します。次のようなものです。

CREATE OR REPLACE FUNCTION xy_sqfn
    (main_query VARCHAR2
      , sub_query VARCHAR2   ) 
    RETURN NUMBER 
IS      
    n pls_integer;
BEGIN         
    execute immediate sub_query into n;
    EXECUTE IMMEDIATE 'BEGIN '||main_query||'; END;'
          using n;
    RETURN 5; 
END;

このように呼んでください

result := xy_sqfn ('update xy set id = id + :1'
                   , 'SELECT min(id) FROM xy');

現在、このアプローチは相関サブクエリでは機能しません。だから、あなたがそれらのいずれかを持っているなら、あなたはもう一度何か違うことをする必要があるでしょう。


ちなみに、AUTONOMOUS TRANSACTIONプラグマを使用して、SELECTステートメントでDMLを実行することをごまかすのは、非常に恐ろしいことです。PL / SQLで関数を実行してみませんか?または手順を使用しますか?データ移行をサポートするためのちょっとしたコードを書いているだけなので、それは問題ではないと言うでしょう。これは十分に公平ですが、将来の探求者の利益のために:これをしないでください!それは非常に悪い習慣です!

于 2012-04-03T12:02:42.903 に答える