2010-07-31 12 views
55

Come posso usare un parametro all'interno di sql openquery, come ad esempio:compresi i parametri in OPENQUERY

SELECT * FROM OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME 
where [email protected]') T1 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME 
T2 ON T1.PK = T2.PK 
+0

Una soluzione è creare una visualizzazione con l'openquery e poi utilizzando la vista nel join – Ismael

risposta

98

Dalla documentazione OPENQUERY si afferma che:

OPENQUERY non accetta le variabili per i suoi argomenti.

Vedere questo article per una soluzione alternativa.

UPDATE:

Come suggerito, sto comprese le raccomandazioni l'articolo qui sotto.

Passo di base Valori

Quando l'istruzione di base Transact-SQL è nota, ma si deve passare in uno o più valori specifici, utilizzare il codice che è simile al seguente esempio:

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) 

passare la query intero

Quando si deve passare nell'intera query Transact-SQL o il nome del server collegato (o entrambi), utilizzare cod e che è simile al seguente esempio:

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 (@[email protected]) 

Utilizzare la stored procedure sp_executesql

Per evitare le virgolette multilivello, utilizzare il codice che è 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 
+7

Usando uno di questi esempi, come si recuperano i record restituiti dal comando exec? – philreed

+1

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

+0

Posso chiedere perché è necessario utilizzare tante virgolette singole? –

13

è possibile eseguire una stringa con OPENQUERY una volta che si costruisce su. 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) 
+0

Purtroppo non funziona, se si desidera utilizzare ApriQuery nel contesto con 'se ', per esempio in' if (SELEZIONA COL1 DA OPENQUERY ('Seleziona ...')> 0) INIZIA ... END' –

+0

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

11

Dal MSDN page:

OPENQUERY non accetta le variabili per i suoi argomenti

Fondamentalmente, questo significa che non è possibile emettere una query dinamica. Per ottenere ciò il campione sta cercando, provate 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 vostra tavola TABLENAME contiene una grande quantità di dati, questo andrà attraverso la rete e le prestazioni troppo potrebbe essere scarsa. D'altra parte, per una piccola quantità di dati, questo funziona bene ed evita i sovraccarichi dinamici di costruzione sql (sql injection, escape quotes) che potrebbe richiedere un approccio exec.

+0

questo mi ha indicato lungo la strada giusta per riuscire in quello che stavo cercando di realizzare! Grazie! questo dovrebbe avere più upvotes – Malachi

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 !$#@[email protected] thing in a string. 
1

Nell'esempio seguente sto passando un parametro di reparto a una stored procedure (spIncreaseTotalsRpt) e allo stesso tempo sono cre a un tavolo temporaneo tutto da un OPENQUERY. La tabella Temp deve essere una Temp globale (##) in modo che possa essere referenziata al di fuori del proprio 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

Dept aumentare Cnt 0 1 2 3 4 5 6 0,0000 1,0000 0,0000 0,0000 0,0000 0,0000 0,0000 0,0000

6

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 di archiviazione in MSSQL 2012, ma dovrebbe funzionare con MSSQL 2008+.

Microsoft dice 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)

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

Questo codice richiede un avviso che A) campo1 per * TUTTE * le righe di TABLENAME verranno passate dal server collegato, un'operazione potenzialmente molto costosa. B) l'INNER JOIN potrebbe anche essere "molto costoso" – brewmanz

0

ho trovato un modo che funziona per me. Richiede l'uso di un tavolo scratch che a un server collegato allo ha accesso.

Ho creato una tabella e l'ho popolata con i valori necessari, quindi faccio riferimento a tale 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]) ') 
-2
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] 
-1

semplice esempio in base al largo di esempio di @Tuan Zaidi sopra che sembrava il più facile. Non sapevo che puoi fare il filtro all'esterno di OPENQUERY ... molto più facile!

Tuttavia nel mio caso ho avuto bisogno di inserirli in una variabile, così ho creato un sottolivello 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) 
0

Combinare SQL dinamico con OpenQuery. (Questo va a un server di 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; 
Problemi correlati