JSON是一个非常流行的,用于数据交换的文本数据(textual data)格式,主要用于Web和移动应用程序中。JSON 使用“键/值对”(Key:Value pair)存储数据,能够表示嵌套键值对和数组两种复杂数据类型,JSON仅仅使用逗号(引用Key)和中括号(引用数组元素),就能路由到指定的属性或成员,使用简单,功能强大。在SQL Server 2016版本中支持JSON格式,使用Unicode字符类型表示JSON数据,并能对JSON数据进行验证,查询和修改。推荐一款JSON验证和格式化的在线工具:json formatter

SQL Server 提供了内置函数,用于查询和更新JSON数据,分析JSON文本,如图:

一,定义和验证JSON数据

使用nvarchar存储JSON文本数据,通过函数 ISJSON(expression) 验证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;

二,JSON 数据的PATH 表达式

Path 表达式分为两部分:Path Mode和Path,Path Mode是可选的(optional),有两种模式:lax和strict。

1,Path Mode

在Path 表达式的开始,可以通过lax 或 strict 关键字显式声明Path Mode,如果不声明,默认的Path Mode是lax。在lax 模式下,如果path表达式出错,那么JSON函数返回NULL。在strict模式下,如果Path表达式出错,那么JSON函数抛出错误;

2,Path 表达式

Path是访问JSON数据的途径,有四种运算符:

  • $:代表整个JSON 数据的内容;
  • 逗号 . :表示JSON对象的成员,也叫做,字段(Field),或Key;
  • 中括号 [] :表示数组中的元素,元素的起始位置是0;
  • Key Name:键的名字,通过Key Name引用对应的Value;如果Key Name中包含空格,$,逗号,中括号,使用双引号;

例如,有如下JSON 数据,通过Path表达式,能够路由到JSON的各个属性:

{ "people":
[
{ "name": "John", "surname": "Doe" },
{ "name": "Jane", "surname": null, "active": true }
]
}

Path表达式查询的数据是:

  • $:表示JSON的内容,是最外层大括号中的所有Item,本例是一个people数组,数组的下标是从0开始的;
  • $.people[0]:表示people数组的第一元素:{ "name": "Jane", "surname": null, "active": true }
  • $.people[0].name :从people数组的第一个元素中,查询Key是Name的Item对应的数据,本例是John;
  • $.people[1].surname:people数组中部存在surname 字段,由于该Path 表达式没有声明Path Mode,默认值是lax,当Path表达式出现错误时,返回NULL;

三,通过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数据的流程是:

  • 修改现有的属性:按照参数path从JSON数据中找到指定的属性,将该属性的Value修改为参数newValue,返回值是修改之后的JSON数据;
  • 新增新的键值对(Key:Value pair):如果JSON数据中不存在指定的属性,那么按照参数Path,在指定的路径上新增键值对;
  • 删除键值对(Key:Value pair):如果参数newValue的值是NULL,那么表示从JSON数据中删除指定的属性;
  • append 关键字:用于从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 ]
)
]
  • path 参数:也叫table path,指定关系表在JSON数据中的路径;
  • column_path 参数:基于path参数,指定每个column在关系表JSON中的路径,应总是显式指定column path;
  • AS JSON 属性:如果指定AS JSON属性,那么 column的数据类型必须定义为nvarchar(max),表示该column的值是JSON数据;如果不指定AS JSON属性,那么该Column的值是标量值;
  • with 选项:指定关系表的Schema,应总是指定with选项;如果不指定with 选项,那么函数返回key,value和type三列;

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格式,

  • Auto 模式:根据select语句中column的顺序,自动生成JSON数据的格式;
  • Path 模式:使用column name的格式来生成JSON数据的格式,column name使用逗号分隔(dot-separated)表示组-成员关系;

示例,有表: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 $[]

使用for json返回JSON时,可以去掉外层的数组包装器 [],例如

for json path,without_array_wrapper

参考文档:

JSON Data (SQL Server)

JSON Path Expressions (SQL Server)

JSON Functions (Transact-SQL)

OPENJSON (Transact-SQL)

Index JSON data

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)

JSON Support in SQL Server 2016

JSON in SQL Server 2016: Part 1 of 4

使用TSQL查询和更新 JSON 数据的更多相关文章

  1. Atitit.列表页面and条件查询的实现最佳实践(1)------设置查询条件and提交查询and返回json数据

    Atitit.列表页面and条件查询的实现最佳实践(1)------设置查询条件and提交查询and返回json数据 1. 1. 配置条件字段@Conditional 1 1 2. 2. 配置条件字段 ...

  2. Atitit.列表页and查询条件的最佳实践(1)------设定搜索条件and提交查询and返回json数据

    Atitit.列表页and查询条件的最佳实践(1)------设置查询条件and提交查询and返回json数据 1. 1. 配置条件字段@Conditional 1 1 2. 2. 配置条件字段显示类 ...

  3. 在SQL中直接把查询结果转换为JSON数据

    下面这篇中,已经有准备一些数据: <MS SQL server对象类型type>https://www.cnblogs.com/insus/p/10903916.html 为前端服务,直接 ...

  4. springmvc-高级参数绑定-映射-异常-json数据交互-拦截器

    1.1. 高级参数绑定 1.1.1. 复制工程 把昨天的springmvc-web工程复制一份,作为今天开发的工程 复制工程,如下图: 粘贴并修改工程名为web2,如下图: 工程右键点击,如下图: 修 ...

  5. python测试开发django-15.查询结果转json(serializers)

    前言 django查询数据库返回的是可迭代的queryset序列,如果不太习惯这种数据的话,可以用serializers方法转成json数据,更直观 返回json数据,需要用到JsonResponse ...

  6. MS SQL读取JSON数据

    前面有一篇<在SQL中直接把查询结果转换为JSON数据>https://www.cnblogs.com/insus/p/10905566.html,是把table转换为json. 现反过来 ...

  7. redis存json数据时选择string还是hash

    redis存json数据时选择string还是hash 我们在缓存json数据到redis时经常会面临是选择string类型还是选择hash类型去存储.接下来我从占用空间和IO两方面来分析这两种类型的 ...

  8. T-SQL 查询、修改数据表

    T-SQL修改表数据 INSERT语句 语法: INSERT [TOP(expression) [PERCENT]] [INTO] { <object> | rowset_function ...

  9. T-SQL 随机返回特定行数据和分页查询

    T-SQL 随机返回特定行数据和分页查询 T-SQL 语言相较于标准SQL添加了很多特性,为了提高SQL Server的表现,是有必要深入了解的,面试时一般也会包含这两个小问题. 首先,是在一个Adv ...

随机推荐

  1. “不给力啊,老湿!”:RSA加密与破解

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 加密和解密是自古就有技术了.经常看到侦探电影的桥段,勇敢又机智的主角,拿着一长串毫 ...

  2. ASP.NET Core的路由[3]:Router的创建者——RouteBuilder

    在<注册URL模式与HttpHandler的映射关系>演示的实例中,我们总是利用一个RouteBuilder对象来为RouterMiddleware中间件创建所需的Router对象,接下来 ...

  3. 关于面试题 Array.indexof() 方法的实现及思考

    这是我在面试大公司时碰到的一个笔试题,当时自己云里雾里的胡写了一番,回头也曾思考过,最终没实现也就不了了之了. 昨天看到有网友说面试中也碰到过这个问题,我就重新思考了这个问题的实现方法. 对于想进大公 ...

  4. 深入浅出JavaScript之原型链&继承

    Javascript语言的继承机制,它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instanc ...

  5. nodejs操作arduino入门(javascript操作底层硬件)

    用Javascript来操作硬件早就不是一件稀奇的事情了. 所以作为一名电子专业出身的FE,我也打算尝试一下用js来驱动arduino: 要想操作这些底层硬件,肯定是需要一些工具的,我这里介绍的工具主 ...

  6. springmvc 多数据源 SSM java redis shiro ehcache 头像裁剪

    获取下载地址   QQ 313596790  A 调用摄像头拍照,自定义裁剪编辑头像 B 集成代码生成器 [正反双向](单表.主表.明细表.树形表,开发利器)+快速构建表单;  技术:31359679 ...

  7. BPM配置故事之案例3-参与者与数据自动加载

    这才过了两天,阿海又来了. 阿海:公司决定改进管理方式,以后物资申请的申请人和申请部门要写具体使用人的名字和部门了. 小明:不是要让我改回去吧? 阿海:那太麻烦了,你能不能把申请人改成选择,选好人自动 ...

  8. Android—基于微信开放平台v3SDK,开发微信支付填坑。

    接触微信支付之前听说过这是一个坑,,,心里已经有了准备...我以为我没准跳坑出不来了,没有想到我填上了,调用成功之后我感觉公司所有的同事都是漂亮的,隔着北京的大雾霾我仿佛看见了太阳~~~好了,装逼结束 ...

  9. uboot环境配置

    uboot环境配置 通过配置uboot让它在启动过程中从tftp获取内核和设备树,并从在加载内核之后把通过启动参数将"从nfs挂载根文件系统"传入内核.这个配置主要是通过uboot ...

  10. 用apt-file解决找不到头文件的问题

    在编译C语言的开源项目的时候,经常会出现头文件找不到的问题. 解决这类问题有一个特别好用的工具apt-file 1.在ubuntu下安装 sudo apt install apt-file 2.更新索 ...