javascript
SQL Server 2016 JSON原生支持实例说明
背景
Microsoft SQL Server 對(duì)于數(shù)據(jù)平臺(tái)的開發(fā)者來說越來越友好。比如已經(jīng)原生支持XML很多年了,在這個(gè)趨勢(shì)下,如今也能在SQLServer2016中使用內(nèi)置的JSON。尤其對(duì)于一些大數(shù)據(jù)很數(shù)據(jù)接口的解析環(huán)節(jié)來說這顯得非常有價(jià)值。與我們現(xiàn)在所做比如在SQL中使用CLR或者自定義的函數(shù)來解析JSON相比較,新的內(nèi)置JSON會(huì)大大提高性能,同時(shí)優(yōu)化了編程以及增刪查改等方法。
??? 那么是否意味著我們可以丟棄XML,然后開始使用JSON?當(dāng)然不是,這取決于數(shù)據(jù)輸出處理的目的。如果有一個(gè)外部的通過XML與外部交互數(shù)據(jù)的服務(wù)并且內(nèi)外的架構(gòu)是一致的,那么應(yīng)該是使用XML數(shù)據(jù)類型以及原生的函數(shù)。如果是針對(duì)微型服務(wù)架構(gòu)或者動(dòng)態(tài)元數(shù)據(jù)和數(shù)據(jù)存儲(chǔ),那么久應(yīng)該利用最新的JSON函數(shù)。
實(shí)例
??? 當(dāng)使用查詢這些已經(jīng)有固定架構(gòu)的JSON的數(shù)據(jù)表時(shí),使用“FOR JSON” 提示在你的T-SQL腳本后面,用這種方式以便于格式化輸出。一下實(shí)例我使用了SQLServer 2016 Worldwide Importers sample database,可以在GitHub上直接下載下來(下載地址)。看一下視圖Website.customers。我們查詢一個(gè)數(shù)據(jù)并格式化輸出JSON格式:
SELECT [CustomerID],[CustomerName],[CustomerCategoryName],[PrimaryContact],[AlternateContact],[PhoneNumber],[FaxNumber],[BuyingGroupName],[WebsiteURL],[DeliveryMethod],[CityName],DeliveryLocation.ToString() as DeliveryLocation,[DeliveryRun],[RunPosition]FROM [WideWorldImporters].[Website].[Customers]WHERE CustomerID=1FOR JSON AUTO
?
請(qǐng)注意我們有一個(gè)地理數(shù)據(jù)類型列(DeliveryLocation),這需要引入兩個(gè)重要的變通方案(標(biāo)黃):
首先,需要轉(zhuǎn)換一個(gè)string字符,否則就會(huì)報(bào)錯(cuò):
FOR JSON cannot serialize CLR objects. Cast CLR types explicitly into one of the supported types in FOR JSON queries.
其次,JSON采用鍵值對(duì)的語法因此必須指定一個(gè)別名來轉(zhuǎn)換數(shù)據(jù),如果失敗會(huì)出現(xiàn)下面的錯(cuò)誤:
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.
確認(rèn)了這些,改寫的格式化輸出如下:
[{"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": ""} ]
?
當(dāng)然也可以使用JSON作為輸入型DML語句,例如INSERT/UPDATE/DELETE 語句中使用“OPENJSON”。因此可以在所有的數(shù)據(jù)操作上加入JSON提示。
如果不了解數(shù)據(jù)結(jié)構(gòu)或者想讓其更加靈活,那么可以將數(shù)據(jù)存儲(chǔ)為一個(gè)JSON格式的字符類型,改列的類型可以使NVARCHAR 類型。Application.People 表中的CustomFields 列就是典型這種情況??梢杂萌缦抡Z句看一下表格格式這個(gè)列的內(nèi)容:
declare @json nvarchar(max)SELECT @json=[CustomFields] FROM [WideWorldImporters].[Application].[People] where PersonID=8select * from openjson(@json)
?
結(jié)果集在表格結(jié)果中的顯示:
?
用另一種方式來查詢這條記錄,前提是需要知道在JSON數(shù)據(jù)結(jié)構(gòu)和關(guān)鍵的名字,使用JSON_VALUE 和JSON_QUERY 函數(shù):
SELECTJSON_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 CommissionRateFROM [WideWorldImporters].[Application].[People]where PersonID=8
?
在表格結(jié)果集中展示表格格式的結(jié)果:
?
這個(gè)地方最關(guān)心就是查詢條件和添加索引。設(shè)想一下我們打算去查詢所有2011年以后雇傭的人,你可以運(yùn)行下面的查詢語句:
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 返回一個(gè)單一的文本值(nvarchar(4000))。需要轉(zhuǎn)換返回值到一個(gè)時(shí)間字段中,然后分離年來篩選查詢條件。實(shí)際執(zhí)行計(jì)劃如下:
?
為了驗(yàn)證如何對(duì)JSON內(nèi)容創(chuàng)建索引,需要?jiǎng)?chuàng)建一個(gè)計(jì)算列。為了舉例說明,Application.People 表標(biāo)記版本,并且加入計(jì)算列,當(dāng)系統(tǒng)版本為ON的時(shí)候不支持。我們這里使用Sales.Invoices表,其中ReturnedDeliveryData 中插入json數(shù)據(jù)。接下來獲取數(shù)據(jù),感受一下:
SELECT TOP 100 [InvoiceID],[CustomerID],JSON_QUERY([ReturnedDeliveryData],'$.Events')FROM [WideWorldImporters].[Sales].[Invoices]
?
發(fā)現(xiàn)結(jié)果集第一個(gè)event都是“Ready for collection”:
?
然后獲取2016年3月的發(fā)票數(shù)據(jù):
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'
實(shí)際執(zhí)行計(jì)劃如下:
?
??? 加入一個(gè)計(jì)算列叫做“ReadyDate”, 準(zhǔn)備好集合表達(dá)式的結(jié)果:
ALTER TABLE [WideWorldImporters].[Sales].[Invoices] ADD ReadyDate AS CONVERT(datetime, CONVERT(varchar,JSON_VALUE([ReturnedDeliveryData],'$.Events[0].EventTime')),126)
?
之后,重新執(zhí)行查詢,但是使用新的計(jì)算列作為條件:
SELECT [InvoiceID],[CustomerID],ReadyDateFROM [WideWorldImporters].[Sales].[Invoices]WHERE ReadyDate BETWEEN '20160301' AND '20160331'
?
執(zhí)行計(jì)劃是一樣的,除了SSMS建議的缺失索引:
?
因此,根據(jù)建議在計(jì)算列上建立索引來幫助查詢,建立索引如下:
/* 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
?
我們重新執(zhí)行查詢驗(yàn)證執(zhí)行計(jì)劃:
?
有了索引之后,大大提升了性能,并且查詢JSON的速度和表列是一樣快的。
總結(jié):
本篇通過對(duì)SQL2016 中的新增的內(nèi)置JSON進(jìn)行了簡(jiǎn)單介紹,主要有如下要點(diǎn):
?
- JSON能在SQLServer2016中高效的使用,但是JSON并不是原生數(shù)據(jù)類型;
- 如果使用JSON格式必須為輸出結(jié)果是表達(dá)式的提供別名;
- JSON_VALUE 和 JSON_QUERY? 函數(shù)轉(zhuǎn)移和獲取Varchar格式的數(shù)據(jù),因此必須將數(shù)據(jù)轉(zhuǎn)譯成你需要的類型。
- 在計(jì)算列的幫助下查詢JSON可以使用索引進(jìn)行優(yōu)化。
總結(jié)
以上是生活随笔為你收集整理的SQL Server 2016 JSON原生支持实例说明的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云前端周刊 - 第 14 期
- 下一篇: SpringMVC使用及知识点提炼