incluindo parâmetros em OPENQUERY


86

Como posso usar um parâmetro dentro do sql openquery, como:

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

Uma solução alternativa é criar uma visão com a openquery e, em seguida, usar a visão na junção
Ismael

Respostas:


156

Da documentação do OPENQUERY , afirma que:

OPENQUERY não aceita variáveis ​​para seus argumentos.

Consulte este artigo para obter uma solução alternativa.

ATUALIZAR:

Conforme sugerido, estou incluindo as recomendações do artigo abaixo.

Passe valores básicos

Quando a instrução Transact-SQL básica for conhecida, mas você precisar passar um ou mais valores específicos, use um código semelhante ao seguinte exemplo:

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)

Passe na consulta inteira

Quando você precisar passar toda a consulta Transact-SQL ou o nome do servidor vinculado (ou ambos), use um código semelhante ao seguinte exemplo:

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) 

Use o procedimento armazenado Sp_executesql

Para evitar as aspas em várias camadas, use um código semelhante ao seguinte exemplo:

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
Usando qualquer um desses exemplos, como você recupera os registros retornados do comando exec?
philreed

2
Para recuperar registros, sempre construí uma variável de tabela ou tabela temporária do meu conjunto de resultados e useiINSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret

6
@JamesChen, é mais fácil pensar quando você trabalha para trás. Comece com a consulta do OpenQuery: SELECT * FROM tab WHERE col = 'Y'. Para passar essa declaração como uma string para OpenQuery, todas as aspas simples precisam escapou: SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' '). Então, para passar o SELECT usando OpenQuery para SQL dinâmico, aquelas citações deve ser escapado: EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'. Espero que isto ajude!
Bret

(não pode usar @ nos comentários, então usando 'at') DAX dinâmico com parâmetros ... Eu preparei a string DAX, mas o Openquery não funcionou. Passe o método Whole Query funcionou. Executei meu DAX por meio de: SET atDAX = REPLACE (atDAX, '' '', '' '' '') e no final tive que fechar a string e adicionar um colchete final ... EXEC (atOPENQUERY + atDAX + '' '' + ')')
TDP

@TDP você poderia, se formatasse o código inline como código inline (usando crases):SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon

15

Você pode executar uma string com OPENQUERY depois de criá-la. Se você seguir esse caminho, pense na segurança e tome cuidado para não concatenar o texto inserido pelo usuário em seu 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)

Infelizmente isso não funciona, se você quiser usar OpenQuery em contexto com if, por exemplo, emif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
Stefan Brendle

@Stefan - você pode selecionar em openquery e inserir os resultados em uma tabela temporária. A partir daí, suas opções se abrem muito, é claro.
Jagd de

13

Na página do MSDN :

OPENQUERY não aceita variáveis ​​para seus argumentos

Fundamentalmente, isso significa que você não pode emitir uma consulta dinâmica. Para alcançar o que sua amostra está tentando, tente o seguinte:

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

Obviamente, se sua tabela TABLENAME contiver uma grande quantidade de dados, isso também irá percorrer a rede e o desempenho poderá ser ruim. Por outro lado, para uma pequena quantidade de dados, isso funciona bem e evita os overheads de construção de sql dinâmico (injeção de sql, citações de escape) que uma execabordagem pode exigir.


isso me indicou o caminho certo para ter sucesso no que eu estava tentando realizar! Obrigado! isso deve ter mais votos positivos
Malaquias,

7

Na verdade, encontramos uma maneira de fazer isso:

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

Isso atribuirá o resultado da execução do OpenQuery, na variável @Output.

Testamos o procedimento Store no MSSQL 2012, mas deve funcionar com o MSSQL 2008+.

A Microsoft afirma que sp_executesql (Transact-SQL): Aplica-se a: SQL Server (SQL Server 2008 até a versão atual), Banco de dados SQL do Windows Azure (versão inicial até a versão atual). ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )


Isso só funciona para saída escalar. Tente Xml ou uma variável de tabela e isso não vai funcionar.
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
Este código precisa de um aviso de que A) field1 para TODAS as linhas de TABLENAME será transmitido do servidor vinculado - uma operação potencialmente muito cara. B) o INNER JOIN também pode ser 'muito caro'
brewmanz

2

Combine SQL dinâmico com OpenQuery. (Isso vai para um servidor 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

No exemplo a seguir, estou passando um parâmetro de departamento para um procedimento armazenado (spIncreaseTotalsRpt) e, ao mesmo tempo, estou criando uma tabela temporária de um OPENQUERY. A tabela Temp precisa ser uma Temp global (##) para que possa ser referenciada fora de sua intância. Usando exec sp_executesql, você pode passar o parâmetro de departamento.

Nota: tenha cuidado ao usar sp_executeSQL. Além disso, seu administrador pode não ter essa opção disponível para você.

Espero que isso ajude alguém.

 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

Resultados

Aumento do departamento 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

Eu descobri uma maneira que funciona para mim. Ele requer o uso de uma tabela temporária à qual um servidor vinculado tenha acesso.

Criei uma tabela e a preencha com os valores de que preciso, em seguida, faço referência a essa tabela por meio de um servidor vinculado.

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]

A palavra-chave é openquery cara :)
Christian

é uma abordagem alternativa. Quem sabe openquerydeve conhecer essa abordagem também. Também é muito mais limpo. Então, +1 do meu lado.
Sheikh Abdul Wahid

0

Podemos usar o executemétodo em vez de openquery. Seu código é muito mais limpo. Tive que obter o linked serverresultado da consulta em uma variável. Eu usei o seguinte código.

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 a sua consulta não funcionar, certifique-se de que remote proc transaction promotionestá configurada falsepara sua linked serverconexão.

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

-1

Exemplo simples baseado no exemplo de @Tuan Zaidi acima, que parecia o mais fácil. Não sabia que você pode fazer o filtro do lado de fora do OPENQUERY ... muito mais fácil!

No entanto, no meu caso, precisei colocá-lo em uma variável, então criei um nível de subconsulta adicional para retornar um único valor.

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.