应使用.nodes()配合CROSS APPLY和.value()替代OPENXML,因后者易致内存泄漏、性能差;.nodes()生成虚拟表需APPLY访问,路径区分大小写且须精确匹配;.value()要求XPath返回单值,末尾加[1]并显式用text()或@取值;WHERE中避免直接.value(),应先用.exist()预筛并建主XML索引。

直接用 .nodes() + .value(),别碰 OPENXML
SQL Server 2005 以后,OPENXML 就该停用了。它要显式调用 sp_xml_preparedocument 和 sp_xml_removedocument,漏掉后者会内存泄漏;整个解析走临时内存树,不支持 XML 索引,性能差、并发低。现代写法全用原生 XML 方法:.nodes() 拆行集,.value() 提字段,引擎内置解析,能走索引、可估算行数、无泄漏风险。
.nodes() 路径写错或没配 CROSS APPLY 就查不到数据
.nodes() 不返回值,只生成一个带 xml 类型列的虚拟表,必须配合 CROSS APPLY(或 OUTER APPLY)才能访问。路径区分大小写,且必须精确匹配元素名——/Root/Item 和 /root/item 是两回事。常见错误:
- 直接写
SELECT * FROM @xml.nodes('/root/item')→ 报错:缺少APPLY - 路径写成
/items/item,但实际 XML 是<Items><Item>...</Item></Items>→ 返回空结果集 - 用
CROSS APPLY时路径没匹配到任何节点 → 整行被过滤掉;需保留原行就改用OUTER APPLY
.value() 报错 “requires a singleton” 或总返回 NULL
.value() 要求 XPath 必须返回单个值(或空序列),否则报错或静默返回 NULL。关键点:
- 所有路径末尾必须加
[1],例如(name/text())[1],不能只写name/text() - 取文本内容必须显式写
text(),比如(price/text())[1];不加会返回带标签的 XML 片段,类型不匹配 - 取属性要加
@前缀,@id[1],不是id[1] - 数值类字段建议套一层
TRY_CAST(... AS INT),避免空字符串转INT导致查询中断
WHERE 里直接用 .value() 就等于放弃索引
像 WHERE content.value('(/book/price)[1]', 'DECIMAL') > 49.9 这种写法,会导致全表扫描——函数包裹列无法命中 XML 索引。正确做法分两步:
- 先用
.exist()预筛:content.exist('/book[price > sql:variable("@minPrice")]') = 1(注意>要用实体编码) -
.exist()返回NULL当 XML 列为NULL,所以条件得写成content IS NOT NULL AND content.exist(...) = 1 - 想让
.exist()走索引,必须先建主 XML 索引:CREATE PRIMARY XML INDEX IX_primary ON docs(content);没它,所有次级索引都无效
最易忽略的是主索引这一步——很多人建了 PATH 索引却发现执行计划还是 Table Scan,问题就在这儿。

















