Sql-Server
json_modify 條件
與我之前的問題一樣,我正在將基於 XML 的程序遷移到 JSON 程序。我最新的鬥爭是 json_modify
我習慣於使用 .modify 和 XPATH 來動態修改 XML。
例如,如果我有一個如下所示的 XML(範例取自 SqlShack)
<users> <user user_id="1"> <fname>Josef</fname> <lname>Brown</lname> <email_>jo0@adventure-works.com</email_> <gender>M</gender> </user> <user user_id="2"> <fname>Katie</fname> <lname>McAskill-White</lname> <email_>katie0@adventure-works.com</email_> </user> <user user_id="2"> <fname>Maddie</fname> <lname>Brown</lname> <email_>madhatter@adventure-works.com</email_> </user> </users>
我想將所有 user_id=“2” 的使用者的名稱更改為 “Nat”,我可以編寫以下內容:
xml.modify('replace value of (/users/user[@user_id=("2")]/fname/text()) with "Nat"')
現在讓我們談談 JSON。假設我有以下 JSON
{"Users": [{"user_id":1,"fname":"Josef","lname":"Brown","email":"jo0@adventure-works.com","gender":"M"}, {"user_id":2,"fname":"Katie","lname":"McAskill-White","email":"katie0@adventure-works.com"}, {"user_id":2,"fname":"Maddie","lname":"Brown","email":"madhatter@adventure-works.com"}] }
我想做和上面一樣的事情,但是使用這個 JSON。我如何使用 json_modify 來做到這一點?
.modify
首先,無論如何,您實際上不能同時修改多個節點。您通常會在循環或遞歸 CTE 中執行此操作,或者通過重新建構 XML 來執行此操作。使用 JSON 執行此操作要復雜得多,因為 SQL Server 中的 JPath 不支持任何形式的謂詞。對於需要謂詞的每個級別,您都需要將其分開。
假設您只想在第一項上執行此操作。您可以使用
JSON_MODIFY
,但您需要動態建構 JPath,它僅適用於 SQL Server 2017+。UPDATE t SET json = JSON_MODIFY(t.json, '$.Users[' + j.[key] + '].fname', 'Nat') FROM t CROSS APPLY ( SELECT TOP 1 j.[key] FROM OPENJSON(t.json, '$.Users') j WHERE JSON_VALUE(j.value, '$.user_id') = '2' ) j;
或者對於你可以做的變數
SET @json = JSON_MODIFY(@json, '$.Users[' + ( SELECT TOP 1 j.[key] FROM OPENJSON(@json, '$.Users') j WHERE JSON_VALUE(j.value, '$.user_id') = '2' ) + '].fname', 'Nat');
如您所見,它變得複雜且僅適用於一個值,並且僅適用於 SQL Server 2017+。
一個更好的選擇,特別是如果你有多個值,是重建 JSON。
您可以全力以赴並重建每個對象:
UPDATE t SET json = j.json FROM t CROSS APPLY ( SELECT user_id, fname = CASE WHEN user_id = 2 THEN 'Nat' ELSE fname END, lname, email, gender FROM OPENJSON(t.json, '$.Users') WITH ( user_id int, fname varchar(50), lname varchar(50), email varchar(255), gender char(1) ) j FOR JSON PATH, ROOT('Users') ) j(json);
但是在很多情況下,最簡單的方法是只重建到您需要更改的級別之上,然後使用
JSON_MODIFY
.在這種特殊情況下,這意味著您只需要在沒有模式的情況下進行數組拆分
OPENJSON
,然後有條件JSON_MODIFY
地處理對象。SQL Server 沒有JSON_AGG
,所以我們需要用 hack 來破解它STRING_AGG
:UPDATE t SET json = j.json FROM t CROSS APPLY ( SELECT Users = JSON_QUERY('[' + STRING_AGG(CAST( CASE WHEN JSON_VALUE(j.value, '$.user_id') = '2' THEN JSON_MODIFY(j.value, '$.fname', 'Nat') ELSE j.value END AS nvarchar(max)), ',') + ']') FROM OPENJSON(t.json, '$.Users') j FOR JSON PATH ) j(json);