nodes() 方法(xml 数据类型)
适用于: SQL Server
Azure SQL 数据库
Azure SQL 托管实例
如果要将 xml 数据类型实例拆分为关系数据,则 nodes() 方法非常有用 。 它允许您标识将映射到新行的节点。
每个 xml 数据类型实例都有隐式提供的上下文节点。 对于在列或变量中存储的 XML 实例来说,此节点是文档节点。 文档节点是位于每个 xml 数据类型实例顶部的隐式节点。
nodes() 方法的结果是一个包含原始 XML 实例的逻辑副本的行集。 在这些逻辑副本中,每个行示例的上下文节点都被设置成由查询表达式标识的节点之一。 这样,后续的查询可以浏览与这些上下文节点相关的节点。
您可以从行集中检索多个值。 例如,可以将 value() 方法应用于 nodes() 所返回的行集,从原始 XML 实例中检索多个值 。 当 value() 方法应用于 XML 实例时,它仅返回一个值。
语法
nodes (XQuery) as Table(Column)
参数
XQuery
字符串文字,即一个 XQuery 表达式。 如果查询表达式构造节点,这些已构造的节点将在结果行集中显示。 如果查询表达式生成一个空序列,则行集也为空。 如果查询表达式静态生成一个包含原子值而不是节点的序列,将产生静态错误。
Table(Column)
结果行集的表名称和列名称。
备注
例如,假设有下表:
T (ProductModelID INT, Instructions XML)
表中存储了以下生产说明文档。 仅显示一个片段。 注意,文档中有三个生产位置。
<root>
<Location LocationID="10"...>
<step>...</step>
<step>...</step>
...
</Location>
<Location LocationID="20" ...>
...
</Location>
<Location LocationID="30" ...>
...
</Location>
</root>
带有查询表达式 nodes()
的 /root/Location
方法调用将返回一个行集,其中包含三行(每行都有一个原始 XML 文档的逻辑副本),并且上下文项设置为 <Location>
节点之一:
Product
ModelID Instructions
----------------------------------
1 <root><Location LocationID="10" ... />
<Location LocationID="20" ... />
<Location LocationID="30" .../></root>
1 <root><Location LocationID="10" ... />
<Location LocationID="20" ... />
<Location LocationID="30" .../></root>
1 <root><Location LocationID="10" ... />
<Location LocationID="20" ... />
<Location LocationID="30" .../></root>
然后,可以使用 xml 数据类型方法查询此行集。 以下查询为每个生成的行提取上下文项的子树:
SELECT T2.Loc.query('.')
FROM T
CROSS APPLY Instructions.nodes('/root/Location') AS T2(Loc)
下面是结果:
ProductModelID Instructions
----------------------------------
1 <Location LocationID="10" ... />
1 <Location LocationID="20" ... />
1 <Location LocationID="30" .../>
返回的行集已对类型信息进行了维护。 可以将 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 '<document/>';
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 文档具有一个 <Root
> 顶级元素和三个 <row
> 子元素。 查询使用 nodes()
方法设置单独的上下文节点,每个 <row
> 元素一个上下文节点。 nodes()
方法返回包含三行的行集。 每行都有一个原始 XML 的逻辑副本,其中每个上下文节点都标识原始文档中的一个不同的 <row
> 元素。
然后,查询会从每行返回上下文节点:
DECLARE @x XML
SET @x='<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>'
SELECT T.c.query('.') AS result
FROM @x.nodes('/Root/row') T(c)
GO
在以下示例结果中,查询方法返回上下文项及其内容:
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3"/>
对上下文节点应用父级取值函数将返回所有三行的 <Root
> 元素。
SELECT T.c.query('..') AS result
FROM @x.nodes('/Root/row') T(c)
GO
下面是结果:
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>
<Root>
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
</Root>
针对 xml 类型的列指定 nodes() 方法
此示例中使用了自行车生产说明,该说明存储在 ProductModel 表的 Instructions xml 类型列中 。
在以下示例中,已对 ProductModel
表中 xml 类型的 Instructions
列指定 nodes()
方法。
nodes()
方法通过指定 /MI:root/MI:Location
路径,将 <Location
> 元素设置为上下文节点。 结果行集包括原始文档的逻辑副本,每个副本对应文档中的一个 <Location
> 节点,上下文节点设置为 <Location
> 元素。 因此,nodes()
函数提供一组 <Location
> 上下文节点。
query()
方法对此行集请求 self::node
,然后在每行中返回 <Location>
元素。
在此示例中,查询将特定产品样式的生产说明文档中将每一个 <Location
> 元素都设置为上下文节点。 可以使用这些上下文节点来按照这些方式来检索值:
查找每个 <
Location
> 中的位置 ID在每个 <
Location
> 中检索生产步骤(<step
> 子元素)
在 '.'
方法中,此查询返回上下文项,其中指定了 self::node()
的缩写语法 query()
。
注意以下事项:
将
nodes()
方法应用于 Instructions 列,并且返回行集T (C)
。 此行集包含原始生产说明文档的逻辑副本,并且以/root/Location
作为上下文项。CROSS APPLY 将
nodes()
应用于ProductModel
表中的每行,并且仅返回生成结果集的行。SELECT C.query('.') as result 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 T(C) WHERE ProductModelID=7
下面是部分结果:
<MI:Location LocationID="10" ...> <MI:step ... /> ... </MI:Location> <MI:Location LocationID="20" ... > <MI:step ... /> ... </MI:Location> ...
将 nodes() 应用于另一 nodes() 方法返回的行集
以下代码查询 XML 文档,以获得 Instructions
表的 ProductModel
列中的生产说明。 此查询返回包含产品样式 ID、生产位置和生产步骤的行集。
注意以下事项:
将
nodes()
方法应用于Instructions
列,并且返回T1 (Locations)
行集。 此行集包含原始生产说明文档的逻辑副本,并且/root/Location
元素作为项上下文。将
nodes()
应用于T1 (Locations)
行集,并且返回T2 (steps)
行集。 此行集包含原始生产说明文档的逻辑副本,并且/root/Location/step
元素作为项上下文。
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 <step ... />
7 10 <step ... />
...
7 20 <step ... />
7 20 <step ... />
7 20 <step ... />
...
该查询声明 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