Condividi tramite


Metodo nodes() (tipo di dati xml)

È possibile utilizzare il metodo nodes() per suddividere un'istanza del tipo di dati xml in un insieme di dati relazionali. Tale metodo consente di identificare i nodi che verranno mappati a una nuova riga.

Per ogni istanza del tipo di dati xml esiste un nodo di contesto fornito implicitamente. Nel caso di un'istanza XML archiviata in una colonna o variabile, si tratta del nodo del documento, che costituisce il nodo implicito di livello più alto per ogni istanza del tipo di dati xml.

Il metodo nodes() restituisce un set di righe che contiene copie logiche delle istanze XML originali. In tali copie logiche il nodo di contesto di ogni istanza di riga viene impostato su uno dei nodi identificati tramite l'espressione della query, di modo che le query successive possano eseguire spostamenti relativi rispetto ai nodi di contesto.

Dal set di righe è possibile recuperare più valori. È ad esempio possibile applicare il metodo value() al set di righe restituito dal metodo nodes() e recuperare più valori dall'istanza XML originale. Quando viene applicato a un'istanza XML, il metodo value() restituisce un solo valore.

La sintassi generale è la seguente:

nodes (XQuery) as Table(Column)
  • XQuery
    Valore letterale stringa costituito da un'espressione XQuery. Se l'espressione della query costruisce nodi, questi ultimi verranno esposti nel set di righe risultante. Se l'espressione della query restituisce una sequenza vuota, il set di righe sarà vuoto. Se l'espressione della query restituisce staticamente una sequenza che contiene valori atomici, anziché nodi, verrà generato un errore statico.
  • Table(Column)
    Nome della tabella e della colonna per il set di righe risultante.

Si consideri ad esempio la tabella seguente:

T (ProductModelID int, Instructions xml)

In tale tabella è archiviato il seguente documento di istruzioni per la produzione, di cui è illustrato solo un frammento. Si noti che nel documento sono indicati tre centri di produzione.

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

Una chiamata al metodo nodes() con l'espressione di query /root/Location restituirebbe un set di righe con tre righe, ognuna contenente una copia logica del documento XML originale e con l'elemento di contesto impostato su uno dei nodi <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>

Per eseguire query su tale set di righe, è possibile utilizzare i metodi con tipo di dati xml. La query seguente estrae la sottostruttura dell'elemento di contesto per ogni riga generata:

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

Risultato:

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

Osservazioni

Nel set di righe restituito sono state mantenute le informazioni sul tipo. Al risultato del metodo nodes() è possibile applicare metodi con tipo di dati xml, ad esempio query(), value(), exist() e nodes(), ma non è possibile applicare il metodo modify() per modificare l'istanza XML.

Inoltre, il nodo di contesto nel set di righe non può essere materializzato, ovvero non può essere utilizzato in un'istruzione SELECT. Può essere tuttavia utilizzato nelle istruzioni IS NULL e COUNT(*).

Gli scenari di utilizzo del metodo nodes() sono gli stessi dell'istruzione OPENXML, che visualizza un'istanza XML come set di righe. Non è tuttavia possibile utilizzare cursori, quando si utilizza il metodo nodes() su una tabella che contiene più righe di documenti XML.

Si noti che, poiché il set di righe restituito dal metodo nodes() è senza nome, deve essere denominato in modo esplicito tramite alias.

La funzione nodes() non può essere applicata direttamente ai risultati di una funzione definita dall'utente. Per utilizzare la funzione nodes() con il risultato di una funzione scalare definita dall'utente, è possibile assegnare il risultato della funzione definita dall'utente a una variabile o utilizzare una tabella derivata per assegnare un alias di colonna al valore restituito dalla funzione definita dall'utente e quindi utilizzare CROSS APPLY per eseguire la selezione dall'alias.

Nell'esempio seguente viene illustrato un modo per utilizzare CROSS APPLY per selezionare il risultato di una funzione definita dall'utente.

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

Esempi

A. Utilizzo del metodo nodes() su una variabile di tipo xml

Nell'esempio seguente viene utilizzato un documento XML con un elemento di livello principale <Root> e tre elementi figlio <row>. La query utilizza il metodo nodes() per impostare nodi di contesto separati, uno per ogni elemento <row>. Il metodo nodes() restituisce un set di righe con tre righe. Ogni riga contiene una copia logica dell'istanza XML originale e ogni nodo di contesto identifica un diverso elemento <row> nel documento originale.

La query restituisce quindi il nodo di contesto di ogni riga:

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

Di seguito è riportato il risultato. In questo esempio il metodo della query restituisce l'elemento di contesto e il relativo contenuto:

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

Se si applica la funzione di accesso padre ai nodi di contesto, verrà restituito l'elemento <Root> di ognuna delle tre righe:

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

Risultato:

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

Nella query seguente è specificato un percorso assoluto. Una query su un nodo di contesto in cui viene utilizzata un'espressione di percorso assoluto deve iniziare sul nodo principale del nodo di contesto. Verranno pertanto restituite tutte e tre le righe per ogni nodo di contesto restituito da 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" />

La colonna restituita dal metodo nodes() con tipo di dati xml non può essere utilizzata direttamente. La query seguente, ad esempio, restituisce un errore:

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

Nella query seguente i metodi value() e query() con tipo di dati xml vengono applicati al set di righe restituito dal metodo nodes(). Il metodo value() restituisce l'attributo id dell'elemento di contesto (<row>), mentre il metodo query() restituisce la sottostruttura dell'elemento <name> dell'elemento di contesto.

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

Risultato:

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

Si noti che il risultato include l'ID di riga 3 e l'elemento <row> non ha un elemento <name> figlio. Per filtrare il risultato in modo da restituire le righe con e senza elementi <name> figlio, è possibile:

  • Utilizzare un predicato nell'espressione di percorso nodes(), ad esempio /Root/row[name].
  • Utilizzare il metodo exist() sul set di righe.
  • Utilizzare CROSS APPLY.
  • Utilizzare OUTER APPLY.

Nella query seguente viene specificato il metodo exist() sul set di righe restituito da nodes(). Il metodo exist() restituisce True se il nodo di contesto (<row>) ha un elemento <name> figlio.

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

La query restituisce due righe, con ID 1 e 2.

Nella query seguente viene utilizzato OUTER APPLY. OUTER APPLY applica nodes() a ogni riga in T1(rows) e restituisce sia le righe che formano il set di risultati, sia valori NULL. Viene pertanto utilizzata la clausola WHERE per filtrare le righe in modo da recuperare solo quelle in cui la colonna T2.names non è 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       

Nella query seguente viene utilizzato CROSS APPLY. CROSS APPLY applica il metodo nodes() a ogni riga della tabella esterna, T1(rows), e restituisce solo le righe che producono un set di risultati quando nodes() viene applicato a T1.rows. In questo caso non è necessario utilizzare la clausola WHERE per verificare la condizione 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       

Per informazioni su CROSS APPLY e OUTER APPLY, vedere Utilizzo di APPLY.

B. Utilizzo del metodo nodes() su una colonna di tipo xml

In questo esempio vengono utilizzati documenti di istruzioni per la produzione di biciclette, archiviati nella colonna di tipo xml Instructions della tabella ProductModel. Per ulteriori informazioni, vedere Rappresentazione del tipo di dati XML nel database AdventureWorks.

Nell'esempio seguente il metodo nodes() viene eseguito sulla colonna Instructions di tipo xml della tabella ProductModel.

Il metodo nodes() imposta gli elementi <Location> come nodi di contesto, specificando il percorso /MI:root/MI:Location. Il set di righe risultante include copie logiche del documento originale, una per ogni nodo <Location> del documento, con il nodo di contesto impostato sull'elemento <Location>. Il metodo nodes() restituisce pertanto un set di nodi di contesto <Location>.

Il metodo query() su questo set di righe richiede self::node e pertanto restituisce l'elemento <Location> di ogni riga.

In questo esempio la query imposta ogni elemento <Location> come nodo di contesto nel documento di istruzioni per la produzione di uno specifico modello di prodotto. È possibile utilizzare tali nodi di contesto per recuperare valori quali i seguenti:

  • Trovare ID dei centri di produzione in ogni elemento <Location>.
  • Recuperare passaggi di produzione (gli elementi figlio <step>) in ogni elemento <Location>.

La query seguente restituisce l'elemento di contesto. Nel metodo query() viene specificata la sintassi abbreviata per self::node(), ovvero '.'.

Si noti quanto segue:

  • Il metodo nodes() viene applicato alla colonna Instructions e restituisce il set di righe T (C), il quale contiene copie logiche del documento originale di istruzioni per la produzione con /root/Location come elemento di contesto.

  • CROSS APPLY applica nodes() a ogni riga nella colonna Instructions e restituisce solo le righe che producono un set di risultati.

    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
    

    Risultato parziale:

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

La query seguente è simile alla precedente, con la differenza che utilizza i metodi value() e query() per recuperare un set di valori utilizzando i nodi di contesto nel set di righe. Per ogni centro di produzione, la clausola SELECT restituisce l'ID e gli strumenti utilizzati in tale centro di produzione.

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

Di seguito è riportato il risultato. Per una migliore leggibilità gli spazi dei nomi non sono indicati.

 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. Applicazione del metodo nodes() a un set di righe restituito da un altro metodo nodes()

Il codice seguente esegue una query sui documenti XML con le istruzioni per la produzione archiviati nella colonna Instructions della tabella ProductModel. La query restituisce un set di righe che contiene l'ID del modello di prodotto, i centri di produzione e i passaggi di produzione.

Si noti quanto segue:

  • Il metodo nodes() viene applicato alla colonna Instructions e restituisce il set di righe T1 (Locations), il quale contiene copie logiche del documento originale di istruzioni per la produzione con /root/Location come elemento di contesto.
  • Il metodo nodes() viene applicato al set di righe T1 (Locations) e restituisce il set di righe T2 (steps), il quale contiene copie logiche del documento originale di istruzioni per la produzione con /root/Location/step come elemento di contesto.
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       

Risultato:

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

Nella query il prefisso MI viene dichiarato due volte. In alternativa, è possibile utilizzare WITH XMLNAMESPACES per dichiarare il prefisso una volta sola e utilizzarlo nella query:

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  

La query seguente è simile alla precedente, con la differenza che applica il metodo exist() al valore XML nel set di righe T2(steps) per recuperare solo i passaggi di produzione in cui viene utilizzato almeno uno strumento di produzione, ovvero gli elementi <step> che hanno almeno un elemento <tool> figlio.

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

Vedere anche

Concetti

Aggiunta di spazi dei nomi tramite WITH XMLNAMESPACES
Tipo di dati XML
Generazione di istanze XML
Applicazioni XML di esempio

Altre risorse

Metodi con tipo di dati XML

Guida in linea e informazioni

Assistenza su SQL Server 2005