基本操作 礙於我用 2016 所以只有四個函數要記下ISJSON => 拿來驗證是否為正確的格式用 , 如果 JSON 錯誤格式會噴這條 Msg 13609, Level 16, State 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --列出壞掉與沒壞掉的 json select 	--所有資料 	(select count(*) 	from History) as AllData , 	--找 json 正常的 	(select count(*) 	from History 	WHERE ISJSON(specjson) > 0) as NormalJson , 	--壞掉的 json 	(select count(*) 	from History 	WHERE ISJSON(specjson) = 0) as BadJson 
 
JSON_VALUE 這個函數通常拿來展開單一 property 裡面的值而 JSON_QUERY 則是撈出整個 json , 總之大原則遇到巢狀就用 JSON_QUERY 看他這個官網範例 最後一句如果用 JSON_VALUE 來撈 , 會回傳 NULL 很容易搞混
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 DECLARE @jsonInfo NVARCHAR(MAX) DECLARE @town NVARCHAR(32) SET @jsonInfo= N'{ "id": 123, "info":{ 		"address":[{"town":"Paris"},{"town":"London"}] 	} }'; SET @town=JSON_VALUE(@jsonInfo,'$.info.address[0].town'); -- Paris SET @town=JSON_VALUE(@jsonInfo,'$.info.address[1].town'); -- London SELECT @jsonInfo SELECT JSON_QUERY(@jsonInfo , '$.info') -- {"address":[{"town":"Paris"},{"town":"London"}]} SELECT JSON_VALUE(@jsonInfo , '$.info') -- NULL SELECT JSON_QUERY(@jsonInfo , '$.info.XX') -- NULL 
 
最後看 JSON_MODIFY 假設要新增一個在 json 物件裡面不存在的 property 及數值可以用這樣 更新目前 id 內的數值為 1234
1 2 --{  "id": "1234",  "info":{    "address":[{"town":"Paris"},{"town":"London"}]   }  } SELECT JSON_MODIFY(@jsonInfo , '$.id' , '1234') 
 
在目前 json 物件新增一個 test 的 property 數值設定為 123
1 2 --{  "id": 123,  "info":{    "address":[{"town":"Paris"},{"town":"London"}]   ,"test":"123"}  } SELECT JSON_MODIFY(@jsonInfo , '$.info.test' , '123') 
 
搭配 JSON_VALUE 把 json 物件裡面原有的 id 新增到 test 這個 property 裡面
1 2 --{  "id": 123,  "info":{    "address":[{"town":"Paris"},{"town":"London"}]   ,"test":"123"}  } SELECT JSON_MODIFY(@jsonInfo , '$.info.test' , JSON_VALUE(@jsonInfo , '$.id')) 
 
搭配 JSON_QUERY 在目前的 json 新增一個 test 的 property , 並且插入一個物件 {"id" : 123}
1 2 --{  "id": 123,  "info":{    "address":[{"town":"Paris"},{"town":"London"}]   ,"test":{"id":123}}  } SELECT JSON_MODIFY(@jsonInfo , '$.info.test' , JSON_QUERY('{"id":123}')) 
 
搭配 JSON_QUERY 新增一個 test 的 property , 並且用 JSON_VALUE 去撈 id 並且新增
1 2 --{  "id": 123,  "info":{    "address":[{"town":"Paris"},{"town":"London"}]   ,"test":{"id":123}}  } SELECT JSON_MODIFY(@jsonInfo , '$.info.test' , JSON_QUERY('{"id":' + JSON_VALUE(@jsonInfo , '$.id') + '}')) 
 
其他案例 假定有張資料表以 XY 紀錄各種訊息存放物品 , 物品上面有眾多屬性如 IsOepn , IsFail 等等 而 XYBackup 以 'X,Y' 這樣的方式作為 key 並且去存 FullInfo 的 json 資料
1 2 3 4 5 6 7 8 9 10 11 create  table  XY(	X int  , 	Y int  , 	IsOpen bit  , 	IsFail bit  ) create  table  XYBackup(	XY varchar (200 ) , 	FullInfo varchar (max ) ) 
 
這時候新增一些資料進去 , 並且用 FOR JSON PATH 查看看 json 會長怎樣 , 注意預設的 json 會是 array , 所以要加上 WITHOUT_ARRAY_WRAPPER 參數
1 2 3 4 5 6 7 8 9 10 11 insert  into  XY values ( '0'  , '5'  , 1  , 0 )insert  into  XY values ( '2'  , '3'  , 1  , 0 )insert  into  XY values ( '7'  , '9'  , 1  , 0 )SELECT  * , (	SELECT  * 	FROM  XY I 	WHERE  I.X = O.X AND  I.Y = O.Y 	FOR  JSON  PATH  , WITHOUT_ARRAY_WRAPPER ) AS  FullInfo FROM  XY O
 
接著插入 json 到 XYBackup
1 2 3 4 5 6 insert  into  XYBackup values  ('0,5'  , '{"X":"0","Y":"5","IsOpen":true,"IsFail":false}' )insert  into  XYBackup values  ('0,5'  , '{"X":"0","Y":"5","IsOpen":true,"IsFail":true}' )insert  into  XYBackup values  ('2,3'  , '{"X":"2","Y":"3","IsOpen":true,"IsFail":false}' )insert  into  XYBackup values  ('7,9'  , '{"X":"7","Y":"9","IsOpen":true,"IsFail":false}' )insert  into  XYBackup values  ('9,9'  , '{"X":"7","Y":"9","IsOpen":true,"IsFail":false}' )insert  into  XYBackup values  ('19,29'  , '{"X":"7","Y":"9","IsOpen":true,"IsFail":false}' )
 
接著增加屬性更新看看 , 由於 XYBackup 也要一同更新 , 這時問題就浮現上來了 , 舊有資料欄位為 null , 新的則要給預設數值 礙於 sql server 的 bit 我永遠分不清楚正確規則 , 這邊就參考官網說明  先把簡單的欄位增加然後更新
1 2 3 4 5 6 alter  table  XY add  IsLocked bit  null update  XYset  IsLocked = 1 
 
接著撈資料測試看看 , 首先需要用 JSON_MODIFY 進行更新 , 礙於是用 bit 直接塞進去會出問題  , 所以需要用 CAST(1 AS BIT) 進行轉換JSON_MODIFY 語法跟 linux 上面 cli tool jq  很類似只不過是改用 $ 當作 root 反而比 jq 更 jq ~ ps: 如果你是巢狀物件也是一路接 . 下去就好了像這樣 $.Members.IsGreen
然後直接塞 null 也會有問題 , 所以要參考這篇 算是一個奇怪技法
1 2 3 4 5 6 7 8 9 10 11 12 select  top 1  XY , FullInfo ,	JSON_MODIFY(FullInfo , '$.IsLocked'  , CAST (1  AS  BIT )) AS  LastFullInfo , 	JSON_MODIFY( 		JSON_MODIFY(FullInfo , '$.IsLocked'  , '' ), 		'strict $.IsLocked'  , null  	) AS  AddNullValue from  XYBackupwhere  XY in ( 	select  cast (x as  varchar ) + ','  + cast (y as  varchar ) 	from  XY ) 
 
最後實際更新看看 , 這裡懶得分兩次更新所以直接用 case when 語法即可
1 2 3 4 5 6 7 8 9 10 11 update  XYBackupset  FullInfo = (	case  when  xy in  ( 		select  cast (x as  varchar ) + ','  + cast (y as  varchar ) 		from  XY 	) then  JSON_MODIFY(FullInfo , '$.IsLocked'  , CAST (1  AS  BIT )) 	else  JSON_MODIFY( 		JSON_MODIFY(FullInfo , '$.IsLocked'  , '' ), 		'strict $.IsLocked'  , null  	) end  ) 
 
最後想要直接讓 json 變回 row 可以參考這篇 
1 2 3 4 5 6 7 8 9 10 11 12 13 select  *from  OPENJSON(	( 		select  top 1  FullInfo 		from  XYBackup 	) ) with  ( 	X int  , 	Y int  , 	IsOpen bit  , 	IsFail bit  , 	IsLocked bit  ) 
 
可是實務上都會希望讓所有結果集變回來不太可能只用 top 1 , 所以特別研究下 , 需要搭配 XML PATH 及 STUFF 將 json rows append 為一個 array
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 select  *from  OPENJSON(	( 		SELECT   		'['  + 			STUFF ((SELECT  				','  + FullInfo 			FROM  XYBackup 			FOR  XML  PATH  ('' )) 			, 1 , 1 , '' ) +  		']'  	) ) with  ( 	X int  , 	Y int  , 	IsOpen bit  , 	IsFail bit  , 	IsLocked bit  ) 
 
萬一 json 裡面的屬性為 null 可能會噴這個錯誤 Msg 13608, Level 16, State 2, Line 1 可以參考這篇  語法大概長下面這樣
1 2 3 SELECT * FROM YOURTABLE WHERE JSON_QUERY(jsoncolumn,'$.YOURPROPERTY') IS NOT NULL