使用内置函数 (SQL Server) 验证、查询和更改 JSON 数据
适用范围:SQL Server 2016 (13.x) 及更高版本 Azure SQL 数据库 Azure SQL 托管实例
JSON 的内置支持包括本文简要介绍的下列内置函数。
- ISJSON 测试字符串是否包含有效 JSON。
- JSON_VALUE 从 JSON 字符串中提取标量值。
- JSON_QUERY 从 JSON 字符串中提取对象或数组。
- JSON_MODIFY 更新 JSON 字符串中属性的值,并返回已更新的 JSON 字符串。
对于所有 JSON 函数,请查看 JSON 函数。
本文中的 Transact-SQL 代码示例使用 AdventureWorks2022
或 AdventureWorksDW2022
示例数据库,可从 Microsoft SQL Server 示例和社区项目主页下载它。
此页上的示例 JSON 文本
此页上的示例使用与以下示例中所示内容类似的 JSON 文本:
{
"id": "DesaiFamily",
"parents": [
{ "familyName": "Desai", "givenName": "Prashanth" },
{ "familyName": "Miller", "givenName": "Helen" }
],
"children": [
{
"familyName": "Desai",
"givenName": "Jesse",
"gender": "female",
"grade": 1,
"pets": [
{ "givenName": "Goofy" },
{ "givenName": "Shadow" }
]
},
{
"familyName": "Desai",
"givenName": "Lisa",
"gender": "female",
"grade": 8
}
],
"address": {
"state": "NY",
"county": "Manhattan",
"city": "NY"
},
"creationDate": 1431620462,
"isRegistered": false
}
此 JSON 文档包含嵌套的复杂元素,存储在下面的示例表中:
CREATE TABLE Families (
id INT identity CONSTRAINT PK_JSON_ID PRIMARY KEY,
[doc] NVARCHAR(MAX)
);
无论 JSON 文档以 varchar、nvarchar 还是本机 json 数据类型存储,JSON 函数的工作方式均相同。
使用 ISJSON 函数验证 JSON 文本
ISJSON
函数测试字符串是否包含有效 JSON。
下面的示例将返回 JSON 列包含有效 JSON 文本的行。 如果没有显式 JSON 约束,则可在 nvarchar 列中输入任意文本:
SELECT *
FROM Families
WHERE ISJSON(doc) > 0;
有关详细信息,请参阅 ISJSON。
使用 JSON_VALUE 函数从 JSON 文本中提取值
JSON_VALUE
函数从 JSON 字符串中提取标量值。 下面的查询会返回其中 id
JSON 字段与值 DesaiFamily
匹配的文档,按 city
和 state
JSON 字段排序:
SELECT JSON_VALUE(f.doc, '$.id') AS Name,
JSON_VALUE(f.doc, '$.address.city') AS City,
JSON_VALUE(f.doc, '$.address.county') AS County
FROM Families f
WHERE JSON_VALUE(f.doc, '$.id') = N'DesaiFamily'
ORDER BY JSON_VALUE(f.doc, '$.address.city') DESC,
JSON_VALUE(f.doc, '$.address.state') ASC
此查询结果显示在下表中:
名称 | City | 县 |
---|---|---|
DesaiFamily |
NY |
Manhattan |
有关详细信息,请参阅 JSON_VALUE。
使用 JSON_QUERY 函数从 JSON 文本中提取对象或数组
JSON_QUERY
函数从 JSON 字符串中提取对象或数组。 下面的示例演示了如何在查询结果中返回 JSON 片段。
SELECT JSON_QUERY(f.doc, '$.address') AS Address,
JSON_QUERY(f.doc, '$.parents') AS Parents,
JSON_QUERY(f.doc, '$.parents[0]') AS Parent0
FROM Families f
WHERE JSON_VALUE(f.doc, '$.id') = N'DesaiFamily';
此查询结果显示在下表中:
Address | 父项 | Parent0 |
---|---|---|
{ "state": "NY", "county": "Manhattan", "city": "NY" } |
[ { "familyName": "Desai", "givenName": "Prashanth" }, { "familyName": "Miller", "givenName": "Helen" } ] |
{ "familyName": "Desai", "givenName": "Prashanth" } |
有关详细信息,请参阅 JSON_QUERY。
分析嵌套式 JSON 集合
通过 OPENJSON
函数,可将 JSON 子数组转换为行集,然后将其与父元素联接在一起。 例如,可返回所有家庭文档,并将其与存储为内部 JSON 数组的 children
对象“联接”起来:
SELECT JSON_VALUE(f.doc, '$.id') AS Name,
JSON_VALUE(f.doc, '$.address.city') AS City,
c.givenName,
c.grade
FROM Families f
CROSS APPLY OPENJSON(f.doc, '$.children') WITH (
grade INT,
givenName NVARCHAR(100)
) c
此查询结果显示在下表中:
名称 | 城市 | givenName | 年级 |
---|---|---|---|
DesaiFamily |
NY |
Jesse |
1 |
DesaiFamily |
NY |
Lisa |
8 |
将返回两行,因为一个父行与通过分析子级子数组的两个元素产生的两个子行联接在一起。 OPENJSON
函数分析 doc
列中的 children
片段,并返回每个元素中的 grade
和 givenName
作为一个行集。 此行集可以与父文档进行联接。
查询嵌套式分层 JSON 子数组
可应用多个 CROSS APPLY OPENJSON
调用以查询嵌套式 JSON 结构。 本示例中使用的 JSON 文档具有名为 children
的嵌套数组,其中每个子级都有 pets
的嵌套数组。 下面的查询会分析每个文档中的子级,将每个数组对象作为行返回,然后分析 pets
数组:
SELECT c.familyName,
c.givenName AS childGivenName,
p.givenName AS petName
FROM Families f
CROSS APPLY OPENJSON(f.doc) WITH (
familyName NVARCHAR(100),
children NVARCHAR(MAX) AS JSON
) AS a
CROSS APPLY OPENJSON(children) WITH (
familyName NVARCHAR(100),
givenName NVARCHAR(100),
pets NVARCHAR(max) AS JSON
) AS c
OUTER APPLY OPENJSON(pets) WITH (givenName NVARCHAR(100)) AS p;
首次进行 OPENJSON
调用会使用 AS JSON 子句返回 children
数组的片段。 此数组片段将提供给第二个 OPENJSON
函数,其将返回 givenName
、每个子级的 firstName
以及 pets
的数组。 pets
的数组将提供给第三个 OPENJSON
函数,其返回宠物的 givenName
。
此查询结果显示在下表中:
familyName | childGivenName | petName |
---|---|---|
Desai |
Jesse |
Goofy |
Desai |
Jesse |
Shadow |
Desai |
Lisa |
NULL |
根文档与两个 children
行进行了联接,这两个行由生成两个行(或元组)的首次 OPENJSON(children)
调用返回。 然后,使用 OUTER APPLY
运算符将每行与由 OPENJSON(pets)
生成的新行进行联接。 Jesse 有两只宠物,因此 (Desai, Jesse)
与为 Goofy
和 Shadow
生成的两行进行了联接。 Lisa 没有宠物,因此 OPENJSON(pets)
没有为该元组返回的任何行。 但是,由于我们使用的是 OUTER APPLY
,因此我们将在列中得到 NULL
。 如果使用 CROSS APPLY
而不是 OUTER APPLY
,则不会在结果中返回 Lisa,因为没有可以与该元组联接的任何宠物行。
对比 JSON_VALUE 与 JSON_QUERY
JSON_VALUE
和 JSON_QUERY
之间的主要区别在于 JSON_VALUE
返回标量值,而 JSON_QUERY
返回对象或数组。
请参考以下示例 JSON 文本。
{
"a": "[1,2]",
"b": [1, 2],
"c": "hi"
}
在此示例 JSON 文本中,数据成员“a”和“c”是字符串值,而数据成员“b”是数组。 JSON_VALUE
和 JSON_QUERY
会返回以下结果:
路径 | JSON_VALUE 返回 |
JSON_QUERY 返回 |
---|---|---|
$ |
NULL 或错误 |
{ "a": "[1,2]", "b": [1, 2], "c": "hi" } |
$.a |
[1,2] |
NULL 或错误 |
$.b |
NULL 或错误 |
[1,2] |
$.b[0] |
1 |
NULL 或错误 |
$.c |
hi |
NULL 或错误 |
使用 AdventureWorks 示例数据库测试 JSON_VALUE 和 JSON_QUERY
通过使用 AdventureWorks2022
示例数据库运行以下示例,对本文中所述的内置函数进行测试。 有关如何通过运行脚本添加 JSON 数据进行测试的更多信息,请参阅测试驱动内置的 JSON 支持。
在下面的示例中,SalesOrder_json
表中的 Info
列包含了 JSON 文本。
示例 1 - 返回标准列和 JSON 数据
下面的查询返回将返回标准关系列以及 JSON 列的值。
SELECT SalesOrderNumber,
OrderDate,
Status,
ShipDate,
AccountNumber,
TotalDue,
JSON_QUERY(Info, '$.ShippingInfo') ShippingInfo,
JSON_QUERY(Info, '$.BillingInfo') BillingInfo,
JSON_VALUE(Info, '$.SalesPerson.Name') SalesPerson,
JSON_VALUE(Info, '$.ShippingInfo.City') City,
JSON_VALUE(Info, '$.Customer.Name') Customer,
JSON_QUERY(OrderItems, '$') OrderItems
FROM Sales.SalesOrder_json
WHERE ISJSON(Info) > 0;
示例 2 - 聚合和筛选 JSON 值
下面的查询将按客户名称(存储在 JSON 中)和状态(存储在普通列中)来汇总小计。 然后将按市/县(存储在 JSON 中)和 OrderDate(存储在普通列中)来筛选结果。
DECLARE @territoryid INT;
DECLARE @city NVARCHAR(32);
SET @territoryid = 3;
SET @city = N'Seattle';
SELECT JSON_VALUE(Info, '$.Customer.Name') AS Customer,
Status,
SUM(SubTotal) AS Total
FROM Sales.SalesOrder_json
WHERE TerritoryID = @territoryid
AND JSON_VALUE(Info, '$.ShippingInfo.City') = @city
AND OrderDate > '1/1/2015'
GROUP BY JSON_VALUE(Info, '$.Customer.Name'),
Status
HAVING SUM(SubTotal) > 1000;
使用 JSON_MODIFY 函数更新 JSON 文本中的属性值
JSON_MODIFY
函数会更新 JSON 字符串中属性的值,并返回已更新的 JSON 字符串。
以下示例将更新包含 JSON 的变量中的 JSON 属性的值。
SET @info = JSON_MODIFY(@jsonInfo, '$.info.address[0].town', 'London');
有关详细信息,请参阅 JSON_MODIFY。