【问题标题】:including parameters in OPENQUERY包括 OPENQUERY 中的参数
【发布时间】:2010-07-31 14:26:11
【问题描述】:

如何在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 创建一个视图,然后在连接中使用该视图

标签: sql parameters openquery


【解决方案1】:

OPENQUERY 文档指出:

OPENQUERY 不接受变量 论据。

请参阅此article 了解解决方法。

更新:

按照建议,我将包含以下文章中的建议。

通过基本价值观

当基本 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

【讨论】:

  • 使用这些示例中的任何一个,您如何检索从 exec 命令返回的记录?
  • 为了检索记录,我总是为我的结果集构建一个表变量或临时表,然后使用INSERT INTO @TableVariable EXEC sp_executeSql @TSQL
  • @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'''' '')'。希望这会有所帮助!
  • (不能在 cmets 中使用 @,因此使用 'at')带参数的动态 DAX...我准备了 DAX 字符串,但 Openquery 不起作用。通过 Whole Query 方法工作。通过这个运行我的 DAX: SET atDAX = REPLACE(atDAX, '''', '''''') 最后我不得不关闭字符串并添加最后一个括号... EXEC (atOPENQUERY + atDAX + '''' + ')')
  • @TDP 你可以,如果你将内联代码格式化为内联代码(使用反引号):SET @DAX = REPLACE(@DAX, '''', '''''')
【解决方案2】:

来自MSDN page

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 注入、转义引号)。

【讨论】:

  • 这为我指明了成功的正确道路!谢谢!这应该有更多的支持
【解决方案3】:

我们可以使用execute 方法代替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

注意:

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

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

【讨论】:

    【解决方案4】:

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

    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 变量,但这是行不通的。
    【解决方案5】:
    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 老兄:)
    • 这是一种替代方法。谁知道openquery 也应该知道这种方法。它也更清洁。所以从我这边+1。
    【解决方案6】:

    一旦你建立了一个字符串,你就可以使用 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)
    

    【讨论】:

    • 很遗憾,如果您想在 if 的上下文中使用 OpenQuery,例如在 if (SELECT Col1 FROM OPENQUERY('Select ...') &gt; 0 ) BEGIN ... END 中,它不起作用
    • @Stefan - 您可以从 openquery 中选择并将结果插入到临时表中。当然,从那里你可以有很多选择。
    【解决方案7】:

    将动态 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;
    

    【讨论】:

      【解决方案8】:

      试试这个方法,应该可以,很简单!在您的 WHERE 子句中,在列名和等号之后:- 添加两个单引号、您的搜索值,然后添加三个单引号。关闭括号。

      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

      【讨论】:

      • 您的答案使用文字 SQL,但 OP 想要使用参数(注意他/她对 field1=@someParameter 的引用)。
      【解决方案9】:
      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
      

      【讨论】:

      • 此代码需要警告 A) TABLENAME 的 ALL 行的 field1 将从链接服务器传递 - 这是一个可能非常昂贵的操作。 B)INNER JOIN 也可能“非常昂贵”
      【解决方案10】:

      基于@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)
      

      【讨论】:

        【解决方案11】:
        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.
        

        【讨论】:

          【解决方案12】:

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

          注意:使用 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

          【讨论】:

            【解决方案13】:

            我想出了一个适合我的方法。它确实需要使用 链接服务器 可以访问的暂存表。

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

            SELECT * 
            FROM OPENQUERY(KHSSQLODSPRD,'SELECT *
              FROM ABC.dbo.CLAIM A WITH (NOLOCK)
              WHERE A.DOS >= (SELECT MAX(DATE) FROM KHSDASQL01.DA_MAIN.[dbo].[ALLFILENAMES]) ')
            

            【讨论】:

              【解决方案14】:
              DECLARE @usernames NVARCHAR(MAX);
              SET @usernames = N'';
              DECLARE @len INT;
              SELECT @usernames = @usernames + ISNULL(username + ''''',''''', '')
              FROM #tempusername;
              SET @len = len(@usernames);
              SET @usernames = N'''''' + LEFT(@usernames, @len - 3);
              DECLARE @sql NVARCHAR(MAX);
              SET @sql
              = N'SELECT *
              FROM OPENQUERY
                   ([Linked Server],
                    ''SELECT username  
                     FROM [MySQL Database].[Table Name]
                     WHERE username IN ( ' + @usernames + N')'')';
              

              我在这个链接https://social.technet.microsoft.com/Forums/en-US/98e0c17a-8ead-4633-a053-51c49b313bd3/how-can-i-use-a-table-variable-as-a-parameter-for-openquery?forum=sqltools&prof=required找到了这个解决方法 这很有帮助。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-03-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-01-02
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多