包括OPENQUERY中的参数


86

如何在sql openquery中使用参数,例如:

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

解决方法是使用openquery创建一个视图,然后在联接中使用该视图
Ismael

Answers:


156

OPENQUERY文档中,它指出:

OPENQUERY不接受变量作为其参数。

请参阅本文以了解解决方法。

更新:

如所建议的,我将包括以下文章的建议。

传递基本价值观

当基本的Transact-SQL语句已知但您必须传递一个或多个特定值时,请使用类似于以下示例的代码:

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)

传递整个查询

当您必须传递整个Transact-SQL查询或链接服务器的名称(或两者都传递)时,请使用类似于以下示例的代码:

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) 

使用Sp_executesql存储过程

为避免使用多层引号,请使用类似于以下示例的代码:

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
使用这些示例中的任何一个,如何检索exec命令返回的记录?
2015年

2
为了检索记录,我总是在结果集中建立一个表变量或临时表,然后使用INSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret

6
@JamesChen,倒退工作最容易想到。从OpenQuery的查询开始:SELECT * FROM tab WHERE col = 'Y'。要将该语句作为字符串传递给OpenQuery,所有单引号都必须转义:SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' ')。然后,要将使用OpenQuery的SELECT传递给Dynamic SQL,必须对这些引号进行转义:EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'。希望这可以帮助!
布雷特(Bret)

(不能在注释中使用@,因此请使用'at'。)带参数的动态DAX ...我准备了DAX字符串,但Openquery无法正常工作。通过整个查询方法工作。通过以下命令运行我的DAX:SET atDAX = REPLACE(atDAX,``'',``''''),最后我不得不关闭字符串并添加最后一个括号... EXEC(atOPENQUERY + atDAX + ''+')')
TDP

@TDP,如果将内联代码格式化为内联代码(使用反引号),则可以:SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon

15

建立字符串后,即可使用OPENQUERY执行字符串。如果您走这条路,请考虑安全性,并注意不要将用户输入的文本连接到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)

遗憾的是它不工作,如果你想使用OPENQUERY在上下文if中,例如if (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
斯特凡Brendle

@Stefan-您可以从openquery中选择并将结果插入到临时表中。当然,从那里您可以进行很多选择。
Jagd 2014年

13

MSDN页面

OPENQUERY不接受变量作为参数

从根本上讲,这意味着您无法发出动态查询。要实现您的样本正在尝试的操作,请尝试以下操作:

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

显然,如果您的TABLENAME表包含大量数据,那么这也将遍及网络,并且性能可能会很差。另一方面,对于少量数据,此方法效果很好,并且避免了exec方法可能需要的动态sql构造开销(sql注入,转义引号)。


这为我指明了成功实现我所要完成的正确道路!谢谢!这应该有更多的投票
玛拉基书

7

实际上,我们找到了一种方法:

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

这将在变量@Output中分配OpenQuery执行的结果。

我们在MSSQL 2012中测试了存储过程,但应与MSSQL 2008+一起使用。

Microsoft说sp_executesql(Transact-SQL):适用于:SQL Server(通过当前版本的SQL Server 2008),Windows Azure SQL数据库(通过当前版本的初始版本)。(http://msdn.microsoft.com/en-us/library/ms188001.aspx


这仅适用于标量输出。尝试使用Xml或Table变量,这将无法正常工作。
user5855178'4

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
这段代码需要警告,A)TABLENAME的所有行的field1将从链接服务器传递过来,这可能是非常昂贵的操作。B)内部联接也可能“非常昂贵”
brewmanz

2

将动态SQL与OpenQuery结合使用。(这将转到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

在下面的示例中,我将部门参数传递给存储过程(spIncreaseTotalsRpt),同时,我从OPENQUERY创建一个临时表。Temp表必须是全局Temp(##),以便可以在其实例之外引用。通过使用exec sp_executesql,可以传递department参数。

注意:使用sp_executeSQL时要小心。另外,您的管理员可能没有此选项。

希望这对某人有帮助。

 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

结果

部门增加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

我想出了一种对我有用的方法。它确实需要使用链接服务器可以访问的临时表。

我创建了一个表,并用所需的值填充该表,然后通过链接服务器引用该表。

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]

关键字是openquery dude :)
Christian

这是另一种方法。谁知道也openquery应该知道这种方法。它也更清洁。因此,我这边+1。
谢赫·阿卜杜勒·瓦希德

0

我们可以使用executemethod代替openquery。它的代码更简洁。我必须获取linked server变量中的查询结果。我用下面的代码。

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

注意:

如果您的查询不起作用,请确保remote proc transaction promotionfalse为您的linked server连接设置。

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

-1

基于@Tuan Zaidi上面的示例的简单示例,这似乎是最简单的。不知道您可以在OPENQUERY的外部进行过滤...如此简单!

但是,就我而言,我需要将其填充到变量中,因此我创建了一个附加的子查询级别以返回单个值。

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.