incluyendo parámetros en OPENQUERY

86

¿Cómo puedo usar un parámetro dentro de 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
gaponte69
fuente
Una solución alternativa es crear una vista con openquery y luego usar la vista en la combinación
Ismael

Respuestas:

156

De la documentación de OPENQUERY se establece que:

OPENQUERY no acepta variables para sus argumentos.

Consulte este artículo para obtener una solución.

ACTUALIZAR:

Como se sugirió, incluyo las recomendaciones del artículo a continuación.

Pasar valores básicos

Cuando se conoce la instrucción Transact-SQL básica, pero debe pasar uno o más valores específicos, use un código similar al siguiente ejemplo:

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)

Pasar la consulta completa

Cuando tenga que pasar toda la consulta Transact-SQL o el nombre del servidor vinculado (o ambos), use un código similar al siguiente ejemplo:

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) 

Utilice el procedimiento almacenado Sp_executesql

Para evitar las comillas de varias capas, use un código similar al siguiente ejemplo:

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
fuente
9
Utilizando cualquiera de estos ejemplos, ¿cómo recupera los registros devueltos por el comando exec?
philreed
2
Para recuperar registros, siempre construí una variable de tabla o una tabla temporal de mi conjunto de resultados, luego uséINSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret
6
@JamesChen, es más fácil pensar cuando se trabaja al revés. Comenzar con la consulta del AbrirConsulta: SELECT * FROM tab WHERE col = 'Y'. Para superar esa declaración como una cadena de AbrirConsulta, todas las comillas simples tienen que escaparon: SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' '). A continuación, pase a SELECT usando AbrirConsulta a SQL dinámico, aquellas cotizaciones deben escaparse: EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'. ¡Espero que esto ayude!
Bret
(no puedo usar @ en los comentarios, así que usando 'at') DAX dinámico con parámetros ... Preparé la cadena DAX pero Openquery no funcionó. Pasar el método Whole Query funcionó. Ejecuté mi DAX a través de esto: SET atDAX = REPLACE (atDAX, '' '', '' '' '') y al final tuve que cerrar la cadena y agregar un corchete final ... EXEC (atOPENQUERY + atDAX + '' '' + ')')
TDP
@TDP podría, si formateó el código en línea como código en línea (usando comillas invertidas):SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon
15

Puede ejecutar una cadena con OPENQUERY una vez que la construya. Si sigue esta ruta, piense en la seguridad y tenga cuidado de no concatenar texto introducido por el usuario en su 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)
Tahbaza
fuente
Desafortunadamente, no funciona, si desea utilizar OpenQuery en contexto con if, por ejemplo, enif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
Stefan Brendle
@Stefan: puede seleccionar de openquery e insertar los resultados en una tabla temporal. A partir de ahí, sus opciones se abren mucho, por supuesto.
Jagd
13

Desde la página de MSDN :

OPENQUERY no acepta variables para sus argumentos

Básicamente, esto significa que no puede emitir una consulta dinámica. Para lograr lo que intenta su muestra, intente esto:

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

Claramente, si su tabla TABLENAME contiene una gran cantidad de datos, esto también pasará por la red y el rendimiento podría ser deficiente. Por otro lado, para una pequeña cantidad de datos, esto funciona bien y evita los gastos generales de construcción dinámica de SQL (inyección de SQL, comillas de escape) que un execenfoque podría requerir.

Neil Moss
fuente
¡Esto me indicó el camino correcto para tener éxito en lo que estaba tratando de lograr! ¡Gracias! esto debería tener más votos a favor
Malaquías
7

De hecho, encontramos una manera de hacer esto:

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

Esto asignará el resultado de la ejecución de OpenQuery, en la variable @Output.

Probamos el procedimiento Store en MSSQL 2012, pero debería funcionar con MSSQL 2008+.

Microsoft dice que sp_executesql (Transact-SQL): se aplica a: SQL Server (SQL Server 2008 hasta la versión actual), Windows Azure SQL Database (desde la versión inicial hasta la versión actual). ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )

Juan medina
fuente
Esto solo funciona para la salida escalar. Pruebe Xml o una variable de tabla y esto no 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.
Ryan Maloney
fuente
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
fuente
4
Este código necesita una advertencia de que A) field1 para TODAS las filas de TABLENAME se pasará desde el servidor vinculado, una operación potencialmente muy costosa. B) el INNER JOIN también podría ser 'muy caro'
brewmanz
2

Combine SQL dinámico con OpenQuery. (Esto va a un 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;
Miguel
fuente
1

En el siguiente ejemplo, estoy pasando un parámetro de departamento a un procedimiento almacenado (spIncreaseTotalsRpt) y al mismo tiempo estoy creando una tabla temporal, todo desde un OPENQUERY. La tabla Temp debe ser una Temp global (##) para que pueda ser referenciada fuera de su estado. Al usar exec sp_executesql, puede pasar el parámetro del departamento.

Nota: tenga cuidado al utilizar sp_executeSQL. Además, es posible que su administrador no tenga esta opción disponible para usted.

Espero que esto ayude a alguien.

 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

Incremento de 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

Carlos
fuente
0

Descubrí una forma que funciona para mí. Sin embargo, requiere el uso de una tabla temporal a la que tiene acceso un servidor vinculado .

Creé una tabla y la completé con los valores que necesito, luego hago referencia a esa tabla a través de un 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]) ')
Puño de Hannover
fuente
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
fuente
La palabra clave es openquery dude :)
Christian
es un enfoque alternativo. Quien lo sepa también openquerydebería conocer este enfoque. También es mucho más limpio. Entonces +1 de mi lado.
Sheikh Abdul Wahid
0

Podemos usar el executemétodo en lugar de openquery. Su código es mucho más limpio. Tuve que obtener el linked serverresultado de la consulta en una variable. Usé el siguiente 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:

Si su consulta no funciona, asegúrese de que remote proc transaction promotionesté configurado como falsepara su linked serverconexión.

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

Ejemplo simple basado en el ejemplo anterior de @Tuan Zaidi, que parecía el más fácil. No sabía que podía hacer el filtro en el exterior de OPENQUERY ... ¡mucho más fácil!

Sin embargo, en mi caso, necesitaba incluirlo en una variable, así que creé un nivel de subconsulta adicional para devolver un solo 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)
Anthony Griggs
fuente