繁体   English   中英

修改 SQL Server 2017 中的多个 JSON 数组元素

[英]Modifying multiple JSON array elements in SQL Server 2017

我有一个 SQL Server 2017 表Orders ,它有一个OrderId主键和nvarchar(max)Details 此列包含一个 json 字符串,它表示“项目”数组。 这是一个示例:

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

我试图弄清楚是否有办法拥有一个(或几个)SQL 语句,这将允许我更新此表的Details列中的一个或多个isDeleted属性,给定记录中的OrderId表以及要更新的Details列中的 Id 列表。

因此,例如,我想在给定OrderIdDetails JSON 字符串记录中更新 Ids 2 和 3 为真。 我知道我可以在 while 循环中使用json_modify来做到这一点,但我想知道是否有更优雅的解决方案,结合json_modifyjson_queryopenjson

在此先感谢您的任何建议。

您可以使用以下方法之一:

  • 为每个OrderId OPENJSON()和显式模式解析Details JSON。 结果是一个包含列的表,在WITH子句中定义。 更新此表并使用FOR JSON
  • 为每个OrderId OPENJSON()和默认模式解析Details JSON。 结果是一个表,其中包含keyvaluetype列,并且items JSON 数组中的每个项目(JSON 对象)都有一行。 更新此表并使用基于字符串的方法生成items JSON 数组(我不认为FOR JSON可以生成标量值/ JSON 对象数组) 使用JSON_MODIFY()更新源表中的 JSON 。
  • 使用JSON_MODIFY()生成并执行动态语句

数据表:

CREATE TABLE Orders (OrderId int, Details nvarchar(max))
INSERT INTO Orders (OrderId, Details)
VALUES 
   (1, N'{"items":[{"id":1,"isDeleted":false},{"id":2,"isDeleted":false},{"id":3,"isDeleted":false},{"id":4,"isDeleted":false}]}'),
   (2, N'{"items":[{"id":11,"isDeleted":false},{"id":12,"isDeleted":false},{"id":13,"isDeleted":false}]}')

带有 ID 的表:

CREATE TABLE ItemIds (id int)
INSERT INTO ItemIds (id) VALUES (1), (3)

带有OPENJSON()和显式模式的语句:

UPDATE Orders
SET Details = (
   SELECT 
      j.id AS id, 
      CONVERT(bit, CASE WHEN i.id IS NOT NULL THEN 1 ELSE j.isDeleted END) AS isDeleted
   FROM OPENJSON(Details, '$.items') WITH (
      id int '$.id', 
      isDeleted bit '$.isDeleted'
   ) j
   LEFT OUTER JOIN ItemIds i ON j.id = i.id
   FOR JSON AUTO, ROOT('Items')
)
WHERE OrderId = 1

带有OPENJSON()和默认模式的语句:

UPDATE Orders
SET Details = JSON_MODIFY(
   Details,
   '$.items',
   JSON_QUERY((
      SELECT CONCAT(
         '[', 
         STRING_AGG(
            CASE 
               WHEN i.id IS NULL THEN j.[value] 
               ELSE JSON_MODIFY(j.[value], '$.isDeleted', CONVERT(bit, 1)) 
            END,
            ','
         ),
         ']'
      )   
      FROM OPENJSON(Details, '$.items') j
      LEFT OUTER JOIN ItemIds i ON CONVERT(int, JSON_VALUE(j.[value], '$.id')) = i.id
   ))
)
WHERE OrderId = 1

动态声明:

DECLARE @stm nvarchar(max) 
SELECT @stm = STRING_AGG(
    CONCAT(
      'UPDATE Orders ',
      'SET Details = JSON_MODIFY(Details, ''$.items[', a.[key], '].isDeleted'', CONVERT(bit, 1)) ',
      'WHERE OrderId = ', o.OrderId, ';'
   ),
   ' '
)   
FROM Orders o
CROSS APPLY (
   SELECT o.OrderId, j1.[key]
   FROM OPENJSON(o.Details, '$.items') j1
   CROSS APPLY OPENJSON(j1.[value]) WITH (id int '$.id') j2
   WHERE j2.id IN (SELECT id FROM ItemIds)
) a   
WHERE o.OrderId = 1

PRINT @stm 
EXEC sp_executesql @stm

结果:

OrderId Details
1   {"items":[{"id":1,"isDeleted":true},{"id":2,"isDeleted":false},{"id":3,"isDeleted":true},{"id":4,"isDeleted":false}]}
2   {"items":[{"id":11,"isDeleted":false},{"id":12,"isDeleted":false},{"id":13,"isDeleted":false}]}

SQL 服务器完全能够执行这样的操作。 如果这是一个好的设计,这是另一个问题。


这只是一个演示而不是生产就绪的代码,因此还有很大的改进空间:

-- param section
DECLARE @OrderId INT = 1;

DECLARE @t TABLE(id INT, new_val NVARCHAR(10));
INSERT INTO @t(id, new_val) VALUES(1, 'true'),(3, 'true');



--- single query
WITH cte AS (
 SELECT o.*, 
  s.[key], 
  JSON_VALUE(s.value, '$.id') AS id,
  JSON_VALUE(s.value, '$.isDeleted') AS isDeleted
 FROM Orders o
 CROSS APPLY OPENJSON(o.Details ,N'$.items') s
 WHERE o.OrderId = @OrderId
), cte_new AS (
SELECT DISTINCT c.OrderId, c.Details, s.Details_new
FROM cte c
CROSS APPLY (
  SELECT  c2.id, isDeleted = COALESCE(t.new_val, c2.IsDeleted)
  FROM cte c2
  LEFT JOIN @t t
    ON c2.id = t.id
  WHERE c2.OrderId = c.OrderId
  FOR JSON AUTO) s(Details_new)
)
UPDATE o
SET Details = cn.Details_new
FROM Orders o
JOIN cte_new cn
  ON o.OrderId = cn.OrderId;

db<>小提琴演示

这个怎么运作:

  1. 将 JSON 解析为表格格式

  2. 执行数据操作(这里使用@t 作为参数)

  3. 聚合回 JSON

  4. 执行更新

我没有正确版本的 SQL 服务器来测试此代码。 但是,您应该能够查询和修改数据并生成新的 json 字符串。

DECLARE @json nvarchar(max) = '{"items" : [{"id": 1, "isDeleted": false}, {"id": 2, "isDeleted": false}, {"id": 3, "isDeleted": false}, {"id": 4, "isDeleted": false}]}'

SELECT *
  FROM OPENJSON(@json)
  WITH (id int '$.items.id', isDeleted bit '$.items.isDeleted')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM