einschließlich Parameter in OPENQUERY

84

Wie kann ich einen Parameter in SQL OpenQuery verwenden, z.

SELECT * FROM OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME
where field1=@someParameter') T1 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME
T2 ON T1.PK = T2.PK
gaponte69
quelle
Eine Problemumgehung besteht darin, eine Ansicht mit der offenen Abfrage zu erstellen und dann die Ansicht im Join zu verwenden
Ismael

Antworten:

154

Aus der OPENQUERY- Dokumentation geht hervor, dass:

OPENQUERY akzeptiert keine Variablen für seine Argumente.

In diesem Artikel finden Sie eine Problemumgehung.

AKTUALISIEREN:

Wie vorgeschlagen, füge ich die Empfehlungen aus dem folgenden Artikel hinzu.

Grundwerte übergeben

Wenn die grundlegende Transact-SQL-Anweisung bekannt ist, Sie jedoch einen oder mehrere bestimmte Werte übergeben müssen, verwenden Sie Code, der dem folgenden Beispiel ähnelt:

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)

Übergeben Sie die gesamte Abfrage

Wenn Sie die gesamte Transact-SQL-Abfrage oder den Namen des Verbindungsservers (oder beider) übergeben müssen, verwenden Sie Code, der dem folgenden Beispiel ähnelt:

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) 

Verwenden Sie die gespeicherte Prozedur Sp_executesql

Verwenden Sie Code, der dem folgenden Beispiel ähnelt, um mehrschichtige Anführungszeichen zu vermeiden:

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
Garett
quelle
9
Wie können Sie anhand eines dieser Beispiele die vom Befehl exec zurückgegebenen Datensätze abrufen?
Philreed
2
Um Datensätze abzurufen, habe ich immer eine Tabellenvariable oder eine temporäre Tabelle meiner Ergebnismenge erstellt und dann verwendetINSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret
6
@ JamesChen, es ist am einfachsten, darüber nachzudenken, wenn Sie rückwärts arbeiten. Beginnen Sie mit der OpenQuery-Abfrage : SELECT * FROM tab WHERE col = 'Y'. Um diese Anweisung als Zeichenfolge an OpenQuery zu übergeben, müssen alle einfachen Anführungszeichen maskiert werden : SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' '). Um dann SELECT mit OpenQuery an Dynamic SQL zu übergeben, müssen DIESE Anführungszeichen maskiert werden : EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'. Hoffe das hilft!
Bret
(kann @ in den Kommentaren nicht verwenden, also 'at' verwenden) Dynamischer DAX mit Parametern ... Ich habe den DAX-String vorbereitet, aber Openquery hat nicht funktioniert. Übergeben Sie die Methode "Ganze Abfrage". Ich habe meinen DAX durchlaufen lassen: SET atDAX = REPLACE (atDAX, '' '', '' '' ') und am Ende musste ich den String schließen und eine letzte Klammer hinzufügen ... EXEC (atOPENQUERY + atDAX + '' '' + ')')
TDP
@TDP könnten Sie, wenn Sie den Inline-Code als Inline-Code (mit Backticks) formatiert haben:SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon
15

Sie können einen String mit OPENQUERY ausführen, sobald Sie ihn aufgebaut haben. Wenn Sie diesen Weg gehen, denken Sie an die Sicherheit und achten Sie darauf, vom Benutzer eingegebenen Text nicht in Ihr SQL zu verketten!

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)
Tahbaza
quelle
Leider funktioniert es nicht, wenn Sie OpenQuery im Zusammenhang mit ifzum Beispiel inif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
Stefan Brendle
@Stefan - Sie können aus openquery auswählen und die Ergebnisse in eine temporäre Tabelle einfügen. Von dort aus eröffnen sich Ihnen natürlich viele Möglichkeiten.
Jagd
13

Von der MSDN-Seite :

OPENQUERY akzeptiert keine Variablen für seine Argumente

Grundsätzlich bedeutet dies, dass Sie keine dynamische Abfrage ausgeben können. Versuchen Sie Folgendes, um das zu erreichen, was Ihre Probe versucht:

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

Wenn Ihre TABLENAME-Tabelle eine große Datenmenge enthält, wird dies natürlich auch über das Netzwerk übertragen und die Leistung ist möglicherweise schlecht. Auf der anderen Seite funktioniert dies für eine kleine Datenmenge gut und vermeidet die dynamischen SQL-Konstruktionskosten (SQL-Injection, Escape-Anführungszeichen), die ein execAnsatz möglicherweise erfordert.

Neil Moss
quelle
Dies zeigte mir den richtigen Weg, um erfolgreich zu sein, was ich erreichen wollte! Danke dir! Dies sollte mehr positive Stimmen haben
Malachi
7

Eigentlich haben wir einen Weg gefunden, dies zu tun:

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

Dadurch wird das Ergebnis der OpenQuery-Ausführung in der Variablen @Output zugewiesen.

Wir haben die Store-Prozedur in MSSQL 2012 getestet, sollten aber mit MSSQL 2008+ funktionieren.

Microsoft sagt, dass sp_executesql (Transact-SQL): Gilt für: SQL Server (SQL Server 2008 bis aktuelle Version), Windows Azure SQL-Datenbank (Erstversion bis aktuelle Version). ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )

Juan Medina
quelle
Dies funktioniert nur für die Skalarausgabe. Versuchen Sie es mit XML oder einer Tabellenvariablen, und dies wird nicht funktionieren.
user5855178
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.
Ryan Maloney
quelle
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
Tuan Zaidi
quelle
4
Dieser Code benötigt eine Warnung, dass A) Feld1 für ALLE Zeilen von TABLENAME vom Verbindungsserver übergeben wird - eine möglicherweise sehr teure Operation. B) Der INNER JOIN könnte auch "sehr teuer" sein
Brewmanz
2

Kombinieren Sie Dynamic SQL mit OpenQuery. (Dies geht an einen Teradata-Server)

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;
Mike
quelle
1

Im folgenden Beispiel übergebe ich einen Abteilungsparameter an eine gespeicherte Prozedur (spIncreaseTotalsRpt) und erstelle gleichzeitig eine temporäre Tabelle aus einer OPENQUERY. Die Temp-Tabelle muss ein globales Temp (##) sein, damit außerhalb ihrer Intance auf sie verwiesen werden kann. Mit exec sp_executesql können Sie den Abteilungsparameter übergeben.

Hinweis: Seien Sie vorsichtig, wenn Sie sp_executeSQL verwenden. Möglicherweise steht Ihrem Administrator diese Option auch nicht zur Verfügung.

Hoffe das hilft jemandem.

 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

Ergebnisse

Dept Erhöhung Cnt 0 1 2 3 4 5 6 0,0000 1,0000 0,0000 0,0000 0,0000 0,0000 0,0000 0,0000

Carlos
quelle
0

Ich habe einen Weg gefunden, der für mich funktioniert. Es erfordert jedoch die Verwendung einer Arbeitstabelle, auf die ein Verbindungsserver Zugriff hat.

Ich habe eine Tabelle erstellt und sie mit den benötigten Werten gefüllt. Dann verweise ich über einen Verbindungsserver auf diese Tabelle.

SELECT * 
FROM OPENQUERY(KHSSQLODSPRD,'SELECT *
  FROM ABC.dbo.CLAIM A WITH (NOLOCK)
  WHERE A.DOS >= (SELECT MAX(DATE) FROM KHSDASQL01.DA_MAIN.[dbo].[ALLFILENAMES]) ')
Hannover Faust
quelle
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]
Sachin Khartode
quelle
Schlüsselwort ist openquery Dude :)
Christian
Es ist ein alternativer Ansatz. Wer es weiß, openquerysollte diesen Ansatz ebenfalls kennen. Es ist auch viel sauberer. Also +1 von meiner Seite.
Sheikh Abdul Wahid
0

Wir können executeMethode anstelle von verwenden openquery. Der Code ist viel sauberer. Ich musste das linked serverAbfrageergebnis in einer Variablen erhalten. Ich habe folgenden Code verwendet.

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

Hinweis:

Sollte Ihre Frage nicht funktioniert, stellen Sie sicher , remote proc transaction promotionwird als falsefür Ihre linked serverVerbindung.

EXEC master.dbo.sp_serveroption
       @server = N'{linked server name}',
       @optname = N'remote proc transaction promotion',
       @optvalue = N'false';
Scheich Abdul Wahid
quelle
-1

Einfaches Beispiel basierend auf dem obigen Beispiel von @Tuan Zaidi, das am einfachsten zu sein schien. Ich wusste nicht, dass Sie den Filter außerhalb von OPENQUERY ausführen können ... so viel einfacher!

In meinem Fall musste ich es jedoch in eine Variable einfügen, sodass ich eine zusätzliche Unterabfrageebene erstellte, um einen einzelnen Wert zurückzugeben.

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)
Anthony Griggs
quelle