使用JOIN更新多个JSON节点

时间:2020-06-05 06:15:21

标签: sql json sql-server tsql

在SQL Server中,我有一个存储过程,该存储过程采用JSON参数@ChangeSet,如下所示。

DECLARE  @ChangeSet varchar(MAX) = 
'{
    "Acts":
    [
        {"ActId":100,"ActText":"Intro","ActNumber":1},
        {"ActId":0,  "ActText":"Beginning","ActNumber":2},
        {"ActId":0,  "ActText":"Middle","ActNumber":3},
        {"ActId":0,  "ActText":"End","ActNumber":4},
    ]
 }';

在proc中,我有一个MERGE语句,它根据是INSERT(如果ActId0)还是{{1} }。我想用UPDATE的{​​{1}}表返回的多个PK ActId更新JSON @ChangeSet变量,以便可以在INSERTED参数中返回它。 / p>

MERGE

我可以重新查询数据库,将其输出为JSON,但有兴趣找出一种可能的方法,例如使用OUT等直接更新JSON的技术。

我查看了各种样本,但没有发现任何类似的东西。有人有很好的例子吗?

2 个答案:

答案 0 :(得分:1)

如果我对问题的理解正确,那么您有两种选择:

  • 使用Acts修改JSON_MODIFY JSON数组(但您需要SQL Server 2017+才能将变量用作路径表达式)。这种方法在SELECT语句中设置了局部变量,因此您不得在语句中使用ORDER BYDISTINCT
  • 解析输入的JSON,使用基于集合的方法以表格的形式获取预期结果,并使用FOR JSON AUTO
  • 将表格内容输出为JSON

JSON:

DECLARE @ChangeSet varchar(MAX) = 
'{
    "Acts":
    [
        {"ActId":100,"ActText":"Intro","ActNumber":1},
        {"ActId":0,  "ActText":"Beginning","ActNumber":2},
        {"ActId":0,  "ActText":"Middle","ActNumber":3},
        {"ActId":0,  "ActText":"End","ActNumber":4}
    ]
}';

JSON_MODIFY的声明:

SELECT @ChangeSet = JSON_MODIFY(
   @ChangeSet,
   CONCAT('$.Acts[', j1.[key], '].ActId'), 
   v.[Id]
)   
FROM OPENJSON(@ChangeSet, '$.Acts') j1
CROSS APPLY OPENJSON(j1.[value]) WITH (ActNumber int '$.ActNumber') j2
JOIN (VALUES 
   (100, 'Act', 'UPDATE', 'Intro',     1),
   (101, 'Act', 'INSERT', 'Beginning', 2),
   (102, 'Act', 'INSERT', 'Middle',    3),
   (103, 'Act', 'INSERT', 'End',       4)
) v ([Id], [Type], [Action], [Value], [ActNumber]) ON v.[ActNumber] = j2.[ActNumber]

FOR JSON的声明:

SELECT @ChangeSet = (
   SELECT v.[Id] AS ActId, j.ActText, j.ActNumber
   FROM OPENJSON(@ChangeSet, '$.Acts') WITH (
      ActId int '$.ActId',
      ActText varchar(50) '$.ActText',
      ActNumber int '$.ActNumber'
   ) j
   JOIN (VALUES 
      (100, 'Act', 'UPDATE', 'Intro',     1),
      (101, 'Act', 'INSERT', 'Beginning', 2),
      (102, 'Act', 'INSERT', 'Middle',    3),
      (103, 'Act', 'INSERT', 'End',       4)
   ) v ([Id], [Type], [Action], [Value], [ActNumber]) ON v.[ActNumber] = j.[ActNumber]
   FOR JSON AUTO, ROOT ('Acts')
)

结果:

{
    "Acts":
    [
        {"ActId":100, "ActText":"Intro", "ActNumber":1},
        {"ActId":101, "ActText":"Beginning", "ActNumber":2},
        {"ActId":102, "ActText":"Middle", "ActNumber":3},
        {"ActId":103, "ActText":"End", "ActNumber":4}
    ]
}

答案 1 :(得分:0)

为完整起见,下面是完成的例程,该例程采用Merge语句的Output,将其插入到临时表变量中,然后使用新插入的ActId主键更新输入JSON,以便随后可以将其返回。过程OUT变量。

-- SQL 2017+ REQUIRED

DECLARE @ActActions  table( [ActId] int, [Action] varchar(30),
                     [Value] nvarchar(max), [ActNumber] int );

---------------------------------------------------------------------------------

OUTPUT   COALESCE (INSERTED.ActId, DELETED.ActId), $action, 
         COALESCE (INSERTED.ActText, DELETED.ActText),
         COALESCE (INSERTED.ActNumber, DELETED.ActNumber)
INTO    @ActActions;   -- Required semi-colon at end of MERGE

---------------------------------------------------------------------------------

SELECT  @ChangeSetJson = JSON_QUERY(JSON_MODIFY( 
        @ChangeSetJson, '$.Acts[' + j1.[key] + '].ActId', a.[ActId] )  )
FROM    OPENJSON(@ChangeSetJson, '$.Acts') j1 CROSS APPLY 
        OPENJSON(j1.[value])
        WITH (ActNumber int '$.ActNumber') j2 INNER JOIN
        @ActActions a
            ON a.[ActNumber] = j2.[ActNumber]
WHERE  a.[Action] = 'INSERT'