OPENQUERYにパラメータを含める


86

SQLopenquery内で次のようなパラメータを使用するにはどうすればよいですか。

SELECT * FROM OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME
where field1=@someParameter') T1 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME
T2 ON T1.PK = T2.PK

回避策は、openqueryを使用してビューを作成し、結合でビューを使用することです
Ismael

回答:


156

OPENQUERYのドキュメントから、次のように記載されています。

OPENQUERYは、引数に変数を受け入れません。

回避策については、この記事参照してください。

更新:

提案されているように、私は以下の記事からの推奨事項を含めています。

基本値を渡す

基本的なTransact-SQLステートメントがわかっているが、1つ以上の特定の値を渡す必要がある場合は、次のサンプルのようなコードを使用します。

DECLARE @TSQL varchar(8000), @VAR char(2)
SELECT  @VAR = 'CA'
SELECT  @TSQL = 'SELECT * FROM OPENQUERY(MyLinkedServer,''SELECT * FROM pubs.dbo.authors WHERE state = ''''' + @VAR + ''''''')'
EXEC (@TSQL)

クエリ全体を渡す

Transact-SQLクエリ全体またはリンクサーバーの名前(あるいはその両方)を渡す必要がある場合は、次のサンプルのようなコードを使用してください。

DECLARE @OPENQUERY nvarchar(4000), @TSQL nvarchar(4000), @LinkedServer nvarchar(4000)
SET @LinkedServer = 'MyLinkedServer'
SET @OPENQUERY = 'SELECT * FROM OPENQUERY('+ @LinkedServer + ','''
SET @TSQL = 'SELECT au_lname, au_id FROM pubs..authors'')' 
EXEC (@OPENQUERY+@TSQL) 

Sp_executesqlストアドプロシージャを使用する

多層引用符を回避するには、次のサンプルのようなコードを使用します。

DECLARE @VAR char(2)
SELECT  @VAR = 'CA'
EXEC MyLinkedServer.master.dbo.sp_executesql
N'SELECT * FROM pubs.dbo.authors WHERE state = @state',
N'@state char(2)',
@VAR

9
これらの例のいずれかを使用して、execコマンドから返されたレコードをどのように取得しますか?
philreed 2015

2
レコードを取得するために、私は常に結果セットのテーブル変数または一時テーブルを作成してから使用しましたINSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret

6
@JamesChen、逆方向に作業するときに考えるのが最も簡単です。OpenQueryのクエリから始めますSELECT * FROM tab WHERE col = 'Y'。そのステートメントを文字列としてOpenQueryに渡すには、すべての一重引用符をエスケープする必要がありますSELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' ')。次に、OpenQueryを使用してSELECTを動的SQLに渡すには、これらの引用符をエスケープする必要がありますEXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'。お役に立てれば!
2017年

(コメントで@を使用できないため、「at」を使用します)paramsを使用した動的DAX ... DAX文字列を準備しましたが、Openqueryが機能しませんでした。WholeQueryメソッドを渡します。これを介してDAXを実行しました:SET atDAX = REPLACE(atDAX、 '' ''、 '' '' '')そして最後に文字列を閉じて最後のブラケットを追加する必要がありました... EXEC(atOPENQUERY + atDAX + '' '' + ')')
TDP

@TDPインラインコードをインラインコードとしてフォーマットした場合(バッククォートを使用):SET @DAX = REPLACE(@DAX, '''', '''''')
MathieuGuindon19年

15

文字列を作成したら、OPENQUERYを使用して文字列を実行できます。このルートを使用する場合は、セキュリティについて考え、ユーザーが入力したテキストをSQLに連結しないように注意してください。

DECLARE @Sql VARCHAR(8000)
SET @Sql = 'SELECT * FROM Tbl WHERE Field1 < ''someVal'' AND Field2 IN '+ @valueList 
SET @Sql = 'SELECT * FROM OPENQUERY(SVRNAME, ''' + REPLACE(@Sql, '''', '''''') + ''')'
EXEC(@Sql)

あなたが持つコンテキストでOPENQUERYを使用したい場合は残念ながらそれは、仕事をしないifで、たとえば、if (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
ステファンBrendle

@ Stefan-openqueryから選択して、結果を一時テーブルに挿入できます。もちろん、そこからあなたの選択肢はたくさん開かれます。
Jagd 2014

13

MSDNページから:

OPENQUERYは引数に変数を受け入れません

基本的に、これは動的クエリを発行できないことを意味します。サンプルが試みていることを達成するには、これを試してください。

SELECT * FROM 
   OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME') T1 
   INNER JOIN 
   MYSQLSERVER.DATABASE.DBO.TABLENAME T2 ON T1.PK = T2.PK 
where
   T1.field1 = @someParameter

明らかに、TABLENAMEテーブルに大量のデータが含まれている場合、これもネットワークを通過し、パフォーマンスが低下する可能性があります。一方、少量のデータの場合、これはうまく機能し、execアプローチで必要になる可能性のある動的なSQL構築のオーバーヘッド(SQLインジェクション、引用符のエスケープ)を回避します。


これは、私が達成しようとしていたことを成功させるための正しい道を私に示しました!ありがとうございました!これにはもっと賛成票が必要です
マラチ

7

実際、これを行う方法を見つけました。

DECLARE @username varchar(50)
SET @username = 'username'
DECLARE @Output as numeric(18,4)
DECLARE @OpenSelect As nvarchar(500)
SET @OpenSelect = '(SELECT @Output = CAST((CAST(pwdLastSet As bigint) / 864000000000) As numeric(18,4)) FROM OpenQuery (ADSI,''SELECT pwdLastSet
                                FROM  ''''LDAP://domain.net.intra/DC=domain,DC=net,DC=intra''''
                                WHERE objectClass =  ''''User'''' AND sAMAccountName = ''''' + @username + '''''
                          '') AS tblADSI)'
EXEC sp_executesql @OpenSelect, N'@Output numeric(18,4) out', @Output out
SELECT @Output As Outputs

これにより、OpenQueryの実行結果が変数@Outputに割り当てられます。

MSSQL 2012でストアドプロシージャをテストしましたが、MSSQL2008以降で動作するはずです。

Microsoftによると、sp_executesql(Transact-SQL):適用対象:SQL Server(SQL Server 2008から現在のバージョン)、Windows Azure SQLデータベース(初期リリースから現在のリリース)。(http://msdn.microsoft.com/en-us/library/ms188001.aspx


これは、スカラー出力でのみ機能します。XmlまたはTable変数を試してみてください。これは機能しません。
user5855178 2018

4
DECLARE @guid varchar(36);  select @guid= convert(varchar(36), NEWID() );
/*
    The one caveat to this technique is that ##ContextSpecificGlobal__Temp should ALWAYS have the exact same columns.  
    So make up your global temp table name in the sproc you're using it in and only there!
    In this example I wanted to pass in the name of a global temporary table dynamically.  I have 1 procedure dropping 
    off temporary data in whatever @TableSrc is and another procedure picking it up but we are dynamically passing 
    in the name of our pickup table as a parameter for OPENQUERY.
*/
IF ( OBJECT_ID('tempdb..##ContextSpecificGlobal__Temp' , 'U') IS NULL )
    EXEC ('SELECT * INTO ##ContextSpecificGlobal__Temp FROM OPENQUERY(loopback, ''Select *,''''' +  @guid +''''' as tempid FROM ' + @TableSrc + ''')')
ELSE 
    EXEC ('INSERT ##ContextSpecificGlobal__Temp SELECT * FROM OPENQUERY(loopback, ''Select *,''''' +  @guid +''''' as tempid FROM ' + @TableSrc + ''')')

--If this proc is run frequently we could run into race conditions, that's why we are adding a guid and only deleting
--the data we added to ##ContextSpecificGlobal__Temp
SELECT * INTO #TableSrc FROM ##ContextSpecificGlobal__Temp WHERE tempid = @guid

BEGIN TRAN t1
    IF ( OBJECT_ID('tempdb..##ContextSpecificGlobal__Temp' , 'U') IS NOT NULL ) 
    BEGIN
        -- Here we wipe out our left overs if there if everyones done eating the data
        IF (SELECT COUNT(*) FROM ##ContextSpecificGlobal__Temp) = 0
            DROP TABLE ##ContextSpecificGlobal__Temp
    END
COMMIT TRAN t1

-- YEAH! Now I can use the data from my openquery without wrapping the whole !$#@$@ thing in a string.

2
SELECT field1 FROM OPENQUERY 
                   ([NameOfLinkedSERVER], 
                   'SELECT field1 FROM TABLENAME') 
                           WHERE field1=@someParameter T1 
                                 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME           
                                 T2 ON T1.PK = T2.PK

4
このコードには、A)TABLENAMEのすべての行のfield1がリンクサーバーから渡されるという警告が必要です。これは非常にコストがかかる可能性のある操作です。B)
内部

2

動的SQLとOpenQueryを組み合わせます。(これはTeradataサーバーに送られます)

DECLARE 
    @dayOfWk    TINYINT = DATEPART(DW, GETDATE()),
    @qSQL       NVARCHAR(MAX) = '';

SET @qSQL = '
SELECT
    *
FROM
    OPENQUERY(TERASERVER,''
        SELECT DISTINCT
            CASE
                WHEN ' + CAST(@dayOfWk AS NCHAR(1)) + ' = 2
                THEN ''''Monday''''
                ELSE ''''Not Monday''''
            END
        '');';

EXEC sp_executesql @qSQL;

1

次の例では、部門パラメーターをストアドプロシージャ(spIncreaseTotalsRpt)に渡し、同時にすべてOPENQUERYから一時テーブルを作成しています。Tempテーブルは、インスタンスの外部で参照できるように、グローバルTemp(##)である必要があります。exec sp_executesqlを使用すると、departmentパラメーターを渡すことができます。

注:sp_executeSQLを使用するときは注意してください。また、管理者がこのオプションを利用できない場合があります。

これが誰かを助けることを願っています。

 IF OBJECT_ID('tempdb..##Temp') IS NOT NULL
/*Then it exists*/
    begin
       DROP TABLE ##Temp
    end 
 Declare @Dept as nvarchar(20) ='''47'''

 declare @OPENQUERY  as nvarchar(max)
set @OPENQUERY = 'Select ' + @Dept + ' AS Dept,  * into ##Temp from openquery(SQL_AWSPROD01,''' 

declare @sql nvarchar(max)= @openquery +  'SET FMTONLY OFF EXECUTE SalaryCompensation.dbo.spIncreaseTotalsRpts ' + '''' + @Dept + ''''  + ''')'
declare @parmdef nvarchar(25) 
DECLARE @param nvarchar(20) 

SET @parmdef = N'@Dept varchar(20)'
-- select @sql
-- Print @sql + @parmdef  + @dept
exec sp_executesql @sql,@parmdef, @Dept  
Select * from ##Temp

結果

部門増加Cnt0 1 2 3 4 5 6 0.0000 1.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000


0

私は自分に合った方法を見つけました。リンクサーバーがスクラッチテーブルを使用する必要がありますただしがアクセスできるます。

テーブルを作成し、必要な値を入力してから、リンクサーバーを介してそのテーブルを参照します。

SELECT * 
FROM OPENQUERY(KHSSQLODSPRD,'SELECT *
  FROM ABC.dbo.CLAIM A WITH (NOLOCK)
  WHERE A.DOS >= (SELECT MAX(DATE) FROM KHSDASQL01.DA_MAIN.[dbo].[ALLFILENAMES]) ')

0
declare @p_Id varchar(10)
SET @p_Id = '40381'

EXECUTE ('BEGIN update TableName
                set     ColumnName1 = null,
                        ColumnName2 = null,
                        ColumnName3 = null,
                        ColumnName4 = null
                 where   PERSONID = '+ @p_Id +'; END;') AT [linked_Server_Name]

キーワードはopenqueryの男です:)
クリスチャン

これは別のアプローチです。知っている人openqueryは誰でもこのアプローチも知っておくべきです。それはまたはるかにきれいです。だから私の側から+1。
シェイクアブドゥルワヒド

0

execute代わりにメソッドを使用できますopenquery。そのコードははるかにクリーンです。linked serverクエリ結果を変数で取得する必要がありました。以下のコードを使用しました。

CREATE TABLE #selected_store
(
   code VARCHAR(250),
   id INT
)
declare @storeId as integer = 25
insert into #selected_store (id, code) execute('SELECT store_id, code from quickstartproductionnew.store where store_id = ?', @storeId) at [MYSQL]  

declare @code as varchar(100)
select @code = code from #selected_store
select @code
drop table #selected_store

注意:

クエリが機能しない場合は、接続remote proc transaction promotionが設定されていることを確認しfalseてくださいlinked server

EXEC master.dbo.sp_serveroption
       @server = N'{linked server name}',
       @optname = N'remote proc transaction promotion',
       @optvalue = N'false';

-1

上記の@TuanZaidiの例に基づいた簡単な例で、最も簡単に思えました。OPENQUERYの外側でフィルターを実行できることを知りませんでした...とても簡単です!

ただし、私の場合は変数に詰め込む必要があったため、単一の値を返すために追加のサブクエリレベルを作成しました。

SET @SFID = (SELECT T.Id FROM (SELECT Id,  Contact_ID_SQL__c  FROM OPENQUERY([TR-SF-PROD], 'SELECT Id,  Contact_ID_SQL__c FROM Contact') WHERE Contact_ID_SQL__c = @ContactID) T)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.