web-dev-qa-db-ja.com

リモートのユーザー定義関数を呼び出すにはどうすればよいですか?

別のサーバーへのリンクを含むSQLサーバーがあります(例:テスト)リンクサーバーに関数があります(name:getvalue(@parameter))。soこの関数の出力をSQLサーバー2012で取得したいのですが、以下の方法論。

DECLARE @sql nvarchar(4000);
DECLARE @somevalue nvarchar(16)

SET @sql = 'SELECT * FROM OPENQUERY(test, 
                          ''SELECT @somevalue=getvalue(' +CAST('545454545' as nvarchar(10))+ ')'')';
EXEC (@sql)

次のようなエラーが発生しています

リンクサーバー「test」のOLE DBプロバイダー「OraOLEDB.Oracle」がメッセージ「ORA-00936:式がありません」を返しました。メッセージ7321、レベル16、状態2、行1リンク用のOLE DBプロバイダー "OraOLEDB.Oracle"に対して実行するクエリ "SELECT @ somevalue = getvalue(545454545)"の準備中にエラーが発生しましたサーバー「テスト」。

これで私を助けてください。よろしくお願いします

1
user3013146

リモートクエリは2回ネストされます。最初はOPENQUERYで、次にEXECです。 _@somevalue_変数は最も外側のレベルで宣言されており、リモートクエリのスコープに存在しないため、参照は無効です。

しかし、あなたも使用できません変数が2つの理由でそこにあります:

  1. まず第一に、 Colin 't Hartが注記しました のように、リンクサーバーはOracleです。 SELECT @var = function(...)構文はSQL Serverであり、Oracleでは無効です。

  2. oPENQUERY行セット関数を使用すると、リモート側の結果を扱うことができます行セットとしてのみ –途中で、または行セットの代替として変数を設定するようには設計されていません。したがって、基になるクエリで_@var_のような参照が許可されていても(たとえば、リモートサーバーがSQL Serverの場合)、OPENQUERYを使用しても参照できませんouter( "リモートクエリでこれらの変数を使用する場合は、リモートクエリのスコープの変数、および外部スコープが内部スコープ(その側)で宣言された変数にアクセスできない。

この時点で結論付けると、SQL ServerのOracleスカラー関数から結果を取得する最良の方法は、リモートクエリに結果を返すようにすることです行セットとして行セット関数を使用してリモートクエリを呼び出すOPENQUERYのように。 After OPENQUERYから行セットを取得します。SQLServerで許可されていることなら何でも行うことができます。これには、列の値を変数に格納することも含まれます。したがって、OPENQUERYクエリは基本的に次のようになります。

_SELECT
  @somevalue = somecolumn
FROM
  OPENQUERY(
    'test',
    'your Oracle query'
  )
;_

_@somevalue =_の位置に注意してください。これは1レベル上に移動され、有効な構文になり、意味があります。 somecolumnは、結果を返すOracleクエリの単一列に割り当てる名前です。これはおそらく、Oracleクエリが次のようになるはずです。

_SELECT getvalue(argument) AS somecolumn FROM DUAL
_

ここで、パラメータ化する必要がなかった場合argumentの場合、完全なTransact-SQLクエリは次のようになります。

_DECLARE @somevalue nvarchar(16);

SELECT
  @somevalue = myresult
FROM
  OPENQUERY(
    test, 
    'SELECT getvalue(545454545) AS myresult FROM DUAL'
  )
;
_

ただし、引数をパラメーター化できるようにするには、_SELECT FROM OPENQUERY_クエリを動的にする必要があります。文字列として作成します。

_DECLARE @sql nvarchar(max);

…

SET @sql =
N'SELECT
  …
FROM
  OPENQUERY(
    test, 
    ''SELECT getvalue(' + CAST(@someargument AS nvarchar(16)) + N') AS myresult FROM DUAL''
  )
';
_

次に文字列を実行します:

_EXEC (@sql);
_

あなたがすでにやっているすべてのこと。ただし、EXEC (...)構文を使用する場合、結果を変数に直接受け取ることはできません。1、それは結果セットとしてのみ取得できます–はい、OPENQUERYと同じ制限があります。ただし、OPENQUERYとは異なり、FROM句でEXECを呼び出すことはできません。

したがって、この時点では、getvalueの結果を変数に入れるという問題にまだ直面しています。

基本的には、2つの方法で対処できます。 1つ目は、動的クエリから_@somevalue =_ビットを削除して、OPENQUERYの結果セットが最も外側のスコープに渡されるようにすることです。 ... FROM EXEC (...)は実行できませんが、canINSERT ... EXEC (...)を実行します。したがって、EXECの結果セットを一時データセット(テーブル変数など)に挿入します。

_DECLARE @tmpresult TABLE (result nvarchar(16));

INSERT INTO @tmpresult (result)
EXEC (@sql);
_

次に、列の値を一時ストレージからスカラー変数に読み取ります。

_SELECT @somevalue = result FROM @tmpresult;
_

他の、そして私の意見では、より良いオプションは、動的クエリを実行するためにEXEC ()ではなく_EXEC sp_executesql_を使用することです。 _sp_executesql_ システムストアドプロシージャを使用すると、出力パラメーターなど、基になるクエリのパラメーターを設定できます。出力パラメーターは、メインスクリプトで宣言した_@somevalue_変数に結果を渡す方法です。

したがって、動的クエリの_@somevalue =_の部分はそのままにして、宣言するinクエリではなくinvoking whenクエリを宣言します。そうすれば、変数はローカル変数としてだけでなくパラメーターとしても機能します。パラメータ宣言にOUTPUTを含めると、出力パラメータにもなります。だから、これはあなたが行く方法です:

_SET @sql =
N'SELECT
  @tmpvalue = myresult
FROM
  …
';

EXEC sp_executesql @sql, '@tmpvalue nvarchar(16) OUTPUT', @somevalue OUTPUT;
_

上記のコードで、動的クエリの変数の名前を少し変更したことがわかります。これは、動的クエリの変数とメインスクリプトの変数が異なることを強調するために行いました。 _@tmpvalue_は動的クエリ内で使用する変数ですが、_@somevalue_は_@tmpvalue_に格納されている値の実際のレシーバーとして渡すメインスコープの変数です。これは、動的クエリの変数と呼び出し元のスクリプトの変数の間でデータを交換する方法です。また、パラメーター宣言とレシーバー変数を指定するときの両方でOUTPUTを指定する必要があることにも注意してください。これは同じ規則 ユーザー定義のストアドプロシージャの出力パラメーターと同様 です。

要約すると、最終的なコードは次のようになります。

_DECLARE
  @sql      nvarchar(max),
  @somvalue nvarchar(16)
;

SET @sql =
N'SELECT
  @tmpvalue = myresult
FROM
  OPENQUERY(
    test, 
    ''SELECT getvalue(' + CAST(@someargument AS nvarchar(16)) + N') AS myresult FROM DUAL''
  )
';

EXEC sp_executesql
  @sql,
  N'@tmpvalue nvarchar(16) OUTPUT',
  @somevalue OUTPUT
;
_

1実際には you can EXECを使用して、出力パラメーターを含むパラメーターがあります ですが、リモートサーバーで動的クエリを実行するように指定している場合のみ:

_EXEC (@sql, @var1, @var2, ...) AT linked_server
_

通常、これは、リモートサーバーがSQL Server以外のサーバーである場合により便利です。SQLServerインスタンスの場合、_EXEC sp_executesql_メソッドの方が適している可能性があります。

1
Andriy M

@somevalueは、リンクされたクエリのコンテキストには存在しません。上記のメインセッションで宣言されています。 ExecまたはOpenQueryはそれを認識しません。次に、EXECクエリはメインの選択からデータを出力します。変数に値を戻したい場合は、@ sqlを更新してsp_executesqlを使用できます。

SET @Param = N'@returnvalue nvarchar(16) OUTPUT';
EXECUTE sp_executesql @sql, @Param, @returnValue = @someVariable Output;
SELECT @max_title;
0