[英]Modifying multiple JSON array elements in SQL Server 2017
I have a SQL Server 2017 table Orders
which has an OrderId
primary key and nvarchar(max)
column Details
.我有一个 SQL Server 2017 表
Orders
,它有一个OrderId
主键和nvarchar(max)
列Details
。 This column contains a json string which represents an array of "items".此列包含一个 json 字符串,它表示“项目”数组。 Here is a sample:
这是一个示例:
{ items[
{
"id": 1,
"isDeleted": false
},
{
"id": 2,
"isDeleted": false
},
{
"id": 3,
"isDeleted": false
},
{
"id": 4,
"isDeleted": false
}
] }
I am trying to figure out if there is a way to have a single (or few) SQL statement which will allow me to update one or more of the isDeleted
attributes in the Details
column of this table, given an OrderId
for the record in the table and also a list of Ids in the Details
column to update.我试图弄清楚是否有办法拥有一个(或几个)SQL 语句,这将允许我更新此表的
Details
列中的一个或多个isDeleted
属性,给定记录中的OrderId
表以及要更新的Details
列中的 Id 列表。
So for instance, I would like to update Ids 2 and 3 to be true in the Details
JSON string record for a given OrderId
.因此,例如,我想在给定
OrderId
的Details
JSON 字符串记录中更新 Ids 2 和 3 为真。 I know I can do this in a while loop and using json_modify
, but I am wondering if there is a more elegant solution with some combination of json_modify
, json_query
or openjson
.我知道我可以在 while 循环中使用
json_modify
来做到这一点,但我想知道是否有更优雅的解决方案,结合json_modify
、 json_query
或openjson
。
Thanks in advance for any suggestions.在此先感谢您的任何建议。
You may use one of the following approaches:您可以使用以下方法之一:
Details
JSON for each OrderId
uisng OPENJSON()
and explicit schema.OrderId
OPENJSON()
和显式模式解析Details
JSON。 The result is a table with columns, defined in the WITH
clause.WITH
子句中定义。 Update this table and return the changed data as JSON again using FOR JSON
.FOR JSON
。Details
JSON for each OrderId
uisng OPENJSON()
and default schema.OrderId
OPENJSON()
和默认模式解析Details
JSON。 The result is a table with columns key
, value
and type
and one row for each item (JSON object) in the items
JSON array.key
、 value
和type
列,并且items
JSON 数组中的每个项目(JSON 对象)都有一行。 Update this table and generate the items
JSON array with string-based approach (I don't think that FOR JSON
can generate an array of scalar values / JSON objects).items
JSON 数组(我不认为FOR JSON
可以生成标量值/ JSON 对象数组) Update the JSON in the source table with JSON_MODIFY()
.JSON_MODIFY()
更新源表中的 JSON 。JSON_MODIFY()
JSON_MODIFY()
生成并执行动态语句Table with data:数据表:
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}]}')
Table with IDs:带有 ID 的表:
CREATE TABLE ItemIds (id int)
INSERT INTO ItemIds (id) VALUES (1), (3)
Statement with OPENJSON()
and explicit schema:带有
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
Statement with OPENJSON()
and default schema:带有
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
Dynamic statement:动态声明:
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
Result:结果:
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 Server is perfectly capable of performing such operation. SQL 服务器完全能够执行这样的操作。 It is another question if this is good design though.
如果这是一个好的设计,这是另一个问题。
This is just a demo and not production ready code, so there is a lot of space for improvement:这只是一个演示而不是生产就绪的代码,因此还有很大的改进空间:
-- 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;
How it works:这个怎么运作:
Parse JSON to tabular format将 JSON 解析为表格格式
Perform data manipulation(here using @t as parameter)执行数据操作(这里使用@t 作为参数)
Aggregate back to JSON聚合回 JSON
Perform UPDATE执行更新
I don't have the right version of SQL Server to test out this code.我没有正确版本的 SQL 服务器来测试此代码。 But, you should be able to query and modify the data and generate a new json string.
但是,您应该能够查询和修改数据并生成新的 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.