JSON是一個很是流行的,用於數據交換的文本數據(textual data)格式,主要用於Web和移動應用程序中。JSON 使用「鍵/值對」(Key:Value pair)存儲數據,可以表示嵌套鍵值對和數組兩種複雜數據類型,JSON僅僅使用逗號(引用Key)和中括號(引用數組元素),就能路由到指定的屬性或成員,使用簡單,功能強大。在SQL Server 2016版本中支持JSON格式,使用Unicode字符類型表示JSON數據,並能對JSON數據進行驗證,查詢和修改。推薦一款JSON驗證和格式化的在線工具:json formatter。sql
SQL Server 提供了內置函數,用於查詢和更新JSON數據,分析JSON文本,如圖:express
一,定義和驗證JSON數據編程
使用nvarchar存儲JSON文本數據,經過函數 ISJSON(expression) 驗證JSON數據是否有效。json
declare @json nvarchar(max) set @json = N'{ "info":{ "type":1, "address":{ "town":"bristol", "county":"avon", "country":"england" }, "tags":["sport", "water polo"] }, "type":"basic" }' select isjson(@json)
ISJSON 函數的格式是: ISJSON ( expression ) ,返回1,表示字符串是JSON數據;返回0,表示字符串不是JSON數據;返回NULL,表示 expression是NULL;c#
二,JSON 數據的PATH 表達式數組
Path 表達式分爲兩部分:Path Mode和Path,Path Mode是可選的(optional),有兩種模式:lax和strict。app
1,Path Modeide
在Path 表達式的開始,能夠經過lax 或 strict 關鍵字顯式聲明Path Mode,若是不聲明,默認的Path Mode是lax。在lax 模式下,若是path表達式出錯,那麼JSON函數返回NULL。在strict模式下,若是Path表達式出錯,那麼JSON函數拋出錯誤;函數
2,Path 表達式工具
Path是訪問JSON數據的途徑,有四種運算符:
例如,有以下JSON 數據,經過Path表達式,可以路由到JSON的各個屬性:
{ "people": [ { "name": "John", "surname": "Doe" }, { "name": "Jane", "surname": null, "active": true } ] }
Path表達式查詢的數據是:
三,經過Path查詢JSON數據
1,查詢標量值(JSON_VALUE)
使用 JSON_VALUE(expression , path ) 函數,從JSON數據,根據Path 參數返回標量值,返回的數據是寬字符類型,最大值Nvarchar(4000);若是必須返回大於nvarchar(4000)的數據,使用OpenJson行集函數。
declare @json nvarchar(max) set @json = N'{ "info":{ "type":1, "address":{ "town":"bristol", "county":"avon", "country":"england" }, "tags":["sport", "water polo"] }, "type":"basic" }' select json_value(@json, '$.type') as type, json_value(@json, '$.info.type') as info_type, json_value(@json, '$.info.address.town') as town, json_value(@json, '$.info.tags[0]') as tag
2,返回JSON數據(JSON_QUERY)
使用 JSON_QUERY ( expression [ , path ] ) 函數,根據Path 參數,返回JSON 數據(JSON fragment);參數path是可選的(optional),若是不指定option參數,那麼默認的path是$,即,返回整個JSON數據。
declare @json nvarchar(max) set @json = N'{ "info":{ "type":1, "address":{ "town":"bristol", "county":"avon", "country":"england" }, "tags":["sport", "water polo"] }, "type":"basic" }' select json_query(@json, '$') as json_context, json_query(@json, '$.info') as info, json_query(@json, '$.info.address') as info_address, json_query(@json, '$.info.tags') as info_tags
四,經過Path修改JSON數據
使用 JSON_MODIFY ( expression , path , newValue ) 修改JSON數據中的屬性值,並返回修改以後的JSON數據,該函數修改JSON數據的流程是:
示例,對JSON數據進行update,insert,delete和追加數據元素
declare @info nvarchar(100) = '{"name":"john","skills":["c#","sql"]}' -- update name set @info = json_modify(@info, '$.name', 'mike') -- insert surname set @info = json_modify(@info, '$.surname', 'smith') -- delete name set @info = json_modify(@info, '$.name', null) -- add skill set @info = json_modify(@info, 'append $.skills', 'azure')
五,將JSON數據轉換爲關係表
OPENJSON函數是一個行集函數(RowSet),可以將JSON數據轉換爲關係表,
OPENJSON( jsonExpression [ , path ] ) [ WITH ( colName type [ column_path ] [ AS JSON ] [ , colName type [ column_path ] [ AS JSON ] ] [ , . . . n ] ) ]
1,示例,從JSON數據中,以關係表方式呈現數據
declare @json nvarchar(max) set @json = N'{ "info":{ "type":1, "address":{ "town":"bristol", "county":"avon", "country":"england" }, "tags":["sport", "water polo"] }, "type":"basic" }' SELECT info_type,info_address,tags FROM OPENJSON(@json, '$.info') with ( info_type tinyint 'lax $.type', info_address nvarchar(max) 'lax $.address' as json, tags nvarchar(max) 'lax $.tags' as json )
2,OpenJSON 函數的另一個功能是遍歷數組,爲數組中的每個元素產生一個數據行
When you use OPENJSON with an explicit schema, the function returns a table with the schema that you defined in the WITH clause. In the WITH clause, you define columns, their types, and the paths of the source properties for each column.
For each element in the array in the input expression, OPENJSON generates a separate row in the output table.
For each property of the array elements specified by using the colName type column_path syntax, OPENJSON converts the value to the specified type and populates a cell in the output table.
SET @json = N'{"Orders": {"OrdersArray": [ { "Order": { "Number":"SO43659", "Date":"2011-05-31T00:00:00" }, "AccountNumber":"AW29825", "Item": { "Price":2024.9940, "Quantity":1 } }, { "Order": { "Number":"SO43661", "Date":"2011-06-01T00:00:00" }, "AccountNumber":"AW73565", "Item": { "Price":2024.9940, "Quantity":3 } } ] } }' SELECT t.* FROM OPENJSON ( @json, '$.Orders.OrdersArray' ) WITH ( Number varchar(200) '$.Order.Number', Date datetime '$.Order.Date', Customer varchar(200) '$.AccountNumber', Quantity int '$.Item.Quantity', [Order] nvarchar(MAX) AS JSON ) as t
3,OpenJSON 函數搭配Apply使用,爲表中的JSON數據轉換成關係表形式
select t.*,sl.result,sl.time from [dbo].[WebPages] sl cross apply openjson(JSON_QUERY(Parameters,'$.CategoryList')) with ( ID varchar(64) '$.ID', name varchar(64) '$.Name', Type varchar(64) '$.Type' ) as t where sl.action='New Product' and t.Type in('Blogs','Forums') order by sl.time desc
六,將關係表數據以JSON格式存儲
經過For JSON Auto/Path,將關係表數據存儲爲JSON格式,
示例,有表:dt_json,存儲如下數據:
1,以Auto 模式生成JSON格式
select id, name, category from dbo.dt_json for json auto,root('json')
返回的數據格式是
{ "json":[ { "id":1, "name":"C#", "category":"Computer" }, { "id":2, "name":"English", "category":"Language" }, { "id":3, "name":"MSDN", "category":"Web" }, { "id":4, "name":"Blog", "category":"Forum" } ] }
2,以Path模式生成JSON格式,推薦使用path模式,特別是在字段來源於多個表的狀況下,控制JSON的格式
select id as 'book.id', name as 'book.name', category as 'product.category' from dbo.dt_json for json path,root('json')
返回的數據格式是:
{ "json":[ { "book":{ "id":1, "name":"C#" }, "product":{ "category":"Computer" } }, { "book":{ "id":2, "name":"English" }, "product":{ "category":"Language" } }, { "book":{ "id":3, "name":"MSDN" }, "product":{ "category":"Web" } }, { "book":{ "id":4, "name":"Blog" }, "product":{ "category":"Forum" } } ] }
七,索引JSON數據
JSON文本不是內置的數據類型,沒有專門的JSON索引,可是,能夠經過建立計算列和標準B-Tree索引提升查詢JSON數據的性能,避免全表掃描(Full Table Scan),經過索引計算列,間接實現對JSON進行查找。
索引JSON數據的Workaround是:爲查詢條件(Filter)建立計算列,使用persisted屬性持久存儲;在計算列上建立索引,使用包含列(Include)包含特定的字段,以免鍵值查找(Key Lookup),提升索引查找的性能。
例如,有以下關係表,字段category包含JSON數據:
按照type屬性過濾,包含name字段,建立索引的示例是:
alter table dbo.dt_json add category_type as (cast(json_value(category,'$.type') as int)) persisted; create nonclustered index idx_dt_json_category_type on dbo.dt_json ( category_type ) include(name);
八,JSON查詢技巧
1,使用Path模式,控制JSON結構的Path(層次)
當字段來源於多個Table時,使用Auto模式,在SQL Server 2016中,默認會將字段分組,
select top 3 t.name ,o.object_id ,o.type from sys.objects o inner join sys.tables t on o.object_id=t.object_id for json auto
返回的結果是,多了一個層次:
[{"name":"table_1","o":[{"object_id":27147142,"type":"U "}]}, {"name":"table_2","o":[{"object_id":87671360,"type":"U "}]}, {"name":"table_3","o":[{"object_id":91147370,"type":"U "}]}]
使用Path模式(for json path),path是根據列的別名來定義Json的層次
[{"name":"table_1","object_id":27147142,"type":"U "},
{"name":"table_2","object_id":87671360,"type":"U "},
{"name":"table_3","object_id":91147370,"type":"U "}]
2,嵌套JSON結構
在查詢時,Table_2的JsonData字段是個Json數據,須要嵌套到另外一個JSON中,例如:[{"UnitPrice":12, "OrderQty":1}],若是在外層JSON結構中,嵌套一個內層的JSON結構:
select t1.ID ,t2.JsonData from dbo.table_1 t1 inner join dbo.table_2 t2 on ... for json path
返回的數據以下,JsonData是一個字符串,SQL Server自動對其進行字符轉碼:
[
{
"Id": 12,
"JsonData": "[{\"UnitPrice\":12, \"OrderQty\":1}]"
}
]
在嵌套的JSON上,使用JSON_Query(expression,path),返回數據,而後再對其進行JSON 格式:
select t1.ID ,json_query(t2.JsonData) as JsonData from dbo.table_1 t1 inner join dbo.table_2 t2 on ... for json path
返回的JSON結構以下,知足:
[
{
"Id": 12,
"JsonData": [{"UnitPrice":12, "OrderQty":1}]
}
]
九,編程注意事項
1,空JSON
JSON_QUERY(expression,path) 要求expression必須是有效的,爲避免JSON_QUERY執行失敗,對NULL值,要麼保持NULL值,要麼設置空JSON,而空JSONO是 [] 或 {},而不是空的字符。
2,JSON中的數組
在查詢時,常常會返回JSON數組,使用[index]來遍歷數組元素,數組下標從0開始,例如,如下JSON數組,及其查詢示例:
[ {...} ] --path expression lax $[0]
使用for json返回JSON時,能夠去掉外層的數組包裝器 [],例如
for json path,without_array_wrapper
參考文檔:
JSON Path Expressions (SQL Server)
Format Query Results as JSON with FOR JSON (SQL Server)
Format Nested JSON Output with PATH Mode (SQL Server)
Format JSON Output Automatically with AUTO Mode (SQL Server)