共用方式為


nodes() 方法 (XML 資料類型)

當您想將 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>

含有查詢運算式 /root/Locationnodes() 方法引動過程,會傳回含有三個資料列的資料列集,每個資料列都含有原始 XML 文件的邏輯副本,且其內容項目會設成其中一個 <Location> 節點:

Product
ModelID      Instructions
----------------------------------
1       <root>
             <Location LocationID="20" ... />
             <Location LocationID="30" .../></root>
1      <root><Location LocationID="10" ... />
             
             <Location LocationID="30" .../></root>
1      <root><Location LocationID="10" ... />
             <Location LocationID="20" ... />
             </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 相同。它會提供 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

範例

A. 針對 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>

下列查詢指定了絕對路徑。在內容節點上使用絕對路徑運算式的查詢,將會在內容節點的根節點上啟動。因此,針對 nodes() 所傳回的每個內容節點,您都會收到三個資料列。

SELECT T.c.query('/Root/row') 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" />
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />
<row id="1"><name>Larry</name><oflw>some text</oflw></row>
<row id="2"><name>moe</name></row>
<row id="3" />

請注意,您不能直接使用 xml 資料類型之 nodes() 方法所傳回的資料行。例如,下列查詢會傳回錯誤:

...
SELECT T.c
FROM   @x.nodes('/Root/row') T(c)

在下列查詢中,xml 資料類型的 value() 方法及 query() 方法,會套用至 nodes() 方法所傳回的資料列集。value() 方法會傳回內容項目 (<row>) 的 id 屬性,而 query() 方法會傳回內容項目的 <name> 元素子樹。

DECLARE @x xml 
SET @x='
<Root>
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>
    <row id="2"><name>Joe</name></row>
    <row id="3" />
</Root>
'
SELECT T.c.value('@id','int') as id,
       T.c.query('name') as NAME
FROM   @x.nodes('/Root/row') T(c)
GO

以下是結果:

 id  NAME
-----------------------
 1   <name>Larry</name>
 2   <name>Joe</name>
 3   

請注意,此結果包含資料列識別碼 3,而且 <row> 元素沒有 <name> 子系。如果您要篩選結果,以傳回或不傳回不含 <name> 子系的資料列,您可以用下列其中一種方法來篩選:

  • nodes() 路徑運算式中使用述詞,例如 /Root/row[name]
  • 在資料列集上使用 exist() 方法。
  • 使用 CROSS APPLY。
  • 使用 OUTER APPLY。

下列查詢會針對nodes() 所傳回的資料列集指定 exist() 方法。如果內容節點 (<row>) 具有 <name> 子系,則 exist() 方法會傳回 True。

DECLARE @x xml        
SET @x='<Root>       
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>       
    <row id="2"><name>Joe</name></row>       
    <row id="3" />       
</Root>'       
SELECT T1.rows.value('@id','int') as id       
FROM @x.nodes('/Root/row') T1(rows)       
WHERE T1.rows.exist('name') = 1;       
GO

這會傳回二個資料列:資料列識別碼 1 和 2。

下列查詢使用 OUTER APPLYOUTER APPLY 會將 nodes() 套用至 T1(rows) 中的每個資料列,並傳回會產生結果集或產生 NULL 的資料列。因此,再使用 WHERE 子句來篩選資料列,只擷取 T2.names 資料行不是 NULL 的資料列。

DECLARE @x xml        
SET @x='       
<Root>       
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>       
    <row id="2"><name>Joe</name></row>       
    <row id="3" />       
</Root>'       
SELECT T1.rows.value('@id','int') as id       
FROM @x.nodes('/Root/row') T1(rows)       
OUTER APPLY T1.rows.nodes('./name') as T2(names)       
WHERE T2.names IS NOT NULL        
GO       

下列查詢使用 CROSS APPLYCROSS APPLY 會將 nodes() 套用至外部資料表 T1(rows) 中的每個資料列,並且只傳回將 nodes() 套用至 T1.rows 時,會產生結果集的資料列。在此情況下,您不必使用 WHERE 子句來測試 IS NOT NULL。

DECLARE @x xml        
SET @x='<Root>       
    <row id="1"><name>Larry</name><oflw>some text</oflw></row>       
    <row id="2"><name>Joe</name></row>       
    <row id="3" />       
</Root>'       
SELECT T1.rows.value('@id','int') as id       
FROM @x.nodes('/Root/row') T1(rows)       
CROSS APPLY T1.rows.nodes('./name') as T2(names)       
GO       

如需 CROSS APPLY 及 OUTER APPLY 的詳細資訊,請參閱<使用 APPLY>。

B. 針對 xml 類型的資料行來指定 nodes() 方法

此範例使用自行車製造指示,並將其儲存在 ProductModel 資料表的 Instructions xml 類型資料行中。如需詳細資訊,請參閱<在 AdventureWorks 資料庫中的 xml 資料類型表示法>。

在下列範例中,會針對 ProductModel 資料表中 xml 類型的 Instructions 資料行來指定 nodes() 方法。

nodes() 方法藉由指定 /MI:root/MI:Location 路徑,將 <Location> 元素設為內容節點。結果資料列集包含原始文件的邏輯副本 (文件中的每個 <Location> 節點各有一個副本),且其內容節點設為 <Location> 元素。因此,nodes() 函數會提供一組 <Location> 內容節點。

用於此資料列集的 query() 方法會要求 self::node,所以會傳回每個資料列的 <Location> 元素。

在此範例中,查詢在特定產品型號的製造指示文件中,將每個 <Location> 元素都設成內容節點。您可以使用這些內容節點來擷取值,如下所示:

  • 尋找每個 <Location> 中的 Location ID。
  • 擷取每個 <Location> 中的製造步驟 (<step> 子元素)

此查詢會傳回內容項目,其中在 query() 方法中指定了 self::node()'.' 縮寫語法。

請注意下列事項:

  • nodes() 方法會套用至 Instructions 資料行,並傳回資料列集 T (C)。此資料列集包含原始製造指示文件的邏輯副本,並以 /root/Location 做為內容項目。

  • CROSS APPLY 會將 nodes() 套用在 Instructions 資料表中的每個資料列,並且只傳回會產生結果集的資料列。

    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>
    ...
    

下列查詢類似上一個查詢,但它是藉由資料列集中的內容節點,使用 value()query() 來擷取一組值。SELECT 子句會針對每個位置,擷取其 Location ID 及該位置所使用的工具。

SELECT C.value('@LocationID','int') as LId,
       C.query('declare namespace MI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
                 MI:step/MI:tool') 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

以下為結果。為方便讀取,沒有顯示命名空間。

 LId  result
 10  <MI:tool xmlns:MI="...">T-85A framing tool</MI:tool>
     <MI:tool xmlns:MI="...">Trim Jig TJ-26</MI:tool>
     <MI:tool xmlns:MI="...">router with a carbide tip 15</MI:tool>
      <MI:tool xmlns:MI="...">Forming Tool FT-15</MI:tool>
 20
 30  <MI:tool xmlns:MI="...">standard debur tool</MI:tool>
 45  <MI:tool xmlns:MI="...">paint harness</MI:tool>
 50
 60

C. 將 nodes() 套用到由另一個 nodes() 方法傳回的資料列集

下列程式碼會在 XML 文件中查詢 ProductModel 資料表之 Instructions 資料行中的製造指示。該查詢會傳回一個資料列集,其中包含產品型號識別碼、製造位置,以及製造步驟。

請注意下列事項:

  • 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  

下列查詢與上一個查詢類似,但它是將 exist() 方法套用至 T2(steps) 資料列集中的 XML,只擷取至少使用一項製造工具的製造步驟。意即,<step> 元素至少有一個 <tool> 子系。

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 Steps
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
AND    steps.exist('./MI:tool') = 1
GO

請參閱

概念

使用 WITH XMLNAMESPACES 來加入命名空間
XML 資料類型
產生 XML 執行個體
XML 應用程式範例

其他資源

xml 資料類型方法

說明及資訊

取得 SQL Server 2005 協助