xml 数据类型方法的使用准则

本主题介绍 xml 数据类型方法的使用指南。

xml 数据类型方法不能用于 PRINT 语句,如下面的示例所示。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 方法。

报告错误

报告错误时,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

单一性检查

如果编译器无法确定在运行时能否确保单一性,则具有单一性要求的位置步骤、函数参数和运算符将返回错误。此问题经常出现在非类型化数据上。例如,查找属性时就需要使用单一的父元素。通过一个用来选择单个父节点的序号即可满足此要求。而在计算 node()-value() 组合以提取属性值时可能不需要指定序号,如下例所示。

示例:已知单一性

在此示例中,nodes() 方法为每个 <book> 元素生成一个单独的行。对 <book> 节点进行计算的 value() 方法提取 @genre 值,其作为属性,具有单一性。

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> 元素的值。

请参阅

概念

其他资源