如果要将 xml 数据类型实例拆分为关系数据,则 nodes() 方法非常有用。 它允许您标识将映射到新行的节点。
每个 xml 数据类型实例都有隐式提供的上下文节点。 对于在列或变量中存储的 XML 实例来说,它是文档节点。 文档节点是位于每个 xml 数据类型实例顶部的隐式节点。
nodes() 方法的结果是一个包含原始 XML 实例的逻辑副本的行集。 在这些逻辑副本中,每个行示例的上下文节点都被设置成由查询表达式标识的节点之一。这样,后续的查询可以浏览与这些上下文节点相关的节点。
您可以从行集中检索多个值。 例如,可以将 value() 方法应用于 nodes() 所返回的行集,从原始 XML 实例中检索多个值。 注意,当 value() 方法应用于 XML 实例时,它仅返回一个值。
语法
nodes (XQuery) as Table(Column)
参数注释
例如,假设有下表:
T (ProductModelID int, Instructions xml)
表中存储了以下生产说明文档。 仅显示一个片段。 注意,文档中有三个生产位置。
...
...
...
...
...
带有查询表达式 /root/Location 的 nodes() 方法调用将返回一个行集,其中包含三行(每行都有一个原始 XML 文档的逻辑副本),并且上下文项设置为 节点之一:
Product
ModelID Instructions
----------------------------------
1
1
1
然后,可以使用 xml 数据类型方法查询此行集。 以下查询为每个生成的行提取上下文项的子树:
SELECT T2.Loc.query('.')
FROM T
CROSS APPLY Instructions.nodes('/root/Location') as T2(Loc)
结果如下:
ProductModelID Instructions
----------------------------------
1
1
1
注意,返回的行集已对类型信息进行了维护。 可以将 xml 数据类型方法(例如 query()、value()、exist() 和 nodes())应用于 nodes() 方法的结果。 但是,不能将 modify() 方法用于修改 XML 实例。
另外,行集中的上下文节点无法具体化。 即,无法在 SELECT 语句中使用此节点。 但是,可以在 IS NULL 和 COUNT(*) 中使用它。
使用 nodes() 方法的情况与使用 OPENXML (Transact-SQL) 的情况相同。 这就提供了 XML 的行集视图。 但是,当您在包含 XML 文档的若干行的表中使用 nodes() 方法时,无需使用游标。
注意,由 nodes() 方法返回的行集是未命名的行集。 因此,必须通过使用别名来显式命名。
nodes() 函数不能直接应用于用户定义函数的结果。 若要将 nodes() 函数用于标量用户定义函数的结果,可以将该用户定义函数的结果分配给一个变量,也可以使用派生表为该用户定义函数的返回值分配一个列别名,然后使用 CROSS APPLY 从该别名中选择。
以下示例显示了使用 CROSS APPLY 从用户定义函数结果中进行选择的一种方式。
USE AdventureWorks;
GO
CREATE FUNCTION XTest()
RETURNS xml
AS
BEGIN
RETURN ' ';
END;
GO
SELECT A2.B.query('.')
FROM
(SELECT dbo.XTest()) AS A1(X)
CROSS APPLY X.nodes('.') A2(B);
GO
DROP FUNCTION XTest;
GO
示例针对 xml 类型的变量使用 nodes() 方法
在以下示例中,XML 文档具有一个 顶级元素和三个 子元素。 查询使用 nodes() 方法设置单独的上下文节点,每个 元素一个上下文节点。 nodes() 方法返回包含三行的行集。 每行都有一个原始 XML 的逻辑副本,其中每个上下文节点都标识原始文档中的一个不同的 元素。
然后,查询会从每行返回上下文节点:
DECLARE @x xml
SET @x='
Larry some text
moe
'
SELECT T.c.query('.') AS result
FROM @x.nodes('/Root/row') T(c)
GO
结果如下: 在此示例中,查询方法返回上下文项及其内容:
Larry some text
moe
对上下文节点应用父级取值函数将返回所有三行的 元素。
SELECT T.c.query('..') AS result
FROM @x.nodes('/Root/row') T(c)
go
结果如下:
Larry some text
moe
Larry some text
moe
Larry some text
moe
针对 xml 类型的列指定 nodes() 方法
此示例中使用了自行车生产说明,该说明存储在 ProductModel 表的 Instructions xml 类型列中。
在以下示例中,已对 ProductModel 表中 xml 类型的 Instructions 列指定 nodes() 方法。
nodes() 方法通过指定 /MI:root/MI:Location 路径,将 元素设置为上下文节点。 结果行集包括原始文档的逻辑副本,每个副本对应文档中的一个 节点,上下文节点设置为 元素。 因此,nodes() 函数提供一组 上下文节点。
query() 方法对此行集请求 self::node,从而在每行中返回 元素。
在此示例中,查询在特定产品样式的生产说明文档中将每一个 元素都设置为上下文节点。 您可以使用这些上下文节点来按照以下方式来检索值:
在 query() 方法中,此查询返回上下文项,其中指定了 self::node() 的缩写语法 '.'。
注意以下事项:
将 nodes() 应用于另一 nodes() 方法返回的行集
以下代码查询 XML 文档,以获得 ProductModel 表的 Instructions 列中的生产说明。 此查询返回包含产品样式 ID、生产位置和生产步骤的行集。
注意以下事项:
SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
/MI:root/MI:Location') as T1(Locations)
CROSS APPLY T1.Locations.nodes('
declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
./MI:step ') as T2(steps)
WHERE ProductModelID=7
GO
结果如下:
ProductModelID LocID Step
----------------------------
7 10
7 10
...
7 20
7 20
7 20
...
该查询声明 MI 前缀两次。 替代方法为,使用 WITH XMLNAMESPACES 声明前缀一次,便可将其用于查询:
WITH XMLNAMESPACES (
'https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions' AS MI)
SELECT ProductModelID, Locations.value('./@LocationID','int') as LocID,
steps.query('.') as Step
FROM Production.ProductModel
CROSS APPLY Instructions.nodes('
/MI:root/MI:Location') as T1(Locations)
CROSS APPLY T1.Locations.nodes('
./MI:step ') as T2(steps)
WHERE ProductModelID=7
GO
请参阅概念
使用 WITH XMLNAMESPACES 将命名空间添加到查询
创建 XML 数据的实例
其他资源
XML 数据类型方法