前言
JSON 已经成为现代应用数据交换的主流格式。SQL Server 2016 起就支持了 FOR JSON、OPENJSON、JSON_VALUE 等基础 JSON 功能,但构建 JSON 对象和数组 一直要依赖 FOR JSON PATH 这类写法,比较繁琐。
SQL Server 2022 新增了三个 JSON 函数,直击痛点:
| 函数 | 作用 |
|---|---|
JSON_OBJECT() |
直接用键值对构建 JSON 对象 |
JSON_ARRAY() |
直接用值列表构建 JSON 数组 |
JSON_PATH_EXISTS() |
检测 JSON 字符串中是否存在指定路径 |
本文通过完整可运行的示例,带你掌握这三个函数的用法。
准备测试数据
-- 创建员工信息表
CREATE TABLE Employees (
EmpID INT PRIMARY KEY,
Name NVARCHAR(50),
Department NVARCHAR(50),
Salary DECIMAL(10,2),
Skills NVARCHAR(200), -- 逗号分隔的技能列表
Profile NVARCHAR(MAX) -- 存储 JSON 格式的扩展信息
);
INSERT INTO Employees VALUES
(1, N'张伟', N'研发部', 18000.00, N'C#,SQL,Azure',
N'{"age":30,"city":"北京","certified":true}'),
(2, N'李娜', N'数据部', 16000.00, N'Python,SQL,Power BI',
N'{"age":28,"city":"上海","certified":false}'),
(3, N'王磊', N'研发部', 20000.00, N'Java,SQL,Docker',
N'{"age":35,"city":"深圳","certified":true}'),
(4, N'赵敏', N'运营部', 12000.00, N'Excel,SQL',
N'{"age":26,"city":"成都"}'), -- 注意:没有 certified 字段
(5, N'陈静', N'数据部', 17500.00, N'R,Python,Tableau',
N'{"age":31,"city":"杭州","certified":true}');
示例一:JSON_OBJECT() --- 构建 JSON 对象
语法:
JSON_OBJECT ( key_value_pair [, ...] [ NULL ON NULL | ABSENT ON NULL ] )
key : value用冒号分隔NULL ON NULL(默认):值为 NULL 时,输出"key": nullABSENT ON NULL:值为 NULL 时,整个键值对不出现在结果中
示例:为每位员工生成简单 JSON 名片
SELECT
EmpID,
JSON_OBJECT(
'name' : Name,
'dept' : Department,
'salary' : Salary
) AS JsonCard
FROM Employees;
结果:
| EmpID | JsonCard |
|---|---|
| 1 | {"name":"张伟","dept":"研发部","salary":18000.00} |
| 2 | {"name":"李娜","dept":"数据部","salary":16000.00} |
| 3 | {"name":"王磊","dept":"研发部","salary":20000.00} |
| 4 | {"name":"赵敏","dept":"运营部","salary":12000.00} |
| 5 | {"name":"陈静","dept":"数据部","salary":17500.00} |
示例:演示 NULL ON NULL vs ABSENT ON NULL
-- 构造一行含 NULL 值的测试数据
DECLARE @Name NVARCHAR(50) = N'测试员工';
DECLARE @Phone NVARCHAR(20) = NULL; -- 手机号未填
-- 默认 NULL ON NULL:NULL 值以 null 出现在 JSON 中
SELECT JSON_OBJECT(
'name' : @Name,
'phone' : @Phone
) AS WithNull;
-- ABSENT ON NULL:NULL 值对应的键直接省略
SELECT JSON_OBJECT(
'name' : @Name,
'phone' : @Phone
ABSENT ON NULL
) AS WithoutNull;
结果:
| 模式 | 输出 |
|---|---|
| NULL ON NULL(默认) | {"name":"测试员工","phone":null} |
| ABSENT ON NULL | {"name":"测试员工"} |
使用场景 :向前端返回数据时,通常推荐
ABSENT ON NULL,可以减少不必要的空字段,降低传输体积。
示例二:JSON_ARRAY() --- 构建 JSON 数组
语法:
JSON_ARRAY ( value [, ...] [ NULL ON NULL | ABSENT ON NULL ] )
示例:将技能字段拆分后重新组装为 JSON 数组
-- 先用已有的字符串拼接演示基本用法
SELECT
EmpID,
Name,
JSON_ARRAY(
TRIM(value) -- STRING_SPLIT 拆分出来的每项技能
) AS SkillItem
FROM Employees
CROSS APPLY STRING_SPLIT(Skills, ',');

注意:上面示例每行只输出单个技能元素(便于理解 JSON_ARRAY 用法),实际业务中通常配合子查询聚合。
示例:直接构建静态多值数组
SELECT
EmpID,
Name,
JSON_ARRAY(EmpID, Name, Department, Salary) AS InfoArray
FROM Employees;
结果:
| EmpID | Name | InfoArray |
|---|---|---|
| 1 | 张伟 | [1,"张伟","研发部",18000.00] |
| 2 | 李娜 | [2,"李娜","数据部",16000.00] |
| 3 | 王磊 | [3,"王磊","研发部",20000.00] |
| 4 | 赵敏 | [4,"赵敏","运营部",12000.00] |
| 5 | 陈静 | [5,"陈静","数据部",17500.00] |
示例:JSON_OBJECT 嵌套 JSON_ARRAY
SELECT
EmpID,
JSON_OBJECT(
'name' : Name,
'dept' : Department,
'skills' : JSON_ARRAY(
TRIM(s1.value),
TRIM(s2.value),
TRIM(s3.value)
ABSENT ON NULL
)
) AS RichCard
FROM Employees
OUTER APPLY (
SELECT value FROM STRING_SPLIT(Skills, ',') ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
) s1(value)
OUTER APPLY (
SELECT value FROM STRING_SPLIT(Skills, ',') ORDER BY (SELECT NULL) OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
) s2(value)
OUTER APPLY (
SELECT value FROM STRING_SPLIT(Skills, ',') ORDER BY (SELECT NULL) OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY
) s3(value);
两个函数可以自由嵌套,轻松构造复杂 JSON 结构:
结果示例:

示例三:JSON_PATH_EXISTS() --- 检测 JSON 路径是否存在
语法:
JSON_PATH_EXISTS ( json_expression , sql_json_path )
返回 1(路径存在)或 0(路径不存在),若输入 JSON 无效或为 NULL 则返回 NULL。
示例:找出 Profile 中含有 "certified" 字段的员工
SELECT
EmpID,
Name,
Profile,
JSON_PATH_EXISTS(Profile, '$.certified') AS HasCertified
FROM Employees;
结果:
| EmpID | Name | Profile | HasCertified |
|---|---|---|---|
| 1 | 张伟 | {"age":30,"city":"北京","certified":true} | 1 |
| 2 | 李娜 | {"age":28,"city":"上海","certified":false} | 1 |
| 3 | 王磊 | {"age":35,"city":"深圳","certified":true} | 1 |
| 4 | 赵敏 | {"age":26,"city":"成都"} | 0 |
| 5 | 陈静 | {"age":31,"city":"杭州","certified":true} | 1 |
赵敏的 Profile 里没有
certified字段,返回0;其余都有,返回1。
示例:过滤 + 结合 WHERE 子句
-- 只查有认证信息 且 已认证的员工
SELECT
EmpID,
Name,
JSON_VALUE(Profile, '$.certified') AS IsCertified
FROM Employees
WHERE JSON_PATH_EXISTS(Profile, '$.certified') = 1
AND JSON_VALUE(Profile, '$.certified') = 'true';
结果:
| EmpID | Name | IsCertified |
|---|---|---|
| 1 | 张伟 | true |
| 3 | 王磊 | true |
| 5 | 陈静 | true |
示例:嵌套路径检测
-- 假设部分员工 Profile 有嵌套结构
UPDATE Employees
SET Profile = N'{"age":30,"city":"北京","certified":true,"contact":{"email":"zhangwei@example.com"}}'
WHERE EmpID = 1;
-- 检测嵌套路径是否存在
SELECT
EmpID,
Name,
JSON_PATH_EXISTS(Profile, '$.contact.email') AS HasEmail,
JSON_PATH_EXISTS(Profile, '$.contact.phone') AS HasPhone
FROM Employees
WHERE EmpID = 1;
结果:
| EmpID | Name | HasEmail | HasPhone |
|---|---|---|---|
| 1 | 张伟 | 1 | 0 |
与旧版写法对比
构建 JSON 对象
旧写法(SQL Server 2016~2019):
-- 方法一:FOR JSON PATH(需要 SELECT 子查询)
SELECT (
SELECT Name AS name, Department AS dept, Salary AS salary
FROM Employees
WHERE EmpID = 1
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) AS JsonCard;
-- 方法二:字符串拼接(最原始,容易出错)
SELECT
'{"name":"' + Name + '","dept":"' + Department + '"}'
FROM Employees;
新写法(SQL Server 2022):
SELECT
JSON_OBJECT('name': Name, 'dept': Department, 'salary': Salary)
FROM Employees;
对比总结:
| 维度 | 旧写法(FOR JSON PATH) | 新写法(JSON_OBJECT) |
|---|---|---|
| 代码量 | 多,需要子查询 | 少,内联即可 |
| 可读性 | 较低 | 直观清晰 |
| 嵌套支持 | 麻烦 | 直接嵌套调用 |
| NULL 处理 | 不灵活 | 支持 ABSENT ON NULL |
检测 JSON 路径
旧写法:
-- 用 JSON_VALUE 返回 NULL 来"间接"判断路径是否存在,有歧义
-- (NULL 可能是路径不存在,也可能是路径存在但值本身是 null)
SELECT *
FROM Employees
WHERE JSON_VALUE(Profile, '$.certified') IS NOT NULL;
新写法:
-- JSON_PATH_EXISTS 语义明确,无歧义
SELECT *
FROM Employees
WHERE JSON_PATH_EXISTS(Profile, '$.certified') = 1;
旧写法的致命缺陷:如果 JSON 中
"certified": null,JSON_VALUE返回 NULL,会被误判为路径不存在。JSON_PATH_EXISTS彻底解决了这个问题。
兼容性说明
| 功能 | 最低版本 |
|---|---|
JSON_OBJECT() |
SQL Server 2022(16.x) |
JSON_ARRAY() |
SQL Server 2022(16.x) |
JSON_PATH_EXISTS() |
SQL Server 2022(16.x) |
| Azure SQL Database | 已支持(同步更新) |
| Azure SQL Managed Instance | 已支持 |
| SQL Server 2019 及更早 | 不支持,需用 FOR JSON / 字符串拼接替代 |
如果生产环境还在使用 SQL Server 2019,可用
FOR JSON PATH+ 子查询实现相近效果,但语法相对繁琐。
总结
SQL Server 2022 的三个新 JSON 函数让 JSON 构建和查询更加简洁直观:
-
JSON_OBJECT():用键值对语法直接构造 JSON 对象,支持ABSENT ON NULL控制空值输出,可自由嵌套。 -
JSON_ARRAY():用值列表直接构造 JSON 数组,同样支持ABSENT ON NULL,与JSON_OBJECT()嵌套使用可构建任意复杂的 JSON 结构。 -
JSON_PATH_EXISTS():精准判断 JSON 路径是否存在,返回 0/1,语义清晰,彻底解决了旧版用JSON_VALUE IS NOT NULL判断带来的歧义问题。
这三个函数在实际项目中的典型使用场景:
- API 数据封装:直接在 SQL 层组装 JSON 返回给接口,减少应用层代码
- 动态配置查询 :用
JSON_PATH_EXISTS先检测字段再读取,避免报错 - 数据迁移转换:将关系型数据直接转为 JSON 格式写入文档型存储
升级到 SQL Server 2022 后,建议优先用这三个函数替换项目中的 FOR JSON PATH 拼接写法,代码会更简洁、可维护性更强。