2012-04-02 12 views
0

PostgreSQLからRETURNING *の動作をエミュレートするDML文の影響を受ける行をフェッチするOracle関数(8i用)を作成しました。典型的な関数呼び出しは次のようになります。pl/sqlブロックのsubselects上のOracle 8i動的SQLエラー

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;

このperculiar建設の背後にある理由は、DMLから値を取得するための唯一の方法であると思われることです複数の行に影響するステートメント。 col1_tとcol2_tは両方とも、テーブルの列に対応する型のコレクションとして宣言されています。

最後に、問題に。渡された照会に副選択が含まれていると、その関数を実行すると構文エラーが発生します。以下これを説明するためのsimpe例である:

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分(ID)がある記載されているSELECT)

ORA-06550: line 1, column 32: PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: (- + mod not null others avg count current exists max min prior sql stddev sum variance execute forall time timestamp interval date

この問題がで発生8i(8.1.6)ではなく、10gである。 BEGIN .. ENDブロックが削除された場合、問題は消えます。 照会の副選択が他の何か、すなわち定数で置き換えられた場合、問題は消えます。

残念ながら、私は8iで止まってBEGINを削除しています.. ENDはオプションではありません(上記の説明を参照)。

ここには特定のOracle 8iの制限事項がありますか?それは動的SQLでそれを克服することは可能ですか?

+1

8i ??拡張サポートは5年以上前に終了しました... – Ben

+0

@Ben:笑うことができますが、私はまだ8iデータベースインスタンスに変更を加えています... –

+0

@JeffreyKemp、私は笑っていません!時には移動するのが難しいこともあることを理解しています(まだ接続されている9iボックスがいくつかありますが、アップグレードしています)。可能な限りサポートされているバージョンを実際に使用する必要がある場合は、サポートされていないバージョンを使用するよりも理にかなっています。 – Ben

答えて

2

なぜこの作業をすべて行う必要があるのか​​不明です。 Oracle 8iでは、バルク収集を使用してRETURNING INTOをサポートしていました。 Find out more

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

UPDATE tablename 
SET foo = 'bar' 
returning col1, col2 bulk collect into col1_t, col2_t; 
+0

私は同じテーブルに対して発行された多くのクエリがあります。私は匿名のpl/sqlブロックを作成しないようにしたいと思います。代わりにプロシージャ内にラップして、各クエリに対して呼び出すことができます。それで、私はそのようなプロシージャに動的に渡されたクエリを実行しなければなりません。 – alexk

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/SQL SQLエンジンが常に同期していませんでした。したがって、SQLでサポートされている最新の機能は、PL/SQLではサポートされていないことがよくあります。あなたのようなものがあるようです。

9iオラクルは、2つのエンジンを同期させようと努力しているので、SQLでは動作しますがPL/SQLでは動作しないものを見つけるのは非常にまれです。

タスクの性質上、ご使用のOracleのバージョンをアップグレードすることはできません。だから私は提案することができるすべては、そのようなサブクエリの必要性を回避することにより、あなたは(二つの手順、サブクエリ構文をサポートしているものを持っているということである。このような何か:この

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

よう

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; 

呼び出し、それを今、このアプローチは、相関サブクエリのために動作しません。それあなたがそれらのいずれかを持っているので、あなたは再び別の何かをする必要があります。


ところで、自律型トランザクションを使用してプラグマを使用してSELECT文でDMLを実行することは非常に恐ろしいことです。 PL/SQLで関数を実行するだけではどうですか?または手順を使用しますか?データ移行をサポートするためのコードを書いているだけなので、問題ではないと言います。それは十分ですが、将来のシーカーの利益のために:これをしないでください!非常に悪い習慣です!

+0

ありがとうございます。なぜなら、選択と自律型トランザクションが必要なのは、DMLステートメントのデータがPostgreSQLデータベースの類似のものと結果を比較するPythonアプリケーションに来なければならないからです。関数内のDMLステートメントをラップするアプローチとSQLとpl/sqlエンジンの間の8i不一致の問題を抱えていることから、前の解答ごとに匿名のpl/sqlブロックに移動し、ホスト変数を使用して結果を収集します。私に運が欲しい:-) – alexk