适用于:SQL Server
Azure SQL 数据库
Azure SQL 托管实例
Microsoft Fabric 中的 SQL 数据库
本文介绍使用 xml 数据类型方法的指南。
PRINT 语句
不能在PRINT语句中使用 xml 数据类型方法,如以下示例所示。
xml 数据类型方法被视为子查询,并且不允许在PRINT语句中查询子查询。 因此,下面的示例将返回一个错误:
DECLARE @x XML
SET @x = '<root>Hello</root>'
PRINT @x.value('/root[1]', 'varchar(20)') -- will not work because this is treated as a subquery (select top 1 col from table)
一种解决方案是先将 value() 方法的结果分配给一个 xml 类型的变量,然后在查询中指定该变量。
DECLARE @x XML
DECLARE @c VARCHAR(max)
SET @x = '<root>Hello</root>'
SET @c = @x.value('/root[1]', 'VARCHAR(11)')
PRINT @c
GROUP BY 子句
xml 数据类型方法在内部视为子查询来处理。 由于GROUP BY需要标量且不允许聚合和子查询,因此不能在子句中指定 GROUP BY 数据类型方法。 另一种解决方案是调用用户定义函数,然后在其内部使用 XML 方法。
报告错误
报告错误时,xml 数据类型方法会引发一个如下格式的错误:
Msg errorNumber, Level levelNumber, State stateNumber:
XQuery [database.table.method]: description_of_error
例如:
Msg 2396, Level 16, State 1:
XQuery [xmldb_test.xmlcol.query()]: Attribute may not appear outside of an element
注释
XQuery 解析器引发的解析错误(例如作为 XML 数据类型方法一部分引用的 XML 中的语法错误)会中止当前事务,无论当前会话的设置如何 XACT_ABORT 。
单一实例检查
如果编译器无法确定在运行时是否保证单一实例,则需要单一实例的位置步骤、函数参数和运算符将返回错误。 此问题经常出现在非类型化数据上。 例如,查找属性时就需要使用单一的父元素。 通过一个用来选择单个父节点的序号即可满足此要求。 用于提取属性值的 node()-value() 组合的计算可能不需要序号规范。 如下例所示。
示例:已知单一实例
在此示例中,nodes() 方法为每个 元素生成一个单独的行。 对 节点进行计算的 value() 方法提取 <book> 值,并且是单一属性。
SELECT nref.value('@genre', 'VARCHAR(max)') LastName
FROM T CROSS APPLY xCol.nodes('//book') AS R(nref)
XML 架构用于对类型化的 XML 进行类型检查。 如果将某个节点指定为 XML 架构中单一的节点,则编译器将使用该信息,并且不会发生任何错误。 否则,需要使用一个用来选择单个节点的序号。 具体而言,使用 descendant-or-self 轴 (//) 轴(例如在 /book//title 中)会丢失 <title> 元素的单一性基数推理,即使 XML 架构指定其如此。 因此,应将其重写为 (/book//title)[1]。
请务必了解类型检查之间的差异 //first-name[1] 和 (//first-name)[1] 类型检查。 前者返回一组 <first-name> 节点,其中每个节点都是其同级节点中最左侧的 <first-name> 节点。 后者返回 XML 实例中按文档顺序排列的第一个单一的 <first-name> 节点。
示例:使用 value()
对非类型化的 XML 列的以下查询会导致静态编译错误。 这是因为 value() 需要单一实例节点作为第一个参数,编译器无法确定在运行时是否只发生一个 <last-name> 节点:
SELECT xCol.value('//author/last-name', 'NVARCHAR(50)') LastName
FROM T
可以考虑下面的解决办法:
SELECT xCol.value('//author/last-name[1]', 'NVARCHAR(50)') LastName
FROM T
但是,此解决方案无法解决此错误,因为每个 XML 实例中可能会发生多个 <author> 节点。 采用下面的重写代码可以解决问题:
SELECT xCol.value('(//author/last-name/text())[1]', 'NVARCHAR(50)') LastName
FROM T
此查询返回每个 XML 实例中第一个 <last-name> 元素的值。