nodes()-Methode (xml-Datentyp)

Die nodes()-Methode ist nützlich, wenn Sie eine Instanz des xml-Datentyps in relationale Daten aufteilen möchten. Damit können Sie Knoten identifizieren, die einer neuen Zeile zugeordnet werden.

Jede Instanz des xml-Datentyps besitzt einen implizit bereitgestellten Kontextknoten. Für die in einer Spalte oder Variablen gespeicherte XML-Instanz ist das der Dokumentknoten. Der Dokumentknoten ist der implizite Knoten auf der obersten Ebene jeder Instanz des xml-Datentyps.

Das Ergebnis der nodes()-Methode ist ein Rowset, das logische Kopien der ursprünglichen XML-Instanzen enthält. In diesen logischen Kopien ist der Kontextknoten jeder Zeileninstanz auf einen der Knoten festgelegt, der mit dem Abfrageausdruck identifiziert ist, sodass nachfolgende Abfragen relativ zu diesen Kontextknoten navigieren können.

Sie können mehrere Werte aus dem Rowset abrufen. Sie können z. B. die value()-Methode auf das Rowset anwenden, das von nodes() zurückgegeben wurde und mehrere Werte aus der ursprünglichen XML-Instanz abrufen. Beachten Sie, dass die value()-Methode, wenn sie auf die XML-Instanz angewendet wird, nur einen einzigen Wert zurückgibt.

Das ist die allgemeine Syntax:

nodes (XQuery) as Table(Column)
  • XQuery
    Ist ein Zeichenfolgenliteral, ein XQuery-Ausdruck. Wenn der Abfrageausdruck Knoten konstruiert, werden diese konstruierten Knoten im resultierenden Rowset verfügbar gemacht. Wenn der Abfrageausdruck eine leere Sequenz ergibt, ist auch das resultierende Rowset leer. Wenn der Abfrageausdruck statisch eine Sequenz ergibt, die anstelle von Knoten atomare Werte enthält, wird ein statischer Fehler ausgelöst.
  • Table(Column)
    Ist der Tabellenname und der Spaltenname für das resultierende Rowset.

Angenommen, es ist z. B. die folgende Tabelle vorhanden:

T (ProductModelID int, Instructions xml)

In der Tabelle ist das folgende Produktionsanweisungsdokument gespeichert. Es ist nur ein Fragment dargestellt. Beachten Sie, dass es im Dokument drei Produktionsstandorte gibt.

<root>
  <Location LocationID="10"...>
     <step>...</step>
     <step>...</step>
      ...
  </Location>
  <Location LocationID="20" ...>
       ...
  </Location>
  <Location LocationID="30" ...>
       ...
  </Location>
</root>

Ein Aufruf der nodes()-Methode mit dem Abfrageausdruck /root/Location würde ein Rowset mit drei Zeilen zurückgeben, die jeweils eine logische Kopie des ursprünglichen XML-Dokuments enthalten, und bei dem das Kontextelement auf einen der <Location>-Knoten festgelegt ist:

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>

Sie können dann dieses Rowset mithilfe von xml-Datentypmethoden abfragen. Die folgende Abfrage extrahiert die Teilstruktur des Kontextelements für jede generierte Zeile:

SELECT T2.Loc.query('.')
FROM   T
CROSS APPLY Instructions.nodes('/root/Location') as T2(Loc) 

Dies ist das Ergebnis:

ProductModelID  Instructions
----------------------------------
1        <Location LocationID="10" ... />
1        <Location LocationID="20" ... />
1        <Location LocationID="30" .../>

Hinweise

Beachten Sie, dass das zurückgegebene Rowset die Typinformationen beibehalten hat. Sie können xml-Datentypmethoden, wie z. B. query(), value(), exist() und nodes(), auf das Ergebnis einer nodes()-Methode anwenden. Allerdings ist es nicht möglich, die modify()-Methode zum Ändern der XML-Instanz anzuwenden.

Außerdem kann der Kontextknoten im Rowset nicht materialisiert werden. Das bedeutet, dass Sie ihn nicht in einer SELECT-Anweisung verwenden können. Allerdings können Sie ihn in IS NULL und COUNT(*) verwenden.

Die Szenarien zum Verwenden der nodes()-Methode sind dieselben wie für das Verwenden von OPENXML. Damit wird eine Rowsetsicht des XML-Codes bereitgestellt. Allerdings müssen Sie keine Cursor verwenden, wenn Sie die nodes()-Methode für eine Tabelle verwenden, die mehrere Zeilen mit XML-Dokumenten enthält.

Beachten Sie, dass das von der nodes()-Methode zurückgegebene Rowset ein unbenanntes Rowset ist. Deshalb muss es durch Aliasing explizit benannt werden.

Die nodes()-Funktion kann nicht direkt auf die Ergebnisse einer benutzerdefinierten Funktion angewendet werden. Um die nodes()-Funktion mit dem Ergebnis einer skalaren benutzerdefinierten Funktion zu verwenden, können Sie entweder das Ergebnis der benutzerdefinierten Funktion einer Variablen zuweisen oder mithilfe einer abgeleiteten Tabelle dem Rückgabewert der benutzerdefinierten Funktion einen Spaltenalias zuordnen und dann mit CROSS APPLY eine Auswahl aus dem Alias treffen.

Im folgenden Beispiel wird eine Methode gezeigt, um mithilfe von CROSS APPLY eine Auswahl aus dem Ergebnis einer benutzerdefinierten Funktion zu treffen.

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

Beispiele

A. Verwenden der nodes()-Methode für eine Variable des xml-Typs

Im folgenden Beispiel liegt ein XML-Dokument vor, das über ein <Root>-Element auf oberster Ebene und drei untergeordnete <row>-Elemente verfügt. Die Abfrage verwendet die nodes()-Methode, um getrennte Kontextknoten festzulegen, einen für jedes <row>-Element. Die nodes()-Methode gibt ein Rowset mit drei Zeilen zurück. Jede Zeile enthält eine logische Kopie des ursprünglichen XML-Dokuments, wobei jeder Kontextknoten ein unterschiedliches <row>-Element im ursprünglichen Dokument identifiziert.

Die Abfrage gibt dann den Kontextknoten aus jeder Zeile zurück:

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

Im Folgenden wird das Ergebnis gezeigt. In diesem Beispiel gibt die Abfragemethode das Kontextelement und dessen Inhalt zurück:

 <row id="1"><name>Larry</name><oflw>some text</oflw></row>
 <row id="2"><name>moe</name></row>
 <row id="3"/>

Durch Anwenden des übergeordneten Accessors auf den Kontextknoten wird das <Root>-Element für alle drei zurückgegeben:

SELECT T.c.query('..') AS result
FROM   @x.nodes('/Root/row') T(c)
go

Dies ist das Ergebnis:

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

Die folgende Abfrage gibt einen absoluten Pfad an. Eine Abfrage für einen Kontextknoten, der einen absoluten Pfadausdruck verwendet, startet beim Stammknoten des Kontextknotens. Deshalb erhalten Sie alle drei Zeilen für jeden Kontextknoten, der von nodes() zurückgegeben wird.

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" />

Beachten Sie, dass die Spalte, die von der nodes()-Methode des xml-Datentyps zurückgegeben wird, nicht direkt verwendet werden kann. So gibt z. B. die folgende Abfrage einen Fehler zurück:

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

In der folgenden Abfrage werden die Methoden value() und query() des xml-Datentyps auf das von der nodes()-Methode zurückgegebene Rowset angewendet. Die value()-Methode gibt das id-Attribut des Kontextelements (<row>) zurück, und die query()-Methode gibt die <name>-Elementunterstruktur des Kontextelements zurück.

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

Dies ist das Ergebnis:

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

Beachten Sie, dass das Ergebnis die Zeilen-ID 3 enthält und dass das <row>-Element kein untergeordnetes <name>-Element aufweist. Wenn Sie das Resultset so filtern möchten, dass die Zeilen ohne das <name>-Element zurückgegeben oder nicht zurückgegeben werden, können Sie es mit einer der folgenden Methoden filtern:

  • Verwenden Sie ein Prädikat im nodes()-Pfadausdruck, wie z. B. /Root/row[name].
  • Verwenden Sie die exist()-Methode für das Rowset.
  • Verwenden Sie CROSS APPLY.
  • Verwenden Sie OUTER APPLY.

In der folgenden Abfrage wird die exist()-Methode für das Rowset angegeben, das von nodes() zurückgegeben wird. Die exist()-Methode gibt True zurück, wenn der Kontextknoten (<row>) ein untergeordnetes <name>-Element aufweist.

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

Es werden zwei Zeilen mit den Zeilen-IDs 1 und 2 zurückgegeben.

In der folgenden Abfrage wird OUTER APPLY verwendet. OUTER APPLY wendet nodes() auf jede Zeile in T1(rows) an und gibt Zeilen zurück, die das Resultset erzeugen, und auch solche, die NULL-Werte erzeugen. Deshalb werden die Zeilen mit der WHERE-Klausel gefiltert und nur solche Zeilen abgerufen, bei denen die T2.names-Spalte nicht NULL ist.

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       

In der folgenden Abfrage wird CROSS APPLY verwendet. CROSS APPLY wendet nodes() auf jede Zeile in der äußeren Tabelle T1(rows) an und gibt nur die Zeilen zurück, die ein Resultset erzeugen, wenn nodes() auf T1.rows angewendet wird. In diesem Fall muss IS NOT NULL nicht mit der WHERE-Klausel getestet werden.

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       

Informationen zu CROSS APPLY und OUTER APPLY finden Sie unter Verwenden von APPLY.

B. Angeben der nodes()-Methode für eine Spalte des XML-Typs

In diesem Beispiel werden die Produktionsanweisungen für Fahrräder verwendet und in der Instructions-Spalte vom Typ xml der ProductModel-Tabelle gespeichert. Weitere Informationen finden Sie unter Darstellung des xml-Datentyps in der AdventureWorks-Datenbank.

Im folgenden Beispiel wird die nodes()-Methode für die Instructions-Spalte vom Typ xml in der ProductModel-Tabelle angegeben.

Die nodes()-Methode legt die <Location>-Elemente als Kontextknoten fest, indem der /MI:root/MI:Location-Pfad angegeben wird. Das dadurch entstehende Rowset enthält logische Kopien des ursprünglichen Dokuments, eine für jeden <Location>-Knoten im Dokument, wobei der Kontextknoten auf das <Location>-Element festgelegt ist. Deshalb gibt die nodes()-Funktion eine Gruppe von <Location>-Kontextknoten zurück.

Die query()-Methode für dieses Rowset fordert self::node an und gibt deshalb das <Location>-Element in jeder Zeile zurück.

In diesem Beispiel legt die Abfrage jedes <Location>-Element als einen Kontextknoten im Produktionsanweisungsdokument eines bestimmten Produktmodells fest. Sie können diese Kontextknoten verwenden, um Werte wie z. B. die folgenden abzurufen:

  • Suchen der Standort-IDs in jedem <Location>-Element
  • Abrufen der Fertigungsschritte (untergeordnete <step>-Elemente) in jedem <Location>-Element

Diese Abfrage gibt in der query()-Methode das Kontextelement zurück, in dem die abgekürzte Syntax '.' für self::node() angegeben ist.

Beachten Sie Folgendes:

  • Die nodes()-Methode wird auf die Instructions-Spalte angewendet und gibt das Rowset T (C) zurück. Dieses Rowset enthält logische Kopien des ursprünglichen Produktionsanweisungsdokuments mit /root/Location als Kontextelement.

  • CROSS APPLY wendet nodes() auf jede Zeile in der Instructions-Tabelle an und gibt nur die Zeilen zurück, die ein Resultset erzeugen.

    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
    

    Dies ist das Teilergebnis:

    <MI:Location LocationID="10"  ...>
       <MI:step ... />
          ...
    </MI:Location>
    <MI:Location LocationID="20"  ... >
        <MI:step ... />
          ...
    </MI:Location>
    ...
    

Die folgende Abfrage gleicht der vorherigen Abfrage, außer dass sie value() und query() zum Abrufen einer Gruppe von Werten verwendet, wobei die Kontextknoten im Rowset verwendet werden. Für jeden Standort ruft die SELECT-Klausel die Standort-ID und die an diesem Standort verwendeten Werkzeuge ab.

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

Im Folgenden wird das Ergebnis gezeigt. Um die Lesbarkeit zu verbessern, werden die Namespaces nicht gezeigt.

 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. Anwenden von nodes() auf das Rowset, das von einer anderen nodes()-Methode zurückgegeben wurde

Mit dem folgenden Code werden aus den XML-Dokumenten die Produktionsanweisungen in der Instructions-Spalte der ProductModel-Tabelle abgefragt. Die Abfrage gibt ein Rowset zurück, das die Produktmodell-ID, die Produktionsstandorte und die Fertigungsschritte enthält.

Beachten Sie Folgendes:

  • Die nodes()-Methode wird auf die Instructions-Spalte angewendet und gibt das Rowset T1 (Locations) zurück. Dieses Rowset enthält logische Kopien des ursprünglichen Produktionsanweisungsdokuments mit dem /root/Location-Element als Elementkontext.
  • nodes() wird auf das T1 (Locations)-Rowset angewendet und gibt das Rowset T2 (steps) zurück. Dieses Rowset enthält logische Kopien des ursprünglichen Produktionsanweisungsdokuments mit dem /root/Location/step-Element als Elementkontext.
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       

Dies ist das Ergebnis:

ProductModelID LocID Step       
----------------------------       
7      10   <step ... />       
7      10   <step ... />       
...       
7      20   <step ... />       
7      20   <step ... />       
7      20   <step ... />       
...       

Die Abfrage deklariert das MI-Präfix zweimal. Sie können stattdessen WITH XMLNAMESPACES verwenden, um das Präfix einmal zu deklarieren und es dann in der Abfrage zu verwenden:

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  

Die folgende Abfrage gleicht der vorherigen, außer dass sie die exist()-Methode auf den XML-Code im T2(steps)-Rowset anwendet, um ausschließlich solche Produktionsschritte abzurufen, bei denen mindestens ein Produktionswerkzeug verwendet wird. Das heißt, das <step>-Element muss zumindest ein untergeordnetes <tool>-Element besitzen.

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

Siehe auch

Konzepte

Hinzufügen von Namespaces mithilfe von WITH XMLNAMESPACES
XML-Datentyp
Generieren von XML-Instanzen
XML-Beispielanwendungen

Andere Ressourcen

xml-Datentypmethoden

Hilfe und Informationen

Informationsquellen für SQL Server 2005