SQL Server:设置一个具有多个可能值的变量

时间:2018-04-06 02:32:18

标签: sql sql-server

我需要你的帮助。我有一个更新行的命令,如果在变量@ID中包含一个值,这是有效的。

DECLARE @ID nvarchar(100)
SET @ID = (select top 1 id from [SERVICES])

DECLARE @UPDATE nvarchar(max)
SET @UPDATE ='UPDATE SERVICES

SET SERVICES.options = t1.options
FROM SERVICES t
JOIN (SELECT * 
      FROM OPENQUERY([ORI], ''SELECT ID, options
                              FROM log       
                              WHERE ID = ''''' + @ID + ''''' '')) t1 ON t1.id = t.id'

EXEC (@UPDATE)

但我需要更新超过1行。

如果我指定这样的条件:

SET @ID = (SELECT id FROM [ReportSM].[dbo].[SERVICES])

我收到错误:

  

子查询返回的值超过1。

如何解决?

2 个答案:

答案 0 :(得分:1)

选项1

在您当前的设置中,您可以将@ID作为CSV传递给OPENQUERY,以便WHERE IN可以正常工作。

WHERE ID = ' + @ID + '  

然后可以替换为

WHERE ID IN (' + @IDs + ')'

点击此处将您的ID列转换为CSV:SQL Server convert select a column and convert it to a string

请注意IN子句长度的限制。见https://dba.stackexchange.com/questions/14161/what-is-the-maximum-number-of-parameters-i-can-pass-using-the-sql-in-clause-in-s

选项2

由于直接将数据连接到查询中有SQL注入问题,您还可以查看一种更结构化的方法,即使用FOR XML将ID转换为xml片段并将其传递给OPENQUERY,并在其中使用OPENXML读取ID

如果您的服务器都是SQL Server 2016或更高版本,您也可以使用JSON作为传输ID而不是XML的格式。您将使用FOR JSON创建包含ID的JSON数组,并在目标SQL Server上使用OPENJSON将JSON转换回您可以加入的行集。

declare @json varchar(max) = (select id from [ReportSM].[dbo].[SERVICES] FOR JSON PATH)

这将生成一个像这样的字符串

[{"id":1},{"id":2},{"id":3},{"id":4}]

您可以将其添加到您正在准备的查询中的变量中,并使用下面的

进行阅读
SELECT ID
FROM OPENJSON (@json, '$')
WITH (ID IN '$.id')  

将您的查询放在一起将如下所示:

declare @json varchar(max) = (select id from [ReportSM].[dbo].[SERVICES] FOR JSON PATH)

DECLARE @UPDATE nvarchar(max)
SET @UPDATE ='UPDATE SERVICES
    SET SERVICES.options = t1.options
    FROM SERVICES t
    JOIN (SELECT * 
        FROM OPENQUERY([ORI], ''DECLARE @json nvarchar(max) = ''''' + @json + '''''
                                SELECT ID, options
                                FROM log       
                                WHERE ID IN (SELECT ID FROM OPENJSON (@json, ''$'') WITH (ID IN ''$.id'')))) t1 ON t1.id = t.id'

EXEC (@UPDATE)

答案 1 :(得分:1)

听起来你真的想将一个表值参数传递给open查询,但是不支持。您可以删除该过滤器并让连接处理更新准确性,但这将导致可能比必要的更昂贵的远程查询。该解决方案看起来像这样:

UPDATE
    t
SET
    t.options = t1.options
FROM
    Services t
    JOIN (SELECT ID, options FROM OPENQUERY([ORI], 'SELECT ID, options FROM 
log')) t1 ON t1.id = t.id

但是,如果您可以控制ORI链接服务器,则可以将链接服务器设置回ReportSM服务器。这样您就可以在ORI服务器上创建一个包含[ReportSM].[dbo].[SERVICES]表中所有ID的视图,这是您尝试过滤log表的内容。这意味着您可以在ORI端执行ID过滤,然后在ReportSM端运行更简单的更新。在ORI方面有这样的事情:

CREATE VIEW vReportServiceIDs
AS
SELECT
    ID
FROM
    [ReportSM].[dbo].[SERVICES]

CREATE VIEW vReportServiceLogs
AS
SELECT
    reportService.ID,
    oriLog.options
FROM
    vReportServiceIDs reportService
    JOIN [log] oriLog ON reportService.ID = [log].ID

然后在ReportSM方面:

UPDATE
    t
SET
    t.options = t1.options
FROM
    SERVICES t
    JOIN (
        SELECT
            ID, options
        FROM 
            OPENQUERY([ORI], 'SELECT ID, options FROM vReportServiceLogs')

如果您没有ORI服务器的那种访问权限,logs表中有太多数据供您查询,并且在联接期间排除了您不需要的内容,您可能要考虑创建一个logs表的缓存,您可以从ReportSM服务器上的作业更新,然后在更新期间加入该表。