[英]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 列表。
因此,例如,我想在给定OrderId
的Details
JSON 字符串记录中更新 Ids 2 和 3 为真。 我知道我可以在 while 循环中使用json_modify
来做到这一点,但我想知道是否有更优雅的解决方案,结合json_modify
、 json_query
或openjson
。
在此先感谢您的任何建议。
您可以使用以下方法之一:
OrderId
OPENJSON()
和显式模式解析Details
JSON。 结果是一个包含列的表,在WITH
子句中定义。 更新此表并使用FOR JSON
。OrderId
OPENJSON()
和默认模式解析Details
JSON。 结果是一个表,其中包含key
、 value
和type
列,并且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;
这个怎么运作:
将 JSON 解析为表格格式
执行数据操作(这里使用@t 作为参数)
聚合回 JSON
执行更新
我没有正确版本的 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.