compresi i parametri in OPENQUERY


86

Come posso utilizzare un parametro all'interno di sql openquery, come ad esempio:

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

Una soluzione alternativa è creare una vista con la query aperta e quindi utilizzare la vista nel join
Ismael

Risposte:


156

Dalla documentazione di OPENQUERY si afferma che:

OPENQUERY non accetta variabili per i suoi argomenti.

Consulta questo articolo per una soluzione alternativa.

AGGIORNARE:

Come suggerito, includo i consigli dell'articolo di seguito.

Passa valori di base

Quando l'istruzione Transact-SQL di base è nota, ma è necessario passare uno o più valori specifici, utilizzare un codice simile all'esempio seguente:

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)

Passa l'intera query

Quando è necessario passare l'intera query Transact-SQL o il nome del server collegato (o entrambi), utilizzare un codice simile all'esempio seguente:

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) 

Utilizzare la stored procedure Sp_executesql

Per evitare le virgolette a più livelli, utilizzare codice simile al seguente esempio:

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
Utilizzando uno di questi esempi, come si recuperano i record restituiti dal comando exec?
philreed

2
Per recuperare i record, ho sempre creato una variabile di tabella o una tabella temporanea del mio set di risultati, quindi ho utilizzatoINSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret

6
@JamesChen, è più facile pensarci quando lavori a ritroso. Inizia con interrogazione del OpenQuery: SELECT * FROM tab WHERE col = 'Y'. Per superare questa affermazione come una stringa a ApriQuery, tutte le singole citazioni devono fuggiti: SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' '). Poi, per passare il SELECT che utilizza OPENQUERY per SQL dinamico, quelle citazioni devono essere sfuggito: EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'. Spero che sia di aiuto!
Bret

(non è possibile utilizzare @ nei commenti, quindi utilizzare "at") DAX dinamico con parametri ... Ho preparato la stringa DAX ma Openquery non ha funzionato. Passa il metodo Whole Query funzionato. Ho eseguito il mio DAX attraverso questo: SET atDAX = REPLACE (atDAX, '' '', '' '' '') e alla fine ho dovuto chiudere la stringa e aggiungere una parentesi finale ... EXEC (atOPENQUERY + atDAX + '' '' + ')')
TDP

@TDP potresti, se formattassi il codice inline come codice inline (usando i backtick):SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon

15

È possibile eseguire una stringa con OPENQUERY una volta creata. Se segui questa strada, pensa alla sicurezza e fai attenzione a non concatenare il testo inserito dall'utente nel tuo 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)

Sfortunatamente non funziona, se vuoi usare OpenQuery nel contesto if, ad esempio inif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
Stefan Brendle

@Stefan: puoi selezionare da openquery e inserire i risultati in una tabella temporanea. Da lì le tue opzioni si aprono molto, ovviamente.
Jagd

13

Dalla pagina MSDN :

OPENQUERY non accetta variabili per i suoi argomenti

Fondamentalmente, questo significa che non puoi emettere una query dinamica. Per ottenere ciò che sta tentando il tuo campione, prova questo:

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

Chiaramente se la tua tabella TABLENAME contiene una grande quantità di dati, anche questi passeranno attraverso la rete e le prestazioni potrebbero essere scadenti. D'altra parte, per una piccola quantità di dati, questo funziona bene ed evita i sovraccarichi dinamici della costruzione sql (sql injection, escape delle virgolette) che un execapproccio potrebbe richiedere.


questo mi ha indirizzato sulla strada giusta per riuscire in quello che stavo cercando di realizzare! Grazie! questo dovrebbe avere più voti positivi
Malachia

7

In realtà, abbiamo trovato un modo per farlo:

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

Questo assegnerà il risultato dell'esecuzione di OpenQuery, nella variabile @Output.

Abbiamo testato la procedura Store in MSSQL 2012, ma dovrebbe funzionare con MSSQL 2008+.

Microsoft afferma che sp_executesql (Transact-SQL): si applica a: SQL Server (da SQL Server 2008 alla versione corrente), database SQL di Windows Azure (versione iniziale fino alla versione corrente). ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )


Funziona solo per l'output scalare. Prova Xml o una variabile di tabella e non funzionerà.
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.

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
Questo codice richiede un avviso che A) field1 per TUTTE le righe di TABLENAME verrà passato dal server collegato, un'operazione potenzialmente molto costosa. B) L'INNER JOIN potrebbe anche essere "molto costoso"
brewmanz

2

Combina SQL dinamico con OpenQuery. (Questo va a un server 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

Nell'esempio seguente sto passando un parametro di reparto a una stored procedure (spIncreaseTotalsRpt) e allo stesso tempo sto creando una tabella temporanea tutto da un OPENQUERY. La tabella Temp deve essere una Temp globale (##) in modo che possa essere referenziata al di fuori del suo intance. Utilizzando exec sp_executesql è possibile passare il parametro di reparto.

Nota: fare attenzione quando si utilizza sp_executeSQL. Anche il tuo amministratore potrebbe non avere questa opzione a tua disposizione.

Spero che questo aiuti qualcuno.

 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

Risultati

Aumento profondità Cnt 0 1 2 3 4 5 6 0.0000 1.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000


0

Ho scoperto un modo che funziona per me. Tuttavia , richiede l'uso di una tabella di lavoro a cui ha accesso un server collegato .

Ho creato una tabella e l'ho popolata con i valori di cui ho bisogno, quindi faccio riferimento a quella tabella tramite un server collegato.

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]

La parola chiave è tizio openquery :)
Christian

è un approccio alternativo. Chi lo sa openquerydovrebbe conoscere anche questo approccio. È anche molto più pulito. Quindi +1 da parte mia.
Sheikh Abdul Wahid

0

Possiamo usare il executemetodo invece di openquery. Il suo codice è molto più pulito. Ho dovuto ottenere il linked serverrisultato della query in una variabile. Ho usato il seguente codice.

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

Nota:

se la tua query non funziona, assicurati che remote proc transaction promotionsia impostata come falseper la tua linked serverconnessione.

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

-1

Semplice esempio basato sull'esempio di @Tuan Zaidi sopra che sembrava il più semplice. Non sapevo che puoi fare il filtro all'esterno di OPENQUERY ... molto più facile!

Tuttavia, nel mio caso, avevo bisogno di inserirlo in una variabile, quindi ho creato un livello di query secondario aggiuntivo per restituire un singolo valore.

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)

-1

Provalo in questo modo, dovrebbe funzionare, facile! Nella clausola WHERE, dopo il nome della colonna e il segno di uguale a: - aggiungi DUE virgolette singole, il valore di ricerca e poi TRE virgolette singole. Chiudere la staffa.

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


La tua risposta usa SQL letterale, ma l'OP vuole usare un parametro (nota il suo riferimento a field1=@someParameter).
Jeff Mergler
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.