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_VALUE 和JSON_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进行了简单介绍,主要有以下要点: