sqlserver:存储Json数据

环境:

  • window 10 x64 专业版
  • Microsoft SQL Server 2019 (RTM) Enterprise Edition (64-bit)

参照:

一、SqlServer是如何支持Json的?

Sql Server 从 2016 开始支持了一些 json 操作,但在SqlServer中Json还是被存储为字符串,只不过是符合JSON格式的字符串会允许一些JSON特有的操作,如:json对象的操作函数、json对象与关系表格的相互转换等,下面先介绍json对象的操作函数,然后再来说一下json对象和关系表格的相互转换。

二、Json操作相关函数

2.1 判断字符串是否符合JSON格式规范 ISJSON()

ISJSON函数是按照JSON格式规范进行判断的,JSON的格式规范参照《介绍JSON》

select ISJSON('{}') as '{}',
ISJSON('') as '空',
ISJSON('{"name":"xiaoming","age":20}') as '{...}',
ISJSON('[]') as '[]',
ISJSON('[{"name":"小明"}]') as '[{...}]'

在这里插入图片描述

2.2 从Json对象中提取标量值 JSON_VALUE

注意:这个函数是从JSON对象中提取标量值(字符串、数字或true/false)。
json中包含六类数据:0:null、1:字符串、2:数字、3:true/false、4:数组、5:对象
如果你试图用JSON_VALUE从json字符串中取出对象或数组,你将得到一个NULL(默认为null,严格模式下将报错)。

关于路径表达式:

  • JSON_VALUE/OPENJSON/JSON_QUERY/JSON_MODIFY这四个函数中你将会使用到路径表达式。
  • 路径表达式有两种模式宽松(lax)严格(strict)并且默认是宽松模式。

    在宽松模式下,当你取值出错时(比如你用JSON_VALUE取一个对象或者是取一个不存在的属性),返回的是NULL。强制使用宽松模式的实例:select JSON_VALUE('{}','lax $.name')
    在严格模式下,当你取值出错时,它就直接报错。强制使用严格模式的实例:select JSON_VALUE('{}','strict $.name')

  • 路径表达式语法:

    使用$代替json对象本身;
    取对象的属性使用$.prop格式,如果属性名包含特殊格式,则使用"包裹,如:$."first name"
    取数组的语法示例'$[0].name''$.arr[0].name'

declare @jsontext nvarchar(max);
set @jsontext='
{
	"name": "小明",
	"first name": "first xiaoming",
	"age": 20,
	"sex": null,
	"info": {
		"addr": "xiaominglu"
	},
	"books": [{
		"name": "语文",
		"score": 85.5
	}, {
		"name": "数学",
		"score": 98
	}]
}
';

select JSON_VALUE(@jsontext,'$.name') as '$.name',
JSON_VALUE(@jsontext,'$.abc') as '$.abc',
JSON_VALUE(@jsontext,'$.age') as '$.age',
JSON_VALUE(@jsontext,'$.sex') as '$.sex',
JSON_VALUE(@jsontext,'$.info') as '$.info',
JSON_VALUE(@jsontext,'$.info.addr') as '$.info.addr',
JSON_VALUE(@jsontext,'$.books') as '$.books',
JSON_VALUE(@jsontext,'$.books[0].name') as '$.books[0].name',
JSON_VALUE(@jsontext,'$.books[1].score') as '$.books[1].score',
JSON_VALUE(@jsontext,'$.books[2].name') as '$.books[2].name'

在这里插入图片描述

2.3 从Json字符串中提取对象或数组 JSON_QUERY

这个函数和JSON_VALUE是类似的,但它返回的是一个json对象,而不是标量值,如果你试图用JSON_QUERY函数返回一个标量值,那么你将得到一个NULL
注意:因为这个函数返回的是一个json对象,所以可用在JSON_MODIFY的赋值中,以防止SqlServer自动转义成字符串。

declare @jsontext nvarchar(max);
set @jsontext='
{
	"name": "小明",
	"first name": "first xiaoming",
	"age": 20,
	"sex": null,
	"info": {
		"addr": "xiaominglu"
	},
	"books": [{
		"name": "语文",
		"score": 85.5
	}, {
		"name": "数学",
		"score": 98
	}]
}
';

select 
JSON_QUERY(@jsontext) as '无path',
JSON_QUERY(@jsontext,'$') as '$',
JSON_QUERY(@jsontext,'$.name') as '$.name',
JSON_QUERY(@jsontext,'$.info') as '$.info',
JSON_QUERY(@jsontext,'$.abc') as '$.abc',
JSON_QUERY(@jsontext,'$.books') as '$.books',
JSON_QUERY(@jsontext,'$.books[0]') as '$.books[0]'

在这里插入图片描述

2.4 更改JSON字符串的内容 JSON_MODIFY

注意:这里只介绍宽松模式。
借助JSON_MODIFY函数,我们可以实现对JSON对象的属性新增、删除、更新操作,以及扩展的改属性名等操作。如果我们操作的属性是一个非标量的话,我们还需要借助JSON_QUERY函数。

2.4.1 使用JSON_MODIFY更新JSON对象属性值

-- 更改json对象name属性值
DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(@info,'$.name','Mike')
PRINT @info

在这里插入图片描述

2.4.2 使用JSON_MODIFY给JSON对象添加属性

--给json对象添加surname属性
DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(@info,'$.surname','Smith')
PRINT @info

在这里插入图片描述

2.4.3 使用JSON_MODIFY删除JSON对象的属性

--删除json对象的name属性
DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(@info,'$.name',NULL)
PRINT @info

在这里插入图片描述

2.4.4 当json对象的属性为非标量值(对象/数组)时的增删改操作

如果我们要更改的属性是一个数组或对象的话,我们需要使用JSON_QUERY函数以防止系统将属性值更改为转义后的字符串。
下面是一个错误的示例:

-- 在John的技能表里添加"VB"技能并将它排在第一位
DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(@info,'$.skills','["VB","C#","SQL"]')
PRINT @info

在这里插入图片描述
注意,上面输出的属性skills是一个字符串,而不是一个数组。
正确的书写方法如下:

DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(@info,'$.skills',JSON_QUERY('["VB","C#","SQL"]'))
PRINT @info

在这里插入图片描述

2.4.5 当json对象的属性为数组时的快速添加操作

如果我们想向数组中添加一个值的话,有一个快速的命令可以让我们使用,如下:

DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(@info,'append $.skills','Azure')
PRINT @info

在这里插入图片描述

2.4.6 嵌套使用的 JSON_MODIFY

可以嵌套使用JSON_MODIFY,看下面的示例:

DECLARE @info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT @info
SET @info=JSON_MODIFY(JSON_MODIFY(JSON_MODIFY(@info,'$.name','Mike'),'$.surname','Smith'),'append $.skills','Azure')
PRINT @info

在这里插入图片描述

2.4.6.1 修改json对象的属性名称

我们可以嵌套使用JSON_MODIFY函数,以实现修改json对象属性名称的目的,如下:

DECLARE @product NVARCHAR(100)='{"price":49.99}'
PRINT @product
SET @product=
 JSON_MODIFY(
  JSON_MODIFY(@product,'$.Price',CAST(JSON_VALUE(@product,'$.price') AS NUMERIC(4,2))),
  '$.price',
  NULL
 )
PRINT @product

在这里插入图片描述

2.4.6.2 使json对象的属性值递增

我们可以嵌套使用JSON_MODIFY函数,以实现将给定json对象的属性值递增的目的,如下:

DECLARE @stats NVARCHAR(100)='{"click_count": 173}'
PRINT @stats
SET @stats=JSON_MODIFY(@stats,'$.click_count',
 CAST(JSON_VALUE(@stats,'$.click_count') AS INT)+1)
PRINT @stats

在这里插入图片描述

三、使用OPENJSON将json对象转换为关系表

openjson的完整语法为:

OPENJSON( jsonExpression [ , path ] )  [ <with_clause> ]
<with_clause> ::= WITH ( { colName type [ column_path ] [ AS JSON ] } [ ,...n ] )

下面,我们逐步探索OPENJSON的解析过程

3.1 直接使用OpenJson(’{}’)

默认情况下OPENJSON会将jsonExpression表示的json对象解析为table(key,value,type)的表格形式,如下:

DECLARE @json NVarChar(2048) = N'{
   "Null_value": null,
   "String_value": "John",
   "DoublePrecisionFloatingPoint_value": 45,
   "DoublePrecisionFloatingPoint_value": 2.3456,
   "BooleanTrue_value": true,
   "BooleanFalse_value": false,
   "Array_value": ["a","r","r","a","y"],
   "Object_value": {"obj":"ect"}
}';

SELECT * FROM OpenJson(@json);

在这里插入图片描述

对于数组,openjson也默认将它转为这样的三列表格,如下:

DECLARE @json NVarChar(2048) = N'[
	null,
	"Jack",
	"Jone",
	"Tom",
	15,
	false,
	[1,2,3],
	{"age":18,"name":"xiaoming"}
]';

SELECT * FROM OpenJson(@json);

在这里插入图片描述
如果json对象为空对象或空数组的话,那么转换成的表格将没有数据,如下:

select * from OPENJSON('{}')

在这里插入图片描述

3.2 使用path参数将json对象中的指定部分进行转换

上面默认情况下,OPENJSON将json对象整体进行转换,我们可以添加path参数来控制仅转换json对象的一部分。

示例如下:

DECLARE @json NVARCHAR(4000) = N'
{
	"path": {
		"to": {
			"sub-object": ["en-GB", "en-UK", "de-AT", "es-AR", "sr-Cyrl"]
		}
	},
	"path2":[1,2,3]
}
';

SELECT *
FROM OPENJSON(@json,'$.path.to."sub-object"')

在这里插入图片描述
对于数组也是一样的:

DECLARE @json NVARCHAR(4000) = N'
[{
		"name": "小明",
		"age": 20,
		"addr": "天明路"
	},
	{
		"name": "小花",
		"age": 18
	}
]
';

SELECT *
FROM OPENJSON(@json,'$[0]')

在这里插入图片描述

3.3 使用with字句自定义转换后的表格形式

直接看示例:

DECLARE @json NVARCHAR(MAX) = N'[  
  {  
    "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 *
FROM OPENJSON ( @json )  
WITH (   
      Number   varchar(200)   '$.Order.Number',  
      Date     datetime       '$.Order.Date',  
      Customer varchar(200)   '$.AccountNumber',  
      Quantity int            '$.Item.Quantity',  
      [Order]  nvarchar(MAX)  AS JSON  
 )

在这里插入图片描述

注意:
上面的OPENJSON是将数组转为表格,结果是两行数据,如果最外层是对象的话,那么转换后是一行数据。

说明:
使用OPENJSON函数将json转为表格形式后,就可以按照表格的特点进行其他操作了,比如:join、where条件等。。,

四、使用FOR JSON将查询结果输出为json

For Json 子句的完整语法为:

[ FOR <JSON>]  
<JSON> ::=  
JSON   
{   
    { AUTO | PATH }   
    [   
        [ , ROOT [ ( 'RootName' ) ] ]  
        [ , INCLUDE_NULL_VALUES ]  
        [ , WITHOUT_ARRAY_WRAPPER ]  
    ]  
  
}

4.1 使用FOR JSON AUTO 将关系表格输出为json

drop table if exists test;
go
create table test(
id int primary key,
name varchar(50),
age int,
sex varchar(50),
birth datetime
);
go
insert into test(id,name,age,sex,birth) values
(1,'小明',null,'male','2002-01-02'),
(2,'Jack',20,'male','2000-11-02'),
(3,'小花',19,'female','2001-01-16'),
(4,'Tom',18,'male','2002-04-02');
go
select * from test for json auto;

在这里插入图片描述
将上面的输出格式化后:

[{
	"id": 1,
	"name": "小明",
	"sex": "male",
	"birth": "2002-01-02T00:00:00"
}, {
	"id": 2,
	"name": "Jack",
	"age": 20,
	"sex": "male",
	"birth": "2000-11-02T00:00:00"
}, {
	"id": 3,
	"name": "小花",
	"age": 19,
	"sex": "female",
	"birth": "2001-01-16T00:00:00"
}, {
	"id": 4,
	"name": "Tom",
	"age": 18,
	"sex": "male",
	"birth": "2002-04-02T00:00:00"
}]

4.2 使用For Json Path自定义

使用For Json Auto的输出大多是是满足我们使用的,但是当我们希望能更好的控制输出JSON的格式的话,我们可以试一试For Json Path。

drop table if exists test;
go
create table test(
id int primary key,
name varchar(50),
age int,
sex varchar(50),
birth datetime
);
go
insert into test(id,name,age,sex,birth) values
(1,'小明',null,'male','2002-01-02'),
(2,'Jack',20,'male','2000-11-02'),
(3,'小花',19,'female','2001-01-16'),
(4,'Tom',18,'male','2002-04-02');
go
select 
id,
name,
age as 'detail.age',
sex as 'detail.sex',
birth as 'detail.ext.birth'
from test for json path;

在这里插入图片描述
将上面的输出格式化后:

[{
	"id": 1,
	"name": "小明",
	"detail": {
		"sex": "male",
		"ext": {
			"birth": "2002-01-02T00:00:00"
		}
	}
}, {
	"id": 2,
	"name": "Jack",
	"detail": {
		"age": 20,
		"sex": "male",
		"ext": {
			"birth": "2000-11-02T00:00:00"
		}
	}
}, {
	"id": 3,
	"name": "小花",
	"detail": {
		"age": 19,
		"sex": "female",
		"ext": {
			"birth": "2001-01-16T00:00:00"
		}
	}
}, {
	"id": 4,
	"name": "Tom",
	"detail": {
		"age": 18,
		"sex": "male",
		"ext": {
			"birth": "2002-04-02T00:00:00"
		}
	}
}]

4.3 使用 INCLUDE_NULL_VALUES 选项将 NULL 值包含在 JSON 输出中

观察上面的输出,我们会发现,小明的age数据在输出为json的时候被忽略了,因为他的age为null。
这是默认的处理方式,如果我们想将null值也输出的话,可以使用INCLUDE_NULL_VALUES

drop table if exists test;
go
create table test(
id int primary key,
name varchar(50),
age int,
sex varchar(50),
birth datetime
);
go
insert into test(id,name,age,sex,birth) values
(1,'小明',null,'male','2002-01-02'),
(2,'Jack',20,'male','2000-11-02'),
(3,'小花',19,'female','2001-01-16'),
(4,'Tom',18,'male','2002-04-02');
go
select 
id,
name,
age as 'detail.age',
sex as 'detail.sex',
birth as 'detail.ext.birth'
from test 
where id=1
for json path,INCLUDE_NULL_VALUES

在这里插入图片描述

4.4 使用 ROOT 选项将根节点添加到 JSON 输出中

上面的输出的结果都是以数组展示的,因为数组和关系表之间可以直接去映射比较方便。
如果我们想将结果输出为对象形式的,我们可以试试Root

drop table if exists test;
go
create table test(
id int primary key,
name varchar(50),
age int,
sex varchar(50),
birth datetime
);
go
insert into test(id,name,age,sex,birth) values
(1,'小明',null,'male','2002-01-02'),
(2,'Jack',20,'male','2000-11-02'),
(3,'小花',19,'female','2001-01-16'),
(4,'Tom',18,'male','2002-04-02');
go
select 
id,
name,
age as 'detail.age',
sex as 'detail.sex',
birth as 'detail.ext.birth'
from test 
where id=1
for json path,INCLUDE_NULL_VALUES,Root('persons');

在这里插入图片描述

4.5 从 JSON 中删除方括号 - WITHOUT_ARRAY_WRAPPER 选项

一般我们的输出结果都是json数组(除了上面提到的Root选项),如果我们不想要外面那个方括号,我们可以试试WITHOUT_ARRAY_WRAPPER

drop table if exists test;
go
create table test(
id int primary key,
name varchar(50),
age int,
sex varchar(50),
birth datetime
);
go
insert into test(id,name,age,sex,birth) values
(1,'小明',null,'male','2002-01-02'),
(2,'Jack',20,'male','2000-11-02'),
(3,'小花',19,'female','2001-01-16'),
(4,'Tom',18,'male','2002-04-02');
go
select 
*
from test 
for json path,WITHOUT_ARRAY_WRAPPER;

在这里插入图片描述
将上面输出格式化后:

{
	"id": 1,
	"name": "小明",
	"sex": "male",
	"birth": "2002-01-02T00:00:00"
}, {
	"id": 2,
	"name": "Jack",
	"age": 20,
	"sex": "male",
	"birth": "2000-11-02T00:00:00"
}, {
	"id": 3,
	"name": "小花",
	"age": 19,
	"sex": "female",
	"birth": "2001-01-16T00:00:00"
}, {
	"id": 4,
	"name": "Tom",
	"age": 18,
	"sex": "male",
	"birth": "2002-04-02T00:00:00"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值