SQL Server中嵌套数据首选FOR XML PATH+子查询,因其稳定可控;JSON需JSON_QUERY配合子查询生成,FOR JSON无法自动推导多层关系;XML须加TYPE防转义,JSON需INCLUDE_NULL_VALUES处理NULL。

JSON_QUERY 和 JSON_VALUE 配合字符串拼接或子查询,不能靠单条 FOR JSON 自动推导多层关系。
用 FOR XML PATH('') + 相关子查询构造父子嵌套
这是生成订单含明细、部门含员工等一对多嵌套 XML 的主力方案。关键在于外层查父表,内层用相关子查询返回 XML 类型节点,并加 TYPE 防止被转义成字符串。
-
TYPE必须加在子查询的FOR XML末尾,否则返回的是 NVARCHAR,会把尖括号自动转义为和 <code>> - 子查询中字段别名决定 XML 元素名:例如
d.Quantity AS '@qty'生成属性,d.ProductName AS 'product_name'生成子元素 - 若需文本节点(如
<name>John</name>),用AS [text()],注意方括号和引号 - 外层
FOR XML PATH('order')控制每个父记录的包装标签,ROOT('orders')补根节点——不加 ROOT 时结果是片段,没有顶层容器
示例:订单及其明细(含属性+子元素混合)
SELECT
o.OrderID AS '@id',
o.OrderDate AS 'order_date',
(SELECT
d.ProductID AS '@product_id',
d.UnitPrice AS 'unit_price',
d.Quantity AS 'quantity'
FROM OrderDetails d
WHERE d.OrderID = o.OrderID
FOR XML PATH('detail'), TYPE) AS 'details'
FROM Orders o
FOR XML PATH('order'), ROOT('orders');
FOR XML AUTO + ELEMENTS 适合快速扁平嵌套,但控制力弱
当只需简单“主表→从表”一层嵌套,且不介意元素名由表别名决定时,FOR XML AUTO, ELEMENTS 写法最短。但它不支持自定义层级深度、无法重命名元素、也不能混用属性与元素。
- 必须显式列出字段,禁用
*——否则同名列(如两个表都有ID)会导致 XML 生成失败或字段丢失 - 表别名即 XML 外层元素名:
Orders o JOIN OrderDetails d→ 生成<o><d>...</d></o> -
ELEMENTS让所有字段变为子元素;不加则默认作为属性(仅限非空值),且无法控制哪些该是属性、哪些该是元素 - 无法表达“一对多中的多”为同级多个节点(如多个
<detail></detail>),它会强制合并为一个<d></d>下的重复子元素,语义模糊
示例(仅适用于极简场景):
SELECT
o.OrderID,
o.OrderDate,
d.ProductID,
d.Quantity
FROM Orders o
JOIN OrderDetails d ON d.OrderID = o.OrderID
FOR XML AUTO, ELEMENTS, ROOT('result');
SQL Server 2016+ 中用 JSON 函数拼嵌套结构,不是靠 FOR JSON
FOR JSON 只能处理直接 JOIN 关系,无法生成“订单→客户信息→地址→城市”这种跨三张表的嵌套。真要嵌套 JSON,得用 JSON_QUERY 包裹子查询结果,并确保子查询本身返回合法 JSON 字符串。
- 子查询必须用
FOR JSON PATH生成字符串,再用JSON_QUERY()封装,否则会被当成普通字符串加引号(变成"[{\"id\":1}]"而非[{"id":1}]) -
JSON_VALUE只能取标量,JSON_QUERY才能取对象或数组——嵌套结构必须用后者 - 外层
SELECT中不能对JSON_QUERY结果再做字符串拼接,否则破坏 JSON 结构;所有字段需统一走FOR JSON PATH或手动构造 - NULL 值默认不输出字段,加
INCLUDE_NULL_VALUES才生成"field": null
示例:订单 + 内联客户信息(客户数据来自子查询)
SELECT
o.OrderID,
o.OrderDate,
JSON_QUERY((
SELECT c.CustomerName, c.Email
FROM Customers c
WHERE c.CustomerID = o.CustomerID
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)) AS customer
FROM Orders o
FOR JSON PATH, ROOT('orders');
容易忽略的字符与类型陷阱
XML 和 JSON 对特殊字符、NULL、编码的处理逻辑不同,同一份数据输出两种格式时行为不一致。
-
FOR XML默认将&、、<code>>转义;FOR JSON不转义,但要求输入字符串本身是合法 UTF-16(SQL Server 内部编码),含 BOM 或非法 surrogate pair 会报错 -
NULL在FOR XML中默认不生成节点;加XSI:NIL或用ISNULL(col, '')控制;FOR JSON默认跳过 NULL 字段,加INCLUDE_NULL_VALUES才保留 - 日期时间字段:XML 输出为
2025-12-29T10:30:00格式;JSON 输出为字符串"2025-12-29T10:30:00",但若用JSON_VALUE提取,需额外转换类型 - 大对象(如
NVARCHAR(MAX)含长文本)在嵌套子查询中可能触发隐式截断——XML 模式下限制更松,JSON 模式下某些函数(如JSON_MODIFY)对长度敏感
真正复杂的嵌套报告,别指望一条语句全自动搞定;先分层验证子查询是否返回预期结构,再逐层组装,比调试一整块嵌套更容易定位问题。


















