Condividi tramite


Procedure consigliate per l'XML

SQL Server 2005 offre ampio supporto per l'elaborazione dei dati XML. I valori XML possono essere archiviati in modo nativo in una colonna con tipo di dati xml, che può essere tipizzata in base a un insieme di schemi XML oppure lasciata non tipizzata. La colonna XML può essere indicizzata. È inoltre supportata la manipolazione dei dati a un livello estremamente dettagliato, tramite query XQuery e il linguaggio XML DML. Il linguaggio XML DML è un estensione per la modifica dei dati.

SQL Server 2000 e le varie versioni di SQLXML pubblicate sul Web offrono potenti funzionalità per la gestione dei dati XML. Tali funzionalità si basano sul mapping tra i dati relazionali e i dati XML. Le visualizzazioni XML dei dati relazionali possono essere definite utilizzando schemi XSD con annotazioni (AXSD, Annotated XSD) per offrire un approccio incentrato su XML che supporta il caricamento di massa dei dati, le query e funzionalità di aggiornamento per i dati XML. Le estensioni Transact-SQL offrono un approccio incentrato su SQL per il mapping a XML dei risultati delle query relazionali tramite FOR XML e per la generazione di viste relazionali dai dati XML tramite OPENXML. In SQL Server 2005 il supporto di tali funzionalità è stato esteso. Oltre al nuovo supporto XML nativo, SQL Server 2005 offre una potente piattaforma per lo sviluppo di applicazioni complete per la gestione di dati semistrutturati e non strutturati.

In questo argomento vengono fornite linee guida per la modellazione e l'utilizzo di dati XML in SQL Server 2005. L'argomento include le sezioni seguenti:

  • Modellazione dei dati
    In SQL Server 2005 i dati XML possono essere archiviati in vari modi, tramite il tipo di dati nativo xml e suddivisi tra più tabelle. In questo argomento vengono fornite linee guida per la scelta del metodo più appropriato per la modellazione dei dati XML. Vengono inoltre fornite informazioni sull'indicizzazione dei dati XML, sull'innalzamento di livello delle proprietà e sulla tipizzazione delle istanze XML.
  • Utilizzo
    Questa sezione include argomenti correlati all'utilizzo, ad esempio il caricamento di dati XML nel server e l'inferenza dei tipi nella compilazione delle query. In questa sezione vengono inoltre illustrate funzionalità strettamente correlate, evidenziandone le differenze e fornendo consigli per un utilizzo appropriato. Le funzionalità vengono illustrate tramite esempi.

Modellazione dei dati

In questa sezione vengono descritti i motivi per cui è consigliabile utilizzare dati XML in SQL Server 2005. Vengono inoltre fornite linee guida per la scelta tra archiviazione XML nativa e tecnologia di visualizzazione XML, oltre a consigli per la modellazione dei dati.

Modello di dati relazionale o XML

Se i dati utilizzati sono altamente strutturati in base a uno schema noto, il modello relazionale costituisce senz'altro il metodo di archiviazione ottimale. In SQL Server sono disponibili tutte le funzionalità e gli strumenti necessari. Se invece i dati sono semistrutturati, non strutturati o dotati di una struttura sconosciuta, sarà necessario prestare particolare attenzione alla modellazione.

XML costituisce la soluzione ottimale quando si desidera un modello indipendente dalla piattaforma, per garantire la portabilità dei dati tramite codice strutturale e semantico. Costituisce inoltre una soluzione appropriata nelle circostanze seguenti:

  • I dati sono di tipo sparse, non se ne conosce la struttura o si prevede che in futuro la struttura verrà modificata in modo significativo.
  • I dati rappresentano una gerarchia di contenuto, anziché riferimenti tra entità, e potrebbero essere ricorsivi.
  • L'ordine è implicito nei dati.
  • Si desidera eseguire query sui dati o aggiornarne alcune parti, in base alla struttura.

Se nessuna di queste condizioni è soddisfatta, è consigliabile utilizzare il modello di dati relazionale. Se ad esempio i dati sono in formato XML, ma l'applicazione utilizza il database solo per archiviarli e recuperarli, è sufficiente utilizzare una colonna di tipo [n]varchar(max). L'archiviazione dei dati in una colonna XML offre ulteriori vantaggi, ad esempio la possibilità di determinare automaticamente se i dati sono validi e in formato corretto e il supporto per l'esecuzione di aggiornamenti e query dettagliate sui dati XML.

Motivi per l'archiviazione di dati XML in SQL Server 2005

Di seguito sono illustrati alcuni dei motivi per utilizzare le funzionalità XML native di SQL Server 2005, anziché gestire i dati XML nel file system:

  • Si desidera condividere, modificare ed eseguire query sui dati XML in un modo efficiente e basato sulle transazioni. Per l'applicazione è importante disporre di un accesso ai dati estremamente dettagliato, ad esempio perché si desidera estrarre alcune sezioni da un documento XML oppure inserire una nuova sezione senza sostituire l'intero documento.
  • Si utilizzano sia dati relazionali che dati XML e si desidera garantirne l'interoperabilità nell'ambito dell'applicazione.
  • È necessario il supporto del linguaggio per le query e la modifica dei dati per le applicazioni tra domini.
  • Si desidera che il server garantisca che i dati sono nel formato corretto e, facoltativamente, che i dati vengano convalidati in base a schemi XML specifici.
  • Si desidera indicizzare i dati XML per una più efficiente elaborazione delle query e una maggiore scalabilità e per consentire l'utilizzo di un'avanzata utilità di ottimizzazione delle query.
  • Si desidera accedere ai dati XML tramite SOAP, ADO.NET e OLE DB.
  • Per la gestione dei dati XML si desidera utilizzare le funzionalità amministrative del server di database, ad esempio le funzionalità di backup, ripristino e replica.

Se nessuna di queste condizioni è soddisfatta, è preferibile archiviare i dati in un formato diverso da XML utilizzando un tipo di dati per oggetti di grandi dimensioni, ad esempio [n]varchar(max) o varbinary(max).

Opzioni di archiviazione per i dati XML

Le opzioni di archiviazione per i dati XML disponibili in SQL Server 2005 includono le seguenti:

  • Archiviazione nativa tramite il tipo di dati xml.
    I dati vengono archiviati in una rappresentazione interna che mantiene il contenuto XML, inclusi la gerarchia del contenuto, l'ordine del documento e i valori di elementi e attributi. In particolare, viene mantenuto il contenuto InfoSet dei dati XML. Per ulteriori informazioni su InfoSet, collegarsi all'indirizzo http://www.w3.org/TR/xml-infoset. Il contenuto InfoSet potrebbe non essere una copia identica del testo XML, perché non vengono mantenute le informazioni seguenti: spazi vuoti non significativi, ordine degli attributi, prefissi degli spazi dei nomi e dichiarazione XML.
    Per il tipo di dati xml tipizzato, un tipo di dati xml associati a schemi XML, il PSVI (Post-Schema Validation InfoSet) aggiunge all'InfoSet le informazioni sul tipo e viene codificato nella rappresentazione interna. Questo consente di migliorare in modo significativo la velocità di analisi. Per ulteriori informazioni, vedere le specifiche dello schema XML W3C all'indirizzo http://www.w3.org/TR/xmlschema-1 e http://www.w3.org/TR/xmlschema-2.
  • Mapping tra archiviazione XML e relazionale
    Tramite uno schema con annotazioni (AXSD), il codice XML viene scomposto in colonne e archiviato in una o più tabelle. Questo consente di mantenere la conformità dei dati a livello relazionale, conservando la struttura gerarchica anche se l'ordine degli elementi viene ignorato. Lo schema non può essere ricorsivo.
  • Archiviazione di oggetti di grandi dimensioni, tramite i tipi di dati [n]varchar(max) e varbinary(max)
    Viene archiviata una copia identica dei dati. Questo è utile per le applicazioni specializzate per scopi particolari, ad esempio quelle per i documenti legali. Per la maggior parte delle applicazioni non è invece necessaria una copia esatta, ma è sufficiente il contenuto XML (conformità all'InfoSet).

È in genere necessario utilizzare una combinazione di questi approcci, ad esempio se si desidera archiviare i dati XML in una colonna con tipo di dati xml e alzare le relative proprietà al livello di colonne relazionali, oppure se si desidera utilizzare la tecnologia di mapping per archiviare le parti non ricorsive in colonne non XML e solo le parti ricorsive in colonne con tipo di dati xml .

Scelta della tecnologia XML

La scelta della tecnologia XML, ovvero tra XML nativo e visualizzazione XML, dipende in genere dai fattori seguenti:

  • Opzioni di archiviazione
    Per i dati XML potrebbe essere più appropriata l'archiviazione di oggetti di grandi dimensioni (ad esempio, il manuale di un prodotto) oppure l'archiviazione in colonne relazionali (ad esempio, una voce convertita in formato XML). Ogni opzione di archiviazione mantiene la conformità del documento a un livello diverso.
  • Funzionalità di query
    L'opzione di archiviazione più appropriata dipende dalla natura delle query e dal livello di dettaglio con cui si desidera eseguire query sui dati XML. Le query dettagliate sui dati XML, quale la valutazione del predicato sui nodi XML, sono supportate a livelli diversi dalle due opzioni di archiviazione.
  • Indicizzazione dei dati XML
    Tramite l'indicizzazione dei dati XML è possibile migliorare le prestazioni delle query XML. Le opzioni di indicizzazione disponibili dipendono dalle opzioni di archiviazione ed è necessario selezionare quella più appropriata per ottimizzare il carico di lavoro.
  • Funzionalità per la modifica dei dati
    Alcuni carichi di lavoro comportano una modifica estremamente dettagliata dei dati XML, che può ad esempio includere l'aggiunta di una nuova sezione all'interno di un documento, mentre altri, ad esempio il contenuto Web, non richiedono modifiche così specifiche. Il supporto del linguaggio per la modifica dei dati può essere molto importante per un'applicazione.
  • Supporto degli schemi
    I dati XML possono essere descritti da uno schema che può essere costituito o meno da un documento di schema XML. Il supporto dell'XML associato a schema dipende dalla tecnologia XML.

A opzioni diverse corrispondono caratteristiche di prestazioni diverse.

Archiviazione XML nativa

È possibile archiviare i dati XML in una colonna con tipo di dati xml nel server. Questa soluzione è particolarmente appropriata se:

  • Si desidera un metodo diretto per archiviare i dati XML nel server e, al tempo stesso, mantenere l'ordine e la struttura dei documenti.
  • Non è sempre disponibile uno schema per i dati XML.
  • Si desidera modificare ed eseguire query sui dati XML.
  • Si desidera indicizzare i dati XML per una più rapida elaborazione delle query.
  • L'applicazione richiede viste del catalogo di sistema per l'amministrazione di dati e schemi XML.

L'archiviazione XML nativa è utile quando si utilizzano documenti XML con strutture diverse oppure conformi a schemi diversi o complessi, di cui è difficile eseguire il mapping a strutture relazionali.

Esempio: modellazione di dati XML tramite il tipo di dati xml

Si consideri un manuale di prodotto in formato XML, composto da un capitolo per ogni argomento e con più sezioni in ogni capitolo. Una sezione può contenere sottosezioni e, di conseguenza, l'elemento <section> è ricorsivo. I manuali dei prodotti includono elevati volumi di contenuto eterogeneo, ad esempio diagrammi e materiale tecnico, e i dati sono semistrutturati. Gli utenti possono avere l'esigenza di ricercare gli argomenti di interesse in un contesto specifico, ad esempio la sezione dedicata agli indici cluster nel capitolo dedicato all'indicizzazione, e di eseguire query su quantità tecniche.

Una colonna con tipo di dati xml costituisce un modello di archiviazione particolarmente appropriato per i documenti XML, perché mantiene il contenuto InfoSet dei dati XML. È possibile indicizzare la colonna XML per migliorare le prestazioni delle query.

Esempio: conservazione di copie esatte dei dati XML

Si supponga che, per legge, sia necessario conservare copie testuali esatte dei propri documenti XML, quali documenti firmati, documenti legali oppure ordini di transazioni azionarie. È possibile archiviare i documenti in una colonna con tipo di dati [n]varchar(max).

Per le query, in fase di esecuzione è necessario convertire i dati nel tipo di dati xml ed eseguire query XQuery su di essi. La conversione in fase di esecuzione può essere tuttavia molto costosa, soprattutto se il documento è di grandi dimensioni. Se è necessario eseguire query di frequente, è possibile archiviare una seconda copia dei documenti in una colonna con tipo di dati xml e indicizzarla, mentre per restituire le copie esatte dei documenti si utilizza la colonna con tipo di dati [n]varchar(max).

La colonna XML può essere una colonna calcolata, basata sulla colonna [n]varchar(max). Non è tuttavia possibile creare un indice XML su una colonna calcolata di tipo XML, né su colonne di tipo [n]varchar(max) o varbinary(max).

Tecnologia di visualizzazione XML

Definendo un mapping tra gli schemi XML e le tabelle in un database, si crea una visualizzazione XML dei dati persistenti. Tramite la visualizzazione XML, è possibile utilizzare il caricamento di massa XML per popolare le tabelle sottostanti. È possibile eseguire query sulla visualizzazione XML utilizzando XPath versione 1.0. Le query vengono convertite in query SQL sulle tabelle. Analogamente, anche gli aggiornamenti vengono propagati a tali tabelle.

Questa tecnologia è utile nelle situazioni seguenti:

  • Si desidera disporre di un modello di programmazione incentrato su XML, che utilizza visualizzazioni XML basate sui dati relazionali esistenti.
  • Si dispone di uno schema (XSD, XDR) per i dati XML fornito da un partner esterno.
  • L'ordine dei dati non è importante, i dati della tabella su cui si esegue la query non sono ricorsivi oppure si conosce in anticipo la profondità di ricorsione massima.
  • Si desidera modificare ed eseguire query sui dati tramite la visualizzazione XML, utilizzando XPath versione 1.0.
  • Si desidera eseguire il caricamento di massa dei dati XML e scomporli nelle tabelle sottostanti utilizzando la visualizzazione XML.

Questa tecnica viene utilizzata ad esempio per i dati relazionali esposti come XML per lo scambio di dati e i servizi Web e per i dati XML con schema fisso. Per ulteriori informazioni vedere MSDN Online Library.

Esempio: modellazione di dati tramite uno schema XML con annotazioni (AXSD)

Si supponga ad esempio di avere a disposizione dati relazionali relativi a clienti, ordini e voci, che si desidera gestire come XML. Definire una visualizzazione XML applicando uno schema AXSD ai dati relazionali. La visualizzazione XML consente di eseguire il caricamento di massa dei dati XML nelle tabelle, nonché di aggiornare ed eseguire query sui dati relazionali. Questo modello è utile quando è necessario scambiare dati contenenti tag XML con altre applicazioni, senza interrompere le applicazioni SQL.

Modello ibrido

La soluzione più appropriata per la modellazione dei dati è spesso costituita da una combinazione di dati relazionali e colonne con tipo di dati xml . È possibile archiviare parte dei dati XML in colonne relazionali e il resto, o l'intero valore XML, in una colonna XML. Questa soluzione può migliorare le prestazioni, perché offre un maggiore controllo sugli indici creati sulle colonne relazionali e sulle caratteristiche di blocco.

I valori da archiviare nelle colonne relazionali dipendono dal carico di lavoro. Se ad esempio per il recupero di tutti i valori XML si utilizza l'espressione del percorso, /Customer/@CustId, alzando di livello il valore dell'attributo CustId in modo da ottenere una colonna relazionale e indicizzando tale colonna sarà possibile eseguire le query molto più rapidamente. Se tuttavia i dati XML sono estensivamente scomposti in colonne relazionali in modo non ridondante, il riassemblaggio potrebbe avere un costo significativo.

Se ad esempio per dati XML altamente strutturati il contenuto di una tabella viene convertito in XML, sarà possibile eseguire il mapping di tutti i valori a colonne relazionali ed eventualmente utilizzare la tecnologia della visualizzazione XML.

Modellazione dei dati tramite il tipo di dati xml

In questa sezione vengono fornite informazioni sulle tecniche di modellazione dei dati disponibili per l'archiviazione XML nativa, che includono l'indicizzazione dei dati XML, l'innalzamento di livello delle proprietà e l'utilizzo di tipi di dati xml tipizzati.

Utilizzo di un'unica tabella o di più tabelle

È possibile creare una colonna con tipo di dati xml in una tabella che contiene altre colonne relazionali, oppure in una tabella separata con una relazione di chiave esterna con una tabella principale.

È consigliabile creare una colonna con tipo di dati xml nella stessa tabella quando si verifica una delle condizioni seguenti:

  • L'applicazione esegue operazioni di recupero dei dati sulla colonna XML e non richiede un indice XML su tale colonna.
  • Si desidera creare un indice XML sulla colonna con tipo di dati xml e la chiave primaria della tabella principale coincide con la chiave di clustering. Per ulteriori informazioni, vedere l'argomento dedicato all'indicizzazione di una colonna con tipo di dati xml.

È consigliabile creare la colonna con tipo di dati xml in una tabella separata se si verificano le condizioni seguenti:

  • Si desidera creare un indice XML sulla colonna con tipo di dati xml, ma la chiave primaria della tabella principale è diversa dalla chiave di clustering, la tabella principale non ha una chiave primaria, oppure la tabella principale è un heap, ovvero è priva di chiave di clustering. Questa situazione può verificarsi quando si utilizza una tabella principale esistente.
  • Si desidera evitare che la scansione della tabella venga rallentata a causa della presenza della colonna XML, che utilizza spazio sia che venga archiviata all'interno delle righe che all'esterno delle righe.

Granularità dei dati XML

La granularità dei dati XML archiviati in una colonna XML è molto importante sia per il blocco che, a un livello inferiore, per gli aggiornamenti. SQL Server utilizza lo stesso meccanismo di blocco per i dati XML e non XML. Di conseguenza, il blocco a livello di riga causa il blocco di tutte le istanze XML presenti nella riga. Quando la granularità è ampia, in uno scenario multiutente il blocco di istanze XML di grandi dimensioni per gli aggiornamenti provoca una diminuzione della velocità effettiva. Una scomposizione eccessiva, invece, comporta la perdita dell'incapsulamento degli oggetti e l'aumento del costo di riassemblaggio.

Il giusto equilibrio tra requisiti di modellazione dei dati e caratteristiche di blocco e aggiornamento è pertanto importante per una progettazione ottimale. In SQL Server 2005, tuttavia, le dimensioni delle istanze XML effettivamente archiviate non costituiscono un fattore critico.

Per aggiornare un'istanza XML, ad esempio, viene utilizzato il nuovo supporto per l'aggiornamento parziale degli oggetti BLOB (Binary Large Object) e degli indici, in cui l'istanza XML archiviata viene confrontata con la versione aggiornata. Nell'aggiornamento parziale degli oggetti BLOB viene eseguito un confronto differenziale tra le due istanze XML e l'aggiornamento viene limitato alle differenze. Nell'aggiornamento parziale degli indici vengono modificate solo le righe che devono essere modificate nell'indice XML.

Tipo di dati xml non tipizzato, tipizzato e vincolato

In SQL Server 2005 il tipo di dati xml implementa lo standard ISO SQL-2003 per il tipo di dati xml. Consente pertanto di archiviare in una colonna XML non tipizzata documenti con formato XML 1.0 corretto e frammenti di contenuto XML, con nodi di testo e un numero arbitrario di elementi di livello principale. Il sistema verifica che il formato dei dati sia corretto, non richiede che la colonna sia associata a schemi XML e rifiuta i dati con formato non corretto in senso esteso. Questo vale anche per le variabili e i parametri XML non tipizzati.

Se sono disponibili schemi XML che descrivono i dati XML, sarà possibile associare tali schemi alla colonna XML, in modo da ottenere un tipo di dati xml tipizzato. Gli schemi XML vengono utilizzati per convalidare i dati, eseguire verifiche dei tipi più precise, rispetto al tipo di dati xml non tipizzato, durante la compilazione delle query e delle istruzioni per la modifica dei dati, nonché ottimizzare l'archiviazione e l'elaborazione delle query.

Utilizzare il tipo di dati xml non tipizzato nelle situazioni seguenti:

  • Non è disponibile uno schema per i dati XML.
  • Gli schemi sono disponibili ma non si desidera che i dati vengano convalidati dal server. Questo avviene talvolta quando un'applicazione esegue una convalida sul lato client prima di archiviare i dati sul server, archivia temporaneamente dati XML non validi in base allo schema oppure utilizza componenti di schema non supportati dal server, ad esempio key o keyref.

Utilizzare il tipo di dati xml tipizzato nelle situazioni seguenti:

  • Gli schemi per i dati XML sono disponibili e si desidera che i dati XML vengano convalidati dal server in base agli schemi XML.
  • Si desidera ottimizzare archiviazione e query basandosi sulle informazioni sui tipi.
  • Si desidera avvalersi delle informazioni sui tipi durante la compilazione delle query.

Nelle colonne, nelle variabili e nei parametri XML tipizzati è possibile archiviare documenti o contenuto XML. Nella dichiarazione è tuttavia necessario specificare, tramite un flag, se si desidera archiviare un documento o contenuto. È inoltre necessario fornire l'insieme degli schemi XML. Specificare DOCUMENT se ogni istanza XML include esattamente un elemento di livello principale, CONTENT in caso contrario. Nella verifica dei tipi eseguita durante la compilazione delle query il compilatore utilizza il flag DOCUMENT per derivare gli elementi singleton di livello principale.

Oltre a tipizzare una colonna XML, è possibile applicare vincoli relazionali (a livello di colonna o di riga) alle colonne con tipo di dati xml tipizzato o non tipizzato. Utilizzare i vincoli nelle situazioni seguenti:

  • Le regole business non possono essere espresse negli schemi XML. Se ad esempio l'indirizzo di consegna di un fiorista deve essere entro 80 Km dal negozio, tale condizione può essere espressa come vincolo sulla colonna XML. Il vincolo può includere metodi per il tipo di dati xml.
  • Il vincolo coinvolge altre colonne XML o non XML nella tabella. Si desidera ad esempio che l'ID di un cliente (/Customer/@CustId) trovato in un'istanza XML corrisponda al valore nella colonna relazionale CustomerID.

Definizione del tipo di documento (DTD, Document Type Definition)

Le colonne, le variabili e i parametri con tipo di dati xml possono essere tipizzati tramite uno schema XML, ma non tramite una definizione DTD. Sia per i tipi di dati xml tipizzati che non tipizzati, è tuttavia possibile utilizzare DTD inline per specificare valori predefiniti e sostituire i riferimenti alle entità con le corrispondenti forme espanse.

È possibile convertire DTD in documenti di schema XML tramite strumenti di terze parti e caricare gli schemi XML nel database.

Indicizzazione di una colonna con tipo di dati xml

È possibile creare indici XML sulle colonne con tipo di dati xml. Vengono indicizzati tutti i tag, i valori e i percorsi delle istanze XML presenti nella colonna, migliorando le prestazioni delle query. Per le applicazioni l'utilizzo di un indice XML può risultare vantaggioso nelle situazioni seguenti:

  • Le query sulle colonne XML sono frequenti nel carico di lavoro. È necessario tenere conto del costo di manutenzione dell'indice XML durante la modifica dei dati.
  • I valori XML sono di grandi dimensioni mentre le parti recuperate sono relativamente piccole. Creando un indice sarà possibile evitare l'analisi completa dei dati in fase di esecuzione ed eseguire ricerche basate sull'indice, per una più efficiente elaborazione delle query.

Il primo indice di una colonna XML è l'indice XML primario. Quando si utilizza tale indice è possibile creare tre tipi di indici XML secondari sulla colonna XML, per eseguire più velocemente le query più comuni, come illustrato nella sezione seguente.

Indice XML primario

Consente di indicizzare tutti i tag, i valori e i percorsi contenuti nelle istanze XML di una colonna XML. La tabella di base, quella in cui si trova la colonna XML, deve avere un indice cluster sulla chiave primaria. La chiave primaria viene utilizzata per correlare le righe nell'indice con le righe nella tabella di base. Dalle colonne XML è possibile recuperare istanze XML complete, ad esempio tramite una query SELECT *. Le query utilizzano l'indice XML primario e restituiscono valori scalari o sottostrutture XML, utilizzando l'indice stesso.

Esempio: creazione di un indice XML primario

Nella maggior parte degli esempi viene utilizzata la tabella T (pk INT PRIMARY KEY, xCol XML), che include una colonna XML non tipizzata. Gli esempi possono essere estesi senza difficoltà ai dati XML tipizzati. Per ulteriori informazioni sull'utilizzo di dati XML tipizzati, vedere Tipo di dati XML. Per semplicità vengono descritte le query per le istanze di dati XML, come illustrato nel codice seguente:

<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
   <title>Writing Secure Code</title>
   <author>
      <first-name>Michael</first-name>
      <last-name>Howard</last-name>
   </author>
   <author>
      <first-name>David</first-name>
      <last-name>LeBlanc</last-name>
   </author>
   <price>39.99</price>
</book>

L'istruzione seguente crea un indice XML di nome idx_xCol sulla colonna XML di nome xCol della tabella T:

CREATE PRIMARY XML INDEX idx_xCol on T (xCol)

Indici XML secondari

Dopo aver creato l'indice XML primario è possibile creare indici XML secondari per accelerare l'esecuzione di diverse classi di query nell'ambito del carico di lavoro. Sono disponibili tre tipi di indici XML secondari, PATH, PROPERTY e VALUE, che risultano utili, rispettivamente, per le query basate sui percorsi, gli scenari che richiedono la gestione di proprietà personalizzate e le query basate sui valori. L'indice PATH crea una struttura b+-tree sulla coppia (percorso, valore) di ogni nodo XML nell'ordine del documento, per tutte le istanze XML nella colonna. L'indice PROPERTY crea una struttura b+-tree cluster sulle coordinate (PK, percorso, valore) in ogni istanza XML, in cui PK è la chiave primaria della tabella di base. Infine, l'indice VALUE crea una struttura b+-tree sulla coppia (valore, percorso) di ogni nodo XML nell'ordine del documento, per tutte le istanze XML nella colonna XML.

Di seguito sono riportate alcune linee guida per la creazione di uno o più indici di questo tipo:

  • Se nel carico di lavoro viene fatto un utilizzo significativo di espressioni di percorso sulle colonne XML, per migliorare le prestazioni del carico di lavoro è possibile utilizzare l'indice XML secondario PATH. Il caso più comune è costituito dall'utilizzo del metodo exist() sulle colonne XML nella clausola WHERE di Transact-SQL.
  • Se il carico di lavoro recupera più valori da singole istanze XML utilizzando espressioni di percorso, può essere utile eseguire il clustering dei percorsi nell'ambito di ogni istanza XML nell'indice PROPERTY. Questa situazione si presenta in genere negli scenari che prevedono l'utilizzo di contenitori di proprietà, quando vengono recuperate le proprietà di un oggetto di cui è noto il valore della chiave primaria.
  • Se il carico di lavoro richiede l'esecuzione di query per il recupero di valori nelle istanze XML, senza conoscere i nomi degli elementi o attributi che contengono tali valori, sarà possibile creare l'indice VALUE. Questa situazione si verifica in genere nelle ricerche su assi discendenti, ad esempio //author[last-name="Howard"], in cui gli elementi <author> possono presentarsi a ogni livello della gerarchia. Si verifica inoltre nelle query che utilizzano caratteri jolly, ad esempio /book [@* = "novel"], in cui la query cerca elementi <book> contenenti un attributo con valore "novel".
Esempio: ricerca basata sul percorso

Si supponga ad esempio che nel carico di lavoro venga frequentemente eseguita la query seguente:

SELECT pk, xCol
FROM   T
WHERE  xCol.exist ('/book/@genre[.="novel"]') = 1

L'espressione di percorso /book/@genre e il valore "novel" corrispondono ai campi chiave dell'indice PATH. Per questo carico di lavoro può essere pertanto utile creare un indice XML secondario di tipo PATH:

CREATE XML INDEX idx_xCol_Path on T (xCol)
   USING XML INDEX idx_xCol FOR PATH
Esempio: recupero delle proprietà di un oggetto

Si consideri la query seguente, che recupera le proprietà relative a genere, titolo e codice ISBN di un libro da ogni riga della tabella T:

SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
    xCol.value ('(/book/title/text())[1]', 'varchar(50)'),
    xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM    T

In questo caso è consigliabile utilizzare l'indice PROPERTY, che viene creato nel modo seguente:

CREATE XML INDEX idx_xCol_Property on T (xCol)
   USING XML INDEX idx_xCol FOR PROPERTY
Esempio: query basata sui valori

Nella query seguente l'asse descendant-or-self (//) specifica un percorso parziale. Per la ricerca basata sul valore del codice ISBN è pertanto consigliabile utilizzare l'indice VALUE:

SELECT xCol
FROM    T
WHERE    xCol.exist ('//book/@ISBN[. = "0-7356-1588-2"]') = 1

L'indice VALUE viene creato nel modo seguente:

CREATE XML INDEX idx_xCol_Value on T (xCol)
   USING XML INDEX idx_xCol FOR VALUE

Indice full-text su una colonna XML

Sulle colonne XML è possibile creare un indice full-text che indicizza il contenuto dei valori XML, ma ignora i tag XML. L'indicizzazione full-text non viene eseguita per i valori degli attributi, perché sono considerati parte del tag e i tag degli elementi vengono utilizzati come delimitatori di token. In alcuni casi è possibile combinare la ricerca full-text con l'indicizzazione XML, procedendo nel modo seguente:

  • Filtrare innanzitutto i valori XML desiderati utilizzando la ricerca full-text SQL.
  • Eseguire quindi una query sui valori XML che utilizzano l'indice XML sulla colonna XML.
Esempio: combinazione di ricerca full-text e query XML

Dopo la creazione dell'indice full-text sulla colonna XML è possibile utilizzare la query seguente per verificare se un valore XML contiene la parola "custom" nel titolo di un libro:

SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'custom') 
AND    xCol.exist('/book/title/text()[contains(.,"custom")]') =1

Il metodo contains() utilizza l'indice full-text per creare un subset dei valori XML contenenti la parola "custom", in qualunque punto del documento. La clausola exist() garantisce che la parola "custom" compare nel titolo di un libro.

Una ricerca full-text che utilizza contains() e la query XQuery contains() hanno semantiche diverse. Nel secondo caso si tratta di una corrispondenza tra sottostringhe, mentre nel primo si tratta di una corrispondenza tra token basata sullo stemming. Se pertanto si ricerca la stringa che include la parola "run" nel titolo, le corrispondenze includeranno le parole "run", "runs" e "running", perché sono soddisfatti sia i criteri della ricerca full-text contains() che quelli della query XQuery contains(). La query non individua tuttavia la parola "customizable" nel titolo, perché la ricerca full-text contains() non riesce, ma la query XQuery contains() è soddisfatta. In genere, per le semplici corrispondenze di sottostringhe è consigliabile rimuovere la clausola contains() della ricerca full-text.

La ricerca full-text utilizza inoltre lo stemming delle parole, mentre la query XQuery contains() richiede una corrispondenza letterale. Questa differenza è illustrata nell'esempio successivo.

Esempio: ricerca full-text su valori XML tramite stemming

Il controllo eseguito dalla query XQuery contains() nell'esempio precedente non può essere in genere eliminato. Si consideri la query seguente:

SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'run') 

La parola "ran" nel documento soddisfa la condizione di ricerca a causa dello stemming. Se si utilizza una query XQuery, inoltre, il contesto della ricerca non viene verificato.

Quando i valori XML vengono scomposti tramite AXSD in colonne relazionali con indicizzazione full-text, le query XPath sulla visualizzazione XML non eseguono una ricerca full-text sulle tabelle sottostanti.

Innalzamento di livello delle proprietà

Se le query vengono eseguite principalmente su un piccolo numero di valori di attributi ed elementi, sarà possibile alzare tali quantità al livello di colonne relazionali. Questo risulta utile quando le query vengono eseguite su una piccola parte dei dati XML mentre viene recuperata l'intera istanza XML. Non è necessario creare un indice XML sulla colonna XML, ma è possibile indicizzare la colonna alzata di livello. Le query devono essere espressamente scritte in modo da utilizzare la colonna alzata di livello, poiché Query Optimizer non reindirizza sulla colonna alzata di livello le query eseguite sulla colonna XML.

La colonna alzata di livello può essere una colonna calcolata nella stessa tabella oppure una colonna separata e gestita dall'utente in un'altra tabella. È sufficiente utilizzare una colonna calcolata nella stessa tabella quando vengono alzati di livello valori singleton da ogni istanza XML. Per le proprietà multivalore, invece, è necessario creare una tabella separata per la proprietà, come illustrato nella sezione seguente.

Colonna calcolata basata sul tipo di dati xml

Per creare una colonna calcolata è possibile utilizzare una funzione definita dall'utente che richiama metodi per il tipo di dati xml. Il tipo della colonna calcolata può essere qualsiasi tipo SQL, incluso il tipo XML, come illustrato nell'esempio seguente.

Esempio: colonna calcolata basata su un metodo per il tipo di dati xml

Creare la funzione definita dall'utente per il codice ISBN di un libro:

CREATE FUNCTION udf_get_book_ISBN (@xData xml)
RETURNS varchar(20)
BEGIN
   DECLARE @ISBN   varchar(20)
   SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)')
   RETURN @ISBN 
END

Aggiungere alla tabella una colonna calcolata per il codice ISBN:

ALTER TABLE      T
ADD   ISBN AS dbo.udf_get_book_ISBN(xCol)

La colonna calcolata può essere indicizzata come di consueto.

Esempio: query su una colonna calcolata basata sui metodi per il tipo di dati xml

Per ottenere l'elemento <book> con codice ISBN uguale a 0-7356-1588-2:

SELECT xCol
FROM   T
WHERE  xCol.exist('/book/@ISBN[. = "0-7356-1588-2"]') = 1

La query sulla colonna XML può essere riscritta in modo da utilizzare la colonna calcolata, come illustrato di seguito:

SELECT xCol
FROM   T
WHERE  ISBN = '0-7356-1588-2'

È possibile creare una funzione definita dall'utente per restituire il tipo di dati xml e una colonna calcolata, ma non è possibile creare un indice XML sulla colonna XML calcolata.

Creazione di tabelle di proprietà

È possibile alzare di livello alcune delle proprietà multivalore dei dati XML fino a ottenere una o più tabelle, creare indici su tali tabelle e modificare la destinazione delle query in modo da utilizzarle. Un tipico scenario è quello in cui la maggior parte del carico di lavoro delle query è costituita da un piccolo numero di proprietà. È possibile eseguire le operazioni seguenti:

  • Creare una o più tabelle in cui inserire le proprietà multivalore. Può essere conveniente archiviare una proprietà per tabella e duplicare la chiave primaria della tabella di base nelle tabelle di proprietà, per poter eseguire il join all'indietro alla tabella di base.
  • Se si desidera mantenere l'ordine relativo delle proprietà, sarà necessario introdurre una colonna separata per l'ordine relativo.
  • Creare trigger sulla colonna XML per eseguire operazioni di manutenzione delle tabelle di proprietà. Nell'ambito dei trigger, eseguire una delle operazioni seguenti:
    • Utilizzare i metodi per il tipo di dati xml, ad esempio nodes() e value(), per inserire ed eliminare righe nelle tabelle di proprietà.
    • Creare funzioni di flusso valutate a livello di tabella in Common Language Runtime (CLR) per inserire ed eliminare righe nelle tabelle di proprietà.
    • Scrivere query per l'accesso SQL alle tabelle di proprietà e per l'accesso XML alla colonna XML nella tabella di base, utilizzandone la chiave primaria per creare join tra le tabelle.
Esempio: creazione di una tabella di proprietà

Si supponga ad esempio di voler alzare di livello i nomi degli autori. Poiché un libro può avere più autori, il nome è una proprietà multivalore. Ogni nome è archiviato in una riga separata di una tabella di proprietà. La chiave primaria della tabella di base viene duplicata nella tabella di proprietà per consentire il join all'indietro.

create table tblPropAuthor (propPK int, propAuthor varchar(max))
Esempio: creazione di una funzione definita dall'utente per la generazione di un set di righe da un'istanza XML

La seguente funzione valutata a livello di tabella, udf_XML2Table, accetta un valore di chiave primaria e un'istanza XML. Recupera il nome di tutti gli autori degli elementi <book> e restituisce un set di righe composto da coppie chiave primaria-nome.

create function udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (propPK int, propAuthor varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
      select @pk, nref.value('.', 'varchar(max)')
      from   @xCol.nodes('/book/author/first-name') R(nref)
      return
end
Esempio: creazione di trigger per il popolamento di una tabella di proprietà

Il trigger di inserimento consente di inserire righe nella tabella di proprietà:

create trigger trg_docs_INS on T for insert
as
      declare @wantedXML xml
      declare @FK int
      select @wantedXML = xCol from inserted
      select @FK = PK from inserted

   insert into tblPropAuthor
   select * from dbo.udf_XML2Table(@FK, @wantedXML)

Il trigger di eliminazione consente di eliminare righe dalla tabella di proprietà, in base al valore della chiave primaria delle righe eliminate:

create trigger trg_docs_DEL on T for delete
as
   declare @FK int
   select @FK = PK from deleted
   delete tblPropAuthor where propPK = @FK

Il trigger di aggiornamento consente di eliminare dalla tabella di proprietà le righe esistenti corrispondenti all'istanza XML aggiornata e di inserire nuove righe nella tabella di proprietà:

create trigger trg_docs_UPD
on T
for update
as
if update(xCol) or update(pk)
begin
      declare @FK int
      declare @wantedXML xml
      select @FK = PK from deleted
      delete tblPropAuthor where propPK = @FK

   select @wantedXML = xCol from inserted
   select @FK = pk from inserted

   insert into tblPropAuthor 
      select * from dbo.udf_XML2Table(@FK, @wantedXML)
end
Esempio: ricerca di istanze XML i cui autori hanno nome "David"

È possibile creare la query in modo da ricercare valori nella colonna XML oppure in modo da ricercare il nome "David" nella tabella di proprietà ed eseguire un join all'indietro alla tabella di base, per restituire l'istanza XML. Ad esempio:

SELECT xCol 
FROM     T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE    tblPropAuthor.propAuthor = 'David'
Esempio: soluzione che utilizza una funzione di flusso CLR valutata a livello di tabella

Per creare questa soluzione è necessario eseguire i passaggi seguenti:

  1. Definire una classe CLR, SqlReaderBase, che implementa ISqlReader e genera un output di flusso valutato a livello di tabella, applicando un'espressione di percorso a un'istanza XML.
  2. Creare un assembly e una funzione Transact-SQL definita dall'utente per avviare la classe CLR.
  3. Definire i trigger di inserimento, aggiornamento ed eliminazione utilizzando la funzione definita dall'utente per la manutenzione delle tabelle di proprietà.

A tale scopo è innanzitutto necessario creare la funzione CLR di flusso. Il tipo di dati xml viene esposto come classe SqlXml gestita in ADO.NET e supporta il metodo CreateReader(), che restituisce un oggetto XmlReader.

[!NOTA] Il codice di esempio in questa sezione utilizza XPathDocument e XPathNavigator, che impongono il caricamento in memoria di tutti i documenti XML. Se nella propria applicazione si utilizza codice analogo per elaborare numerosi documenti XML di grandi dimensioni, sarà necessario ricordare che tale codice non è scalabile. Se possibile, è preferibile utilizzare allocazioni di memoria di piccole dimensioni e utilizzare interfacce di flusso. Per ulteriori informazioni sulle prestazioni, vedere Architecture of CLR Integration.

public class c_streaming_xml_tvf {
   public static ISqlReader streaming_xml_tvf 
(SqlXml xmlDoc, string pathExpression) {
      return (new TestSqlReaderBase (xmlDoc, pathExpression));
   }
}

// Class that implements ISqlReader
public class TestSqlReaderBase : ISqlReader {
XPathNodeIterator m_iterator;         
   public SqlChars FirstName;
// Metadata for current resultset
private SqlMetaData[] m_rgSqlMetaData;      

   public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) {   
      // Variables for XPath navigation
      XPathDocument xDoc;
      XPathNavigator xNav;
      XPathExpression xPath;
   
      // Set sql metadata
      m_rgSqlMetaData = new SqlMetaData[1];
      m_rgSqlMetaData[0] = new SqlMetaData ("FirstName",  
SqlDbType.NVarChar,50);   
   
      //Set up the Navigator
      if (!xmlDoc.IsNull)
          xDoc = new XPathDocument (xmlDoc.CreateReader());
      else
          xDoc = new XPathDocument ();
      xNav = xDoc.CreateNavigator();
      xPath = xNav.Compile (pathExpression);
      m_iterator = xNav.Select(xPath);
   }
   public bool Read() {
      bool moreRows = true;
      if (moreRows = m_iterator.MoveNext())
         FirstName = new SqlChars (m_iterator.Current.Value);
      return moreRows;
   }
}

Creare quindi un assembly e una funzione Transact-SQL definita dall'utente, SQL_streaming_xml_tvf (non illustrata), che corrisponde alla funzione CLR streaming_xml_tvf. La funzione definita dall'utente viene utilizzata per definire la funzione valutata a livello di tabella, CLR_udf_XML2Table, per la generazione del set di righe:

create function CLR_udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (FK int, FirstName varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
   select @pk, FirstName 
   FROM   SQL_streaming_xml_tvf (@xCol, '/book/author/first-name')
      return
end

Definire infine i trigger come illustrato nell'esempio "Creazione di trigger per il popolamento di una tabella di proprietà", sostituendo tuttavia la funzione udf_XML2Table con la funzione CLR_udf_XML2Table. Il trigger di inserimento è illustrato nell'esempio seguente:

create trigger CLR_trg_docs_INS on T for insert
as
   declare @wantedXML xml
   declare @FK int
   select @wantedXML = xCol from inserted
   select @FK = PK from inserted

   insert into tblPropAuthor
      select *
   from    dbo.CLR_udf_XML2Table(@FK, @wantedXML)

Il trigger di eliminazione è identico alla versione non CLR, mentre nel trigger di inserimento viene semplicemente sostituita la funzione udf_XML2Table() con la funzione CLR_udf_XML2Table().

Insiemi di schemi XML

Un insieme di schemi XML è un'entità di metadati il cui ambito è definito da uno schema relazionale. Contiene uno o più schemi XML che possono essere correlati, ad esempio tramite <xs:import>, oppure non correlati. Nell'ambito dell'insieme di schemi XML i singoli schemi XML vengono identificati tramite i rispettivi spazi dei nomi di destinazione.

Per creare un insieme di schemi XML è necessario utilizzare la sintassi CREATE XML SCHEMA COLLECTION (Transact-SQL) e specificare uno o più schemi XML. Tramite la sintassi ALTER XML SCHEMA COLLECTION (Transact-SQL) è possibile aggiungere altri componenti a uno schema XML esistente e altri schemi a un insieme di schemi XML. Come ogni altro oggetto SQL, gli insiemi di schemi XML possono essere protetti utilizzando il modello di protezione di SQL Server 2005.

Colonne con tipizzazione multipla

È possibile utilizzare l'insieme di schemi XML C per tipizzare la colonna XML xCol in base a più schemi XML. È inoltre possibile utilizzare i flag DOCUMENT e CONTENT per specificare se nella colonna xCol è possibile archiviare, rispettivamente, strutture o frammenti XML.

Nel caso di DOCUMENT, ogni istanza XML specifica lo spazio dei nomi di destinazione dell'elemento di livello principale dell'istanza, che viene tipizzata e convalidata in base a tale spazio dei nomi. Nel caso di CONTENT, invece, ogni elemento di livello principale può specificare qualsiasi spazio dei nomi di destinazione presente in C. L'istanza XML viene convalidata e tipizzata in base a tutti gli spazi dei nomi di destinazione indicati al suo interno.

Evoluzione degli schemi

Gli insiemi di schemi XML possono essere utilizzati per tipizzare colonne, variabili e parametri XML e forniscono un meccanismo per l'evoluzione degli schemi XML. Si supponga ad esempio di aggiungere all'insieme di schemi XML C uno schema XML con spazio dei nomi di destinazione BOOK-V1. La colonna XML xCol, tipizzata tramite C, può archiviare dati XML conformi allo schema BOOK-V1.

Si supponga quindi che un'applicazione abbia l'esigenza di estendere lo schema XML con nuovi componenti, ad esempio definizioni di tipi complessi e dichiarazioni di elementi di livello principale. Questi nuovi componenti possono essere aggiunti allo schema BOOK-V1 senza che sia necessario ripetere la convalida dei dati esistenti nella colonna XML.

Si supponga che, in un secondo momento, l'applicazione abbia l'esigenza di fornire una nuova versione dello schema XML e che selezioni lo spazio dei nomi di destinazione BOOK-V2. Tale schema XML può essere aggiunto a C. Nella colonna XML è possibile archiviare istanze di BOOK-V1 e BOOK-V2, nonché eseguire query e operazioni di modifica dei dati sulle istanze XML conformi a tali spazi dei nomi.

Caricamento di dati XML

Trasferimento di dati XML da SQL Server 2000 a SQL Server 2005

Per trasferire dati XML a SQL Server 2005 è possibile procedere in diversi modi. Ad esempio:

  • Se i dati si trovano un una colonna di tipo [n]text o image in un database di SQL Server 2000, sarà possibile importare la tabella in un database di SQL Server 2005 utilizzando SQL Server 2005 Integration Services (SSIS). Modificare il tipo della colonna in XML utilizzando l'istruzione ALTER TABLE.
  • È possibile eseguire una copia di massa dei dati da SQL Server 2000 utilizzando bcp out e quindi eseguire un inserimento di massa dei dati nel database di SQL Server 2005 utilizzando bcp in.
  • Se i dati si trovano in colonne relazionali in un database di SQL Server 2000, creare una nuova tabella con una colonna di tipo [n]text e, facoltativamente, una colonna chiave primaria per un identificatore di riga. Utilizzare la programmazione sul lato client per recuperare i dati XML generati nel server con FOR XML e scriverli nella colonna [n]text. Utilizzare quindi le tecniche illustrate in precedenza per trasferire i dati in un database di SQL Server 2005. È possibile scegliere di scrivere i dati XML direttamente in una colonna XML nel database di SQL Server 2005.
Esempio: modifica del tipo di dati di una colonna in xml

Si supponga di voler modificare il tipo di dati di una colonna [n]text o image di nome XYZ, nella tabella R, nel tipo di dati xml non tipizzato. Per eseguire questo tipo di modifica, è possibile utilizzare l'istruzione seguente:

ALTER TABLE R ALTER COLUMN XYZ XML
  • Se necessario, la destinazione può essere una colonna XML tipizzata specificando un insieme di schemi XML.

Caricamento di massa di dati XML

È possibile caricare i dati XML in massa nel server utilizzando le funzionalità per il caricamento di massa disponibili in SQL Server, ad esempio bcp. OPENROWSET consente di caricare dati in una colonna XML da uno o più file, come illustrato nell'esempio seguente.

Esempio: caricamento di dati XML da un file

In questo esempio viene illustrato l'inserimento di una riga nella tabella T. Il valore della colonna XML viene caricato dal file C:\MyFile\xmlfile.xml come CLOB e alla colonna di tipo integer viene fornito il valore 10.

INSERT INTO T
SELECT 10, xCol
FROM    (SELECT *    
    FROM OPENROWSET (BULK 'C:\MyFile\xmlfile.xml', SINGLE_CLOB) 
 AS xCol) AS R(xCol)

Codifica del testo

SQL Server 2005 archivia i dati XML in formato Unicode (UTF-16). I dati XML recuperati dal server vengono restituiti con la codifica UTF-16. Se si desidera una codifica diversa, sarà necessario eseguire la conversione appropriata sui dati recuperati. Per i dati XML viene talvolta utilizzata una codifica diversa. In questo caso è necessario prestare particolare attenzione durante il caricamento dei dati. Ad esempio:

  • Se il testo XML è in formato Unicode (UCS-2, UTF-16), sarà possibile assegnarlo senza problemi a una colonna, una variabile o un parametro XML.
  • Se viene utilizzata una codifica implicita diversa da Unicode, a causa della tabella codici di origine, la tabella codici per le stringhe nel database dovrà essere uguale o compatibile con i punti di codice da caricare. Se necessario, utilizzare COLLATE. Se tale tabella codici non esiste sul server, sarà necessario aggiungere una dichiarazione XML esplicita con la codifica appropriata.
  • Per utilizzare una codifica esplicita utilizzare il tipo di dati varbinary(), che non interagisce in alcun modo con le tabelle codici, oppure utilizzare un tipo stringa della tabella codici appropriata. Assegnare quindi i dati a una colonna, a una variabile o a un parametro XML.
Esempio: impostazione di una codifica in modo esplicito

Si consideri un documento XML di nome vcdoc, archiviato come varchar(max) e che non include una dichiarazione XML esplicita. L'istruzione seguente aggiunge una dichiarazione XML con la codifica "iso8859-1", concatena il documento XML, esegue il cast del risultato a varbinary(max), in modo da mantenere la rappresentazione dei byte, e infine esegue il cast al tipo di dati XML. Questo consente al processore XML di analizzare i dati in base alla codifica specificata, "iso8859-1", e di generare la rappresentazione UTF-16 corrispondente per i valori stringa.

SELECT CAST( 
CAST (('<?xml version="1.0" encoding="iso8859-1"?>'+ vcdoc) AS VARBINARY (MAX)) 
 AS XML)

Linguaggio XQuery e inferenza dei tipi

In Transact-SQL è incorporato il linguaggio XQuery, che consente di eseguire query sul tipo di dati xml. Questo linguaggio è attualmente in fase di sviluppo da parte del World Wide Web Consortium (W3C), a cui partecipano tutti i principali fornitori di database che collaborano con Microsoft. XQuery include il linguaggio di esplorazione XPath versione 2.0. Per il tipo di dati xml sono inoltre disponibili costrutti di linguaggio per la modifica dei dati. Per ulteriori informazioni sui costrutti, le funzioni e gli operatori XQuery supportati in SQL Server, vedere Funzioni XQuery per il tipo di dati XML.

Modello di errore

Gli errori di compilazione derivano da espressioni XQuery e istruzioni XML DML con sintassi non corretta. Durante la fase di compilazione viene verificata la correttezza dei tipi statici di espressioni XQuery e istruzioni DML e vengono utilizzati schemi XML per l'inferenza dei tipi per i tipi di dati xml tipizzati. Viene generato un errore di tipo statico se è possibile che, in fase di esecuzione, un'espressione non riesca a causa di una violazione dell'indipendenza dai tipi. Questo si verifica ad esempio quando una stringa viene sommata a un valore integer oppure si esegue una query per individuare dati tipizzati in un nodo inesistente.

Diversamente da quanto previsto dallo standard W3C, gli errori di run-time di XQuery vengono convertiti in sequenze vuote, che possono essere propagate come valori XML o NULL al risultato della query, a seconda del contesto di chiamata.

Per evitare gli errori statici è possibile eseguire il cast esplicito al tipo corretto, tuttavia gli errori di cast che si verificano in fase di esecuzione vengono convertiti in sequenze vuote.

La verifica dei tipi è illustrata in dettaglio nelle sezioni seguenti.

Verifica dei singleton

Se il compilatore non è in grado di determinare se un determinato valore singleton è garantito in fase di esecuzione, i passi, i parametri delle funzioni e gli operatori che richiedono un singleton restituiranno un errore. Questo problema si presenta di frequente con i dati non tipizzati. Per la ricerca di un attributo, ad esempio, è necessario un elemento padre singleton. È sufficiente un numero ordinale che seleziona un singolo nodo padre. Per valutare una combinazione nodo()-valore() per l'estrazione dei valori degli attributi potrebbe non essere necessario specificare il numero ordinale, come illustrato nell'esempio seguente.

Esempio: singleton noto

In questo esempio il metodo nodes() genera una riga distinta per ogni elemento <book>. Il metodo value(), valutato su un nodo <book>, estrae il valore di @genre che, essendo un attributo, è un singleton.

SELECT nref.value('@genre', 'varchar(max)') LastName
FROM   T CROSS APPLY xCol.nodes('//book') AS R(nref)

Per la verifica dei dati XML tipizzati viene utilizzato uno schema XML. Se un determinato nodo è specificato come singleton nello schema XML, il compilatore utilizzerà tale informazione e non verrà generato alcun errore. In caso contrario sarà necessario specificare un numero ordinale che seleziona un singolo nodo. In particolare, l'utilizzo dell'asse descendant-or-self (//), ad esempio in /book//title, impedisce l'inferenza della cardinalità singleton per l'elemento <title>, anche se nello schema XML è specificato come tale, ed è pertanto necessario riscriverlo come (/book//title)[1].

La differenza tra //first-name[1] e (//first-name)[1] è molto importante per la verifica dei tipi. Nel primo caso viene restituita una sequenza di nodi <first-name>, in cui ogni nodo è il nodo <first-name> più a sinistra tra i nodi di pari livello. Nel secondo caso viene restituito il primo nodo <first-name> singleton nell'ordine del documento nell'istanza XML.

Esempio: utilizzo del metodo value()

Per la query seguente su una colonna XML non tipizzata viene restituito un errore statico di compilazione, perché il primo argomento del metodo value() deve essere un nodo singleton e il compilatore non è in grado di determinare se in fase di esecuzione verrà rilevato un solo nodo <last-name>:

SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName
FROM   T

Una possibile soluzione è la seguente:

SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName
FROM   T

Tale soluzione non risolve tuttavia il problema, perché in ogni istanza XML possono essere presenti più nodi <author>. Per risolvere il problema è necessario riscrivere il codice come segue:

SELECT xCol.value('(//author/last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T

Questa query restituisce il valore del primo elemento <last-name> in ogni istanza XML.

Asse parent

Ai nodi di cui non è possibile determinare il tipo viene attribuito il tipo anyType, di cui non è possibile eseguire il cast implicito ad altri tipi. Questa situazione si presenta soprattutto durante l'esplorazione tramite l'asse parent. In xCol.query('/book/@genre/../price'), ad esempio, al nodo padre viene attribuito il tipo anyType. Un elemento può essere inoltre definito come anyType in uno schema XML. In entrambi i casi, la mancanza di informazioni precise sul tipo comporta spesso la generazione di errori statici e richiede il cast esplicito dei valori atomici ai relativi tipi specifici.

Funzioni di accesso data(), text() e string()

Nel linguaggio XQuery è disponibile la funzione fn:data(), che consente di estrarre valori scalari tipizzati dai nodi, il test del nodo text(), che restituisce nodi di testo, e la funzione fn:string(), che restituisce il valore stringa di un nodo. L'utilizzo di tali funzioni non è tuttavia intuitivo. Di seguito sono riportate le linee guida da seguire per il corretto utilizzo di tali funzioni in SQL Server 2005. Per illustrare i concetti viene utilizzata l'istanza XML <age>12</age>.

  • Dati XML non tipizzati: l'espressione di percorso /age/text() restituisce il nodo di testo "12". La funzione fn:data(/age) restituisce il valore stringa "12", così come la funzione fn:string(/age).
  • Dati XML tipizzati: l'espressione /age/text() restituisce un errore statico per ogni elemento <age> con tipizzazione semplice. La funzione fn:data(/age) restituisce invece il valore integer 12, mentre fn:string(/age) restituisce la stringa "12".

Funzioni e operatori sui tipi unione

I tipi unione devono essere gestiti con particolare attenzione, perché possono causare problemi durante la verifica dei tipi. Due di questi problemi sono illustrati negli esempi seguenti.

Esempio: funzione su tipo unione

Si consideri la seguente definizione per l'elemento <r> di tipo unione:

<xs:element name="r">
<xs:simpleType>
   <xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>

Nel contesto di XQuery la funzione per il calcolo della media, fn:avg (//r), restituisce un errore statico perché il compilatore XQuery non è in grado di sommare valori di tipi diversi (xs:int, xs:float o xs:double) per gli elementi <r> nell'argomento di fn:avg(). Per risolvere il problema, è necessario riscrivere la chiamata alla funzione nel modo seguente: fn:avg(for $r in //r return $r cast as xs:double ?).

Esempio: operatore su tipo unione

L'operazione di addizione ('+') richiede tipi di operandi specifici. Di conseguenza, per l'elemento <r> l'espressione (//r)[1] + 1 restituisce un errore statico con la definizione di tipo illustrata in precedenza. È possibile risolvere il problema riscrivendo l'espressione nel modo seguente: (//r)[1] cast as xs:int? +1, dove "?" indica zero o una occorrenza. SQL Server 2005 richiede "cast as" con "?", perché ogni operazione di cast può generare una sequenza vuota in caso di errore di run-time.

Metodi value(), nodes() e OpenXML()

È possibile utilizzare più metodi value() su un tipo di dati xml in una clausola SELECT per generare un set di righe di valori estratti. Il metodo nodes() crea un riferimento interno per ogni nodo selezionato, che può essere utilizzato per ulteriori query. Se sono presenti numerose colonne e, talvolta, quando per la generazione vengono utilizzate espressioni di percorso complesse, è possibile utilizzare una combinazione dei metodi nodes() e value() per generare il set di righe in modo più efficiente.

Il metodo nodes() crea istanze di uno speciale tipo di dati xml, ognuna delle quali ha contesto impostato su un nodo selezionato diverso. Questo tipo di istanza XML supporta i metodi query(), value(), nodes() ed exist() e può essere utilizzato nelle aggregazioni count(*). In tutti gli altri casi provoca un errore.

Esempio: utilizzo del metodo nodes()

Si supponga di voler estrarre il nome e il cognome degli autori il cui nome è diverso da "David". Si supponga inoltre di voler estrarre tali informazioni sotto forma di un set di righe contenente due colonne, FirstName e LastName. A tale scopo è possibile utilizzare i metodi nodes() e value(), come illustrato nell'esempio seguente:

SELECT nref.value('(first-name/text())[1]', 'nvarchar(50)') FirstName,
       nref.value('(last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE  nref.exist('first-name[. != "David"]') = 1

In questo esempio nodes('//author') crea un set di righe di riferimenti agli elementi <author> per ogni istanza XML. I nomi e i cognomi degli autori vengono ottenuti valutando i metodi value() relativamente a tali riferimenti.

In SQL Server 2000 per generare un set di righe da un'istanza XML è possibile utilizzare OpenXml(). È possibile specificare lo schema relazionale del set di righe e il mapping tra i valori presenti nell'istanza XML e le colonne nel set di righe.

Esempio: utilizzo di OpenXml() sul tipo di dati xml

È possibile riscrivere la query dell'esempio precedente utilizzando OpenXml() come illustrato nell'esempio seguente. A tale scopo viene creato un cursore che legge ogni istanza XML in una variabile XML e quindi applica OpenXML() alla variabile:

DECLARE name_cursor CURSOR
FOR
   SELECT xCol 
   FROM   T
OPEN name_cursor
DECLARE @xmlVal XML
DECLARE @idoc int
FETCH NEXT FROM name_cursor INTO @xmlVal

WHILE (@@FETCH_STATUS = 0)
BEGIN
   EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlVal
   SELECT   *
   FROM   OPENXML (@idoc, '//author')
          WITH (FirstName  varchar(50) 'first-name',
                LastName   varchar(50) 'last-name') R
   WHERE  R.FirstName != 'David'

   EXEC sp_xml_removedocument @idoc
   FETCH NEXT FROM name_cursor INTO @xmlVal
END
CLOSE name_cursor
DEALLOCATE name_cursor 

OpenXml() crea una rappresentazione in memoria e utilizza tabelle di lavoro anziché Query Processor. Si basa inoltre sul processore XPath versione 1.0 di MSXML versione 3.0, anziché sul motore XQuery. Le tabelle di lavoro non vengono condivise tra più chiamate a OpenXml(), nemmeno sulla stessa istanza XML. Questo limita la scalabilità del metodo. Se non è specificata la clausola WITH, OpenXml() consente di accedere a un formato di tabella edge per i dati XML e di utilizzare la parte rimanente del valore XML in una colonna di "overflow" separata.

La combinazione delle funzioni nodes() e value() consente di utilizzare gli indici XML in modo molto efficiente e, di conseguenza, offre maggiore scalabilità di OpenXml().

Utilizzo di FOR XML per la generazione di valori XML da set di righe

È possibile generare un'istanza del tipo di dati xml da un set di righe utilizzando FOR XML con la nuova direttiva TYPE.

Il risultato può essere assegnato a una colonna, a una variabile o a un parametro con tipo di dati xml. L'istruzione FOR XML può essere inoltre nidificata, consentendo di creare qualsiasi tipo di struttura gerarchica. Le istruzioni FOR XML nidificate sono molto più facili da scrivere rispetto a FOR XML EXPLICIT, ma forniscono prestazioni inferiori in caso di gerarchie con numerosi livelli di nidificazione. L'istruzione FOR XML introduce inoltre una nuova modalità PATH, che specifica il percorso della struttura XML in cui compare un determinato valore di colonna.

La nuova direttiva FOR XML TYPE consente di definire visualizzazioni XML in sola lettura su dati relazionali con la sintassi SQL. Su tale visualizzazione è possibile eseguire query utilizzando istruzioni SQL e istruzioni XQuery incorporate, come illustrato nell'esempio seguente. È possibile fare riferimento a tali visualizzazioni SQL anche nelle stored procedure.

Esempio: visualizzazione SQL che restituisce un tipo di dati xml generato

La definizione di vista SQL seguente crea una visualizzazione XML su una colonna relazionale, utilizzando la chiave primaria e gli autori dei libri recuperati da una colonna XML:

CREATE VIEW V (xmlVal) AS
SELECT pk, xCol.query('/book/author')
FROM   T
FOR XML AUTO, TYPE

La vista V contiene una singola riga con una singola colonna con nome xmlVal e tipo XML. Può essere utilizzata nelle query come una normale istanza del tipo di dati xml. La query seguente, ad esempio, restituisce l'autore il cui nome è "David":

SELECT xmlVal.query('//author[first-name = "David"]')
FROM   V

Le definizioni delle viste SQL sono analoghe alle visualizzazioni XML create utilizzando schemi con annotazioni, ma esistono alcune differenze importanti. Le definizioni delle viste SQL sono in sola lettura e devono essere manipolate tramite istruzioni XQuery incorporate. Le visualizzazioni XML vengono create utilizzando schemi con annotazioni. Inoltre, in una vista SQL il risultato XML viene materializzato prima di applicare l'espressione XQuery, mentre le query XPath sulle visualizzazioni XML restituiscono query SQL sulle tabelle sottostanti.

Aggiunta di una regola business

Per aggiungere una regola business ai dati XML è possibile procedere in vari modi:

  • È possibile creare vincoli a livello di riga o colonna per applicare vincoli specifici del dominio durante la modifica e l'inserimento dei dati XML.
  • È possibile creare sulla colonna XML un trigger che viene attivato all'inserimento o all'aggiornamento dei valori nella colonna. Tale trigger può contenere regole di convalida specifiche del dominio o popolare tabelle di proprietà.
  • È possibile creare funzioni SQLCLR in codice gestito a cui passare valori XML e utilizzare le funzionalità di elaborazione XML offerte dallo spazio dei nomi System.Xml. È ad esempio possibile applicare la trasformazione XSL ai dati XML. In alternativa è possibile deserializzare il valore XML in una o più classi gestite e utilizzare codice gestito per eseguire le operazioni desiderate su tali classi.
  • È possibile creare funzioni e stored procedure Transact-SQL che avviano l'elaborazione della colonna XML in base alle esigenze aziendali.
Esempio: applicazione della trasformazione XSL

Si consideri la funzione CLR TransformXml(), che accetta un'istanza del tipo di dati xml e una trasformazione XSL archiviata in un file, applica la trasformazione ai dati XML e quindi restituisce il valore XML trasformato come risultato. Di seguito è riportato lo scheletro della funzione scritta in C#:

public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
   // Load XSL transformation
   XslCompiledTransform xform = new XslCompiledTransform();
   XPathDocument xslDoc = new XPathDocument (xslPath);
   xform.Load(xslDoc);

   // Load XML data 
   XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());

   // Return the transformed value
   MemoryStream xsltResult = new MemoryStream();
   xform.Transform(xDoc, null, xsltResult);
   SqlXml retSqlXml = new SqlXml(xsltResult);
   return (retSqlXml);
} 

Dopo aver registrato l'assembly e creato la funzione Transact-SQL definita dall'utente SqlXslTransform(), che corrisponde a TransformXml(), è possibile richiamare la funzione da Transact-SQL come illustrato nella query seguente:

SELECT SqlXslTransform (xCol, 'C:\MyFile\xsltransform.xsl')
FROM    T
WHERE  xCol.exist('/book/title/text()[contains(.,"custom")]') =1

Il risultato della query contiene un set di righe con il valore XML trasformato.

SQLCLR estende le possibilità di scomporre dati XML in tabelle o di alzare il livello delle proprietà, nonché di eseguire query sui dati XML tramite l'utilizzo di classi gestite nello spazio dei nomi System.Xml. Per ulteriori informazioni, vedere la documentazione in linea di SQL Server e la documentazione di .NET Framework SDK.

Query tra domini

Se i dati risiedono in una combinazione di colonne relazionali e con tipo di dati xml, può essere necessario scrivere query che uniscono l'elaborazione dei dati relazionali e XML. È ad esempio possibile convertire i dati presenti nelle colonne relazionali e XML in un'istanza del tipo di dati xml utilizzando FOR XML, e quindi eseguire una query su tali dati utilizzando XQuery. Viceversa, è possibile generare un set di righe da valori XML e quindi eseguire una query su tale set di righe utilizzando Transact-SQL.

Una soluzione più pratica ed efficiente per scrivere query tra domini consiste nell'utilizzare il valore di una colonna o variabile SQL nelle espressioni XML DML o XQuery:

  • Per utilizzare il valore di una variabile SQL in un'espressione XML DML o XQuery, è possibile utilizzare sql:variable().
  • Per utilizzare i valori di una colonna relazionale in un'espressione XML DML o XQuery, è possibile utilizzare sql:column().

Questi due approcci consentono di utilizzare query con parametri nelle applicazioni, come illustrato nell'esempio seguente. I tipi di dati XML e definiti dall'utente non sono tuttavia consentiti in sql:variable() e sql:column().

Esempio: query tra domini tramite sql:variable()

La query seguente è una versione modificata di quella illustrata nell'esempio "Query su una colonna calcolata basata sui metodi per il tipo di dati xml". Nella versione seguente viene passato uno specifico codice ISBN utilizzando la variabile SQL @isbn. Sostituendo la costante con sql:variable(), è possibile utilizzare la query per ricercare qualsiasi codice ISBN, non solo gli elementi con codice ISBN uguale a 0-7356-1588-2.

DECLARE @isbn varchar(20)
SET     @isbn = '0-7356-1588-2'
SELECT  xCol
FROM    T
WHERE   xCol.exist ('/book/@ISBN[. = sql:variable("@isbn")]') = 1

sql:column() ha un utilizzo analogo e offre vantaggi aggiuntivi. Per migliorare l'efficienza è possibile utilizzare indici sulla colonna, secondo quanto stabilito dall'utilità di ottimizzazione query basata sui costi. La colonna calcolata può inoltre contenere una proprietà alzata di livello.

Viste del catalogo per il supporto XML nativo

Sono disponibili viste del catalogo che forniscono informazioni sui metadati per l'utilizzo dei valori XML. Alcune di queste viste sono illustrate nella sezione seguente.

Indici XML

Le voci di un indice XML compaiono nella vista del catalogo sys.indexes con tipo di indice 3. La colonna del nome contiene il nome dell'indice XML.

Gli indici XML vengono registrati anche nella vista del catalogo sys.xml_indexes, che contiene tutte le colonne della vista sys.indexes e alcune colonne specifiche, utili per gli indici XML. Il valore NULL nella colonna secondary_type indica un indice XML primario, mentre i valori 'P', 'R' e 'V' indicano, rispettivamente, indici XML secondari di tipo PATH, PROPERTY e VALUE.

Per ottenere informazioni sullo spazio utilizzato dagli indici XML è possibile utilizzare la funzione valutata a livello di tabella sys.dm_db_index_physical_stats, che restituisce informazioni quali il numero di pagine del disco occupate, le dimensioni medie delle righe in byte e il numero dei record, per tutti i tipi di indici, inclusi gli indici XML. Tali informazioni sono disponibili per ogni partizione del database. Gli indici XML utilizzano lo stesso schema di partizionamento e la stessa funzione di partizionamento della tabella di base.

Recupero di insiemi di schemi XML

Gli insiemi di schemi XML sono enumerati nella vista del catalogo sys.xml_schema_collections. L'insieme di schemi XML "sys" è definito dal sistema e contiene gli spazi dei nomi predefiniti che è possibile utilizzare in tutti gli insiemi di schemi XML definiti dall'utente senza doverli caricare esplicitamente. Tale elenco contiene gli spazi dei nomi per xml, xs, xsi, fn e xdt. Sono disponibili altre due viste del catalogo: sys.xml_schema_namespaces, che enumera tutti gli spazi dei nomi in ogni insieme di schemi XML, e sys.xml_components, che enumera tutti i componenti degli schemi XML presenti in ogni schema XML.

La funzione predefinita XML_SCHEMA_NAMESPACE, schemaName, XmlSchemacollectionName, namespace-uri restituisce un'istanza del tipo di dati xml. Tale istanza contiene frammenti di schema XML per gli schemi inclusi in un insieme di schemi XML, ad eccezione degli schemi XML predefiniti.

Per enumerare il contenuto di un insieme di schemi XML è possibile:

  • Scrivere query Transact-SQL sulle viste del catalogo appropriate per gli insiemi di schemi XML.
  • Utilizzare la funzione predefinita XML_SCHEMA_NAMESPACE(). È possibile applicare i metodi per il tipo di dati xml all'output di questa funzione, ma non è possibile modificare gli schemi XML sottostanti.

Queste tecniche di enumerazione sono illustrate negli esempi seguenti.

Esempio: enumerazione degli spazi dei nomi XML in un insieme di schemi XML

Per l'insieme di schemi XML "myCollection" utilizzare la query seguente:

SELECT XSN.name
FROM    sys.xml_schema_collections XSC JOIN sys.xml_schema_namespaces XSN
    ON (XSC.xml_collection_id = XSN.xml_collection_id)
WHERE    XSC.name = 'myCollection'   
Esempio: enumerazione del contenuto di un insieme di schemi XML

L'istruzione seguente enumera il contenuto dell'insieme di schemi XML "myCollection" nell'ambito dello schema relazionale dbo.

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection')

I singoli schemi XML inclusi nell'insieme possono essere ottenuti come istanze del tipo di dati xml, specificando lo spazio dei nomi di destinazione come terzo argomento della funzione XML_SCHEMA_NAMESPACE(), come illustrato nell'esempio seguente.

Esempio: restituzione di uno schema specifico da un insieme di schemi XML

L'istruzione seguente restituisce lo schema XML con spazio dei nomi di destinazione "https://www.microsoft.com/books" dall'insieme di schemi XML "myCollection" nell'ambito dello schema relazionale dbo.

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', 
N'https://www.microsoft.com/books')

Esecuzione di query su schemi XML

Per eseguire query sugli schemi XML caricati in insiemi di schemi XML è possibile:

  • Scrivere query Transact-SQL sulle viste del catalogo per gli spazi dei nomi degli schemi XML.
  • Creare una tabella contenente una colonna con tipo di dati xml per archiviare gli schemi XML e quindi caricarli nel sistema di tipi XML. Per eseguire query sulla colonna XML, è possibile utilizzare i metodi per il tipo di dati xml. È inoltre possibile creare un indice XML sulla colonna. Questo approccio richiede tuttavia che l'applicazione mantenga la consistenza tra gli schemi XML archiviati nella colonna XML e il sistema di tipi XML. Se ad esempio si elimina lo spazio dei nomi di uno schema XML dal sistema di tipi XML, per mantenere la consistenza sarà necessario eliminarlo anche dalla tabella.

Vedere anche

Riferimento

Gestione di insiemi di schemi XML sul server
Funzioni XQuery per il tipo di dati XML

Concetti

Tipo di dati XML

Altre risorse

sys.dm_db_index_physical_stats
Introduzione alla ricerca full-text

Guida in linea e informazioni

Assistenza su SQL Server 2005