SQL Server 2016 JSON原生支持實例說明

背景

Microsoft SQL Server 對於數據平臺的開發者來講愈來愈友好。好比已經原生支持XML不少年了,在這個趨勢下,現在也能在SQLServer2016中使用內置的JSON。尤爲對於一些大數據很數據接口的解析環節來講這顯得很是有價值。與咱們如今所作好比在SQL中使用CLR或者自定義的函數來解析JSON相比較,新的內置JSON會大大提升性能,同時優化了編程以及增刪查改等方法。git

    那麼是否意味着咱們能夠丟棄XML,而後開始使用JSON?固然不是,這取決於數據輸出處理的目的。若是有一個外部的經過XML與外部交互數據的服務而且內外的架構是一致的,那麼應該是使用XML數據類型以及原生的函數。若是是針對微型服務架構或者動態元數據和數據存儲,那麼久應該利用最新的JSON函數。github

實例

    當使用查詢這些已經有固定架構的JSON的數據表時,使用「FOR JSON」 提示在你的T-SQL腳本後面,用這種方式以便於格式化輸出。一下實例我使用了SQLServer 2016 Worldwide Importers sample database,能夠在GitHub上直接下載下來(下載地址)。看一下視圖Website.customers。咱們查詢一個數據並格式化輸出JSON格式:sql

SELECT [CustomerID]
      ,[CustomerName]
      ,[CustomerCategoryName]
      ,[PrimaryContact]
      ,[AlternateContact]
      ,[PhoneNumber]
      ,[FaxNumber]
      ,[BuyingGroupName]
      ,[WebsiteURL]
      ,[DeliveryMethod]
      ,[CityName]
      
 ,DeliveryLocation.ToString() as DeliveryLocation
      ,[DeliveryRun]
      ,[RunPosition]
  FROM [WideWorldImporters].[Website].[Customers]
  WHERE CustomerID=1
  FOR JSON AUTO

  

 

請注意咱們有一個地理數據類型列(DeliveryLocation),這須要引入兩個重要的變通方案(標黃):express

首先,須要轉換一個string字符,不然就會報錯:編程

FOR JSON cannot serialize CLR objects. Cast CLR types explicitly into one of the supported types in FOR JSON queries.json

其次,JSON採用鍵值對的語法所以必須指定一個別名來轉換數據,若是失敗會出現下面的錯誤:數據結構

Column expressions and data sources without names or aliases cannot be formatted as JSON text using FOR JSON clause. Add alias to the unnamed column or table.架構

確認了這些,改寫的格式化輸出以下:ide

[
    {
        "CustomerID": 1,
        "CustomerName": "Tailspin Toys (Head Office)",
        "CustomerCategoryName": "Novelty Shop",
        "PrimaryContact": "Waldemar Fisar",
        "AlternateContact": "Laimonis Berzins",
        "PhoneNumber": "(308) 555-0100",
        "FaxNumber": "(308) 555-0101",
        "BuyingGroupName": "Tailspin Toys",
        "WebsiteURL": "http://www.tailspintoys.com",
        "DeliveryMethod": "Delivery Van",
        "CityName": "Lisco",
        "DeliveryLocation": "POINT (-102.6201979 41.4972022)",
        "DeliveryRun": "",
        "RunPosition": ""
    }
]

  

 

固然也可使用JSON做爲輸入型DML語句,例如INSERT/UPDATE/DELETE 語句中使用「OPENJSON」。所以能夠在全部的數據操做上加入JSON提示。函數

若是不瞭解數據結構或者想讓其更加靈活,那麼能夠將數據存儲爲一個JSON格式的字符類型,改列的類型可使NVARCHAR 類型。Application.People 表中的CustomFields 列就是典型這種狀況。能夠用以下語句看一下表格格式這個列的內容:

declare @json nvarchar(max)

SELECT @json=[CustomFields]
FROM [WideWorldImporters].[Application].[People]
where PersonID=8

select * from openjson(@json)

  

 

結果集在表格結果中的顯示:

 

用另外一種方式來查詢這條記錄,前提是須要知道在JSON數據結構和關鍵的名字,使用JSON_VALUEJSON_QUERY 函數:

  SELECT
       JSON_QUERY([CustomFields],'$.OtherLanguages') as OtherLanguages,
       JSON_VALUE([CustomFields],'$.HireDate') as HireDate,
       JSON_VALUE([CustomFields],'$.Title') as Title,
       JSON_VALUE([CustomFields],'$.PrimarySalesTerritory') as PrimarySalesTerritory,
       JSON_VALUE([CustomFields],'$.CommissionRate') as CommissionRate
  FROM [WideWorldImporters].[Application].[People]
  where PersonID=8

  

 

在表格結果集中展現表格格式的結果:

 

這個地方最關心就是查詢條件和添加索引。設想一下咱們打算去查詢全部2011年之後僱傭的人,你能夠運行下面的查詢語句:

SELECT personID,fullName,JSON_VALUE(CustomFields,'$.HireDate') as hireDate
FROM [WideWorldImporters].[Application].[People]
where IsEmployee=1
and year(cast(JSON_VALUE(CustomFields,'$.HireDate') as date))>2011

  

 

切記JSON_VALUE 返回一個單一的文本值(nvarchar(4000))。須要轉換返回值到一個時間字段中,而後分離年來篩選查詢條件。實際執行計劃以下:

 

爲了驗證如何對JSON內容建立索引,須要建立一個計算列。爲了舉例說明,Application.People 表標記版本,而且加入計算列,當系統版本爲ON的時候不支持。咱們這裏使用Sales.Invoices表,其中ReturnedDeliveryData 中插入json數據。接下來獲取數據,感覺一下:

SELECT TOP 100 [InvoiceID]
      ,[CustomerID]
      ,JSON_QUERY([ReturnedDeliveryData],'$.Events')
  FROM [WideWorldImporters].[Sales].[Invoices]

  

 

發現結果集第一個event都是「Ready for collection」:

 

而後獲取2016年3月的發票數據:

SELECT [InvoiceID]
      ,[CustomerID]
      ,CONVERT(datetime, CONVERT(varchar,JSON_VALUE([ReturnedDeliveryData],'$.Events[0].EventTime')),126)
  FROM [WideWorldImporters].[Sales].[Invoices]
  WHERE CONVERT(datetime, CONVERT(varchar,JSON_VALUE([ReturnedDeliveryData],'$.Events[0].EventTime')),126)
       BETWEEN '20160301' AND '20160331'

  

實際執行計劃以下:

 

    加入一個計算列叫作「ReadyDate」, 準備好集合表達式的結果:

ALTER TABLE [WideWorldImporters].[Sales].[Invoices]
ADD ReadyDate AS CONVERT(datetime, CONVERT(varchar,JSON_VALUE([ReturnedDeliveryData],'$.Events[0].EventTime')),126)

  

 

以後,從新執行查詢,可是使用新的計算列做爲條件:

SELECT [InvoiceID]
      ,[CustomerID]
      ,ReadyDate
  FROM [WideWorldImporters].[Sales].[Invoices]
  WHERE ReadyDate BETWEEN '20160301' AND '20160331'

  

 

執行計劃是同樣的,除了SSMS建議的缺失索引:

 

所以,根據建議在計算列上創建索引來幫助查詢,創建索引以下:

/*
The Query Processor estimates that implementing the following index could improve the query cost by 99.272%.
*/
CREATE NONCLUSTERED INDEX IX_Invoices_ReadyDate
ON [Sales].[Invoices] ([ReadyDate])
INCLUDE ([InvoiceID],[CustomerID])
GO

  

 

咱們從新執行查詢驗證執行計劃:

 

有了索引以後,大大提高了性能,而且查詢JSON的速度和表列是同樣快的。

總結:

本篇經過對SQL2016 中的新增的內置JSON進行了簡單介紹,主要有以下要點:

 

  • JSON能在SQLServer2016中高效的使用,可是JSON並非原生數據類型;
  • 若是使用JSON格式必須爲輸出結果是表達式的提供別名;
  • JSON_VALUE 和 JSON_QUERY  函數轉移和獲取Varchar格式的數據,所以必須將數據轉譯成你須要的類型。
  • 在計算列的幫助下查詢JSON可使用索引進行優化。
相關文章
相關標籤/搜索