我需要你的帮助。我有一个更新行的命令,如果在变量@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。
如何解决?
答案 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
服务器上的作业更新,然后在更新期间加入该表。