错误模型
语法不正确的 Xquery 表达式和 XML DML 语句会返回编译错误。编译阶段会检查 XQuery 表达式和 DML 语句的静态类型正确性,并针对类型化的 XML 使用 XML 架构进行类型推理。如果表达式在运行时由于类型安全冲突而失败,会引起静态类型错误。静态错误的示例包括将字符串添加到整数,以及在不存在的节点中查询类型化的数据。
与 W3C 标准有所不同的是,XQuery 运行时错误被转换为空序列。这些序列根据调用上下文,可以作为空 XML 或 NULL 传播到查询结果。
通过显式转换为正确的类型,用户可以解决静态错误的问题,尽管运行时转换错误将被转换为空序列。
下列部分详细介绍了类型检查。
单一性检查
如果编译器无法确定是否在运行时保证单一性,则要求单一性的位置步骤、函数参数和运算符将返回错误。此问题经常出现在非类型化数据上。例如,对属性的查找需要单一的父元素。选择单个父节点的序号即可满足需要。计算 node()-value() 组合以提取属性值可能不需要指定序号。如以下示例中所示。
示例:已知单一性
在此示例中,nodes() 方法为每个 <book> 元素生成一个单独的行。对 <book> 节点进行计算的 value() 方法提取 @genre 值,其作为属性,具有单一性。
复制代码
SELECT nref.value('@genre', 'varchar(max)') LastNameFROM 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)') LastNameFROM T
可以考虑下面的解决办法:
复制代码
SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastNameFROM T
但是,该解决办法不解决错误,因为在每个 XML 实例中可能会有多个 <author> 节点。采用下面的重写代码可以解决问题:
复制代码
SELECT xCol.value('(//author/last-name/text())[1]', 'nvarchar(50)') LastNameFROM T
此查询返回每个 XML 实例中第一个 <last-name> 元素的值。
parent 轴
如果无法确定节点的类型,它将成为 anyType。这不会隐式转换为任何其他类型。在使用 parent 轴(如 xCol.query('/book/@genre/../price'))进行导航的过程中,尤其会发生这种情况。父节点类型确定为 anyType。在 XML 架构中,也可以将元素定义为 anyType。在这两种情况下,丢失更为精确的类型信息经常会导致发生静态类型错误,并需要将原子值显式转换为其特定类型。
Data()、text() 和 string() 取值函数
XQuery 有一个从节点提取类型化标量值的函数 fn:data()、一个返回文本节点的节点测试 text(),以及一个返回节点的字符串值的函数 fn:string()。它们的用法容易混淆。以下是在 SQL Server 2005 中正确使用它们的准则。使用 XML 实例 <age>12</age> 进行说明。
非类型化的 XML:路径表达式 /age/text() 返回文本节点“12”。函数 fn:data(/age) 返回字符串值“12”,fn:string(/age) 也是如此。
类型化的 XML:对于任何简单的类型化的 <age> 元素,表达式 /age/text() 都返回静态错误。另一方面,fn:data(/age) 返回整数 12。fn:string(/age) 产生字符串“12”。
联合类型的函数和运算符
由于类型检查,联合类型要求进行小心地处理。下列示例中说明了其中两个问题。