Generazione di uno schema XSD inline

In una clausola FOR XML , è possibile richiedere che la query restituisca uno schema inline oltre ai risultati della query. Per ottenere uno schema XDR, utilizzare la parola chiave XMLDATA nella clausola FOR XML. Per ottenere uno schema XSD, utilizzare invece la parola chiave XMLSCHEMA.

In questo argomento viene descritta la parola chiave XMLSCHEMA e viene illustrata la struttura dello schema XSD risultante. Di seguito sono illustrate le limitazioni previste per la richiesta di schemi inline:

  • È possibile specificare la parola chiave XMLSCHEMA unicamente nelle modalità RAW e AUTO e non nella modalità EXPLICIT.

  • Se in una query FOR XML nidificata è specificata la direttiva TYPE , il risultato della query sarà di tipo xml e verrà considerato come un'istanza di dati XML non tipizzati. Per ulteriori informazioni, vedere Implementazione di XML in SQL Server.

Se si specifica la parola chiave XMLSCHEMA in una query FOR XML, si otterrà sia uno schema che i dati XML, ovvero il risultato della query. Ogni elemento di livello principale dei dati fa riferimento allo schema precedente tramite una dichiarazione dello spazio dei nomi predefinito che, a sua volta, fa riferimento allo spazio dei nomi di destinazione dello schema inline.

Ad esempio:

<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:schema="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="https://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
  <xsd:element name="Production.ProductModel">
    <xsd:complexType>
      <xsd:attribute name="ProductModelID" type="sqltypes:int" use="required" />
      <xsd:attribute name="Name" use="required">
        <xsd:simpleType sqltypes:sqlTypeAlias="[AdventureWorks].[dbo].[Name]">
          <xsd:restriction base="sqltypes:nvarchar" sqltypes:localeId="1033" sqltypes:sqlCompareOptions="IgnoreCase IgnoreKanaType IgnoreWidth" sqltypes:sqlSortId="52">
            <xsd:maxLength value="50" />
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:attribute>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
<Production.ProductModel xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" ProductModelID="1" Name="Classic Vest" />

Il risultato include lo schema XML e il risultato XML. L'elemento di livello principale <ProductModel> nel risultato fa riferimento allo schema tramite la dichiarazione dello spazio dei nomi predefinito, xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" .

La parte del risultato relativa allo schema può contenere più documenti dello schema che descrivono più spazi dei nomi. Verranno restituiti almeno i due documenti di schema seguenti:

  • Un documento di schema per lo spazio dei nomi Sqltypes e per il quale vengono restituiti i tipi SQL di base.

  • Un altro documento di schema che descrive la forma del risultato della query FOR XML.

Inoltre, se il risultato della query contiene tipi di dati xml, verranno inclusi anche gli schemi associatia tali tipi di dati.

Lo spazio dei nomi di destinazione del documento di schema che descrive la forma del risultato della query FOR XML contiene una parte fissa e una parte numerica che viene incrementata automaticamente. Di seguito è illustrato il formato di questo spazio dei nomi, dove n è un numero intero positivo. Ad esempio, nella query precedente urn:schemas-microsoft-com:sql:SqlRowSet1 rappresenta lo spazio dei nomi di destinazione.

urn:schemas-microsoft-com:sql:SqlRowSetn

La modifica degli spazi dei nomi di destinazione nel risultato tra un esecuzione e l'altra non è consigliabile. Ad esempio, se si esegue una query sul codice XML risultante, tale modifica richiederà l'aggiornamento della query. Se nella clausola FOR XML viene aggiunta l'opzione XMLSCHEMA, è possibile specificare facoltativamente uno spazio dei nomi di destinazione. Il codice XML risultante includerà lo spazio dei nomi specificato e rimarrà invariato, indipendentemente dal numero di esecuzioni della query.

select ProductModelID, Name
from   Production.ProductModel
where ProductModelID=1
FOR XML AUTO, XMLSCHEMA ('MyURI')

Elementi entità

Prima di fornire informazioni dettagliate sulla struttura dello schema XSD generato per il risultato della query, è necessario descrivere l'elemento entità.

Un elemento entità nel codice XML restituito dalla query FOR XML è un elemento generato da una tabella e non da una colonna. Ad esempio, la query FOR XML seguente restituisce informazioni relative ai contatti dalla tabella Contact del database AdventureWorks.

SELECT ContactID, FirstName
FROM Person.Contact
WHERE ContactID = 1
FOR XML AUTO, ELEMENTS

Risultato:

  <Contact>
    <ContactID>1</ContactID>
    <FirstName>Syed</FirstName>
  </Contact>

In questo risultato, <Contact> è l'elemento entità. Nel risultato XML possono essere presenti più elementi entità, per ognuno dei quali è disponibile una dichiarazione globale nello schema XSD inline. Ad esempio, la query seguente recupera l'intestazione e i dettagli relativi a un ordine di vendita specifico.

SELECT  SalesOrderHeader.SalesOrderID, ProductID, OrderQty
FROM    Sales.SalesOrderHeader, Sales.SalesOrderDetail
WHERE   SalesOrderHeader.SalesOrderID = SalesOrderDetail.SalesOrderID
AND     SalesOrderHeader.SalesOrderID=5001
FOR XML AUTO, ELEMENTS, XMLSCHEMA

Nella query è specificata la direttiva ELEMENTS e pertanto il codice XML risultante è incentrato sugli elementi. Nella query è inoltre specificata la direttiva XMLSCHEMA e pertanto viene restituito uno schema XSD inline. Risultato:

<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:schema="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="https://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
  <xsd:element name="Sales.SalesOrderHeader">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="SalesOrderID" type="sqltypes:int" />
        <xsd:element ref="schema:Sales.SalesOrderDetail" minOccurs="0" maxOccurs="unbounded" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="Sales.SalesOrderDetail">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="ProductID" type="sqltypes:int" />
        <xsd:element name="OrderQty" type="sqltypes:smallint" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Dalla query precedente si noti quanto segue:

  • Nel risultato, <SalesOrderHeader> e <SalesOrderDetail> sono elementi entità e pertanto vengono dichiarati a livello globale nello schema. Ciò significa che la dichiarazione è presente nel livello principale dell'elemento <Schema>.

  • <SalesOrderID>, <ProductID> e <OrderQty> non sono elementi entità perché sono mappati a colonne. I dati di colonna vengono restituiti come elementi nel codice XML e ciò è dovuto alla direttiva ELEMENTS. Tali elementi sono mappati a elementi locali del tipo complesso dell'elemento entità. Si noti che se non viene specificata la direttiva ELEMENTS, i valori SalesOrderID, ProductID e OrderQty vengono mappati ad attributi locali del tipo complesso dell'elemento entità corrispondente.

Conflitti dei nomi di attributi

Le informazioni seguenti sono basate sulle tabelle CustOrder e CustOrderDetail. Per testare gli esempi seguenti, creare le tabelle e inserire dati di esempio personalizzati:

CREATE TABLE CustOrder (OrderID int primary key, CustomerID int)
go
CREATE TABLE CustOrderDetail (OrderID int, ProductID int, Qty int)
go

Nelle query FOR XML a volte viene utilizzato lo stesso nome per indicare proprietà o attributi diversi. Ad esempio, la query in modalità RAW incentrata sugli attributi seguente crea due attributi con lo stesso nome, OrderID, e pertanto verrà generato un errore.

SELECT CustOrder.OrderID, 
       CustOrderDetail.ProductID, 
       CustOrderDetail.OrderID
FROM   dbo.CustOrder, dbo.CustOrderDetail
WHERE  CustOrder.OrderID = CustOrderDetail.OrderID
FOR XML RAW, XMLSCHEMA

Poiché è tuttavia consentito che due elementi abbiano lo stesso nome, è possibile risolvere il problema aggiungendo la direttiva ELEMENTS:

SELECT CustOrder.OrderID,
       CustOrderDetail.ProductID, 
       CustOrderDetail.OrderID
from   dbo.CustOrder, dbo.CustOrderDetail
where  CustOrder.OrderID = CustOrderDetail.OrderID
FOR XML RAW, XMLSCHEMA, ELEMENTS

Di seguito è riportato il risultato. Si noti che nello schema XSD inline, l'elemento OrderID è definito due volte. Una dichiarazione ha minOccurs impostato su 0 e corrisponde all'elemento OrderID della tabella CustOrderDetail e la seconda dichiarazione esegue il mapping alla colonna chiave primaria OrderID della tabella CustOrder, nella quale minOccurs è 1 per impostazione predefinita.

<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="https://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
  <xsd:element name="row">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="OrderID" type="sqltypes:int" />
        <xsd:element name="ProductID" type="sqltypes:int" minOccurs="0" />
        <xsd:element name="OrderID" type="sqltypes:int" minOccurs="0" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Conflitti dei nomi di elementi

Nelle query FOR XML, è possibile utilizzare lo stesso nome per indicare due sottoelementi. Ad esempio, la query seguente recupera i valori ListPrice e DealerPrice dei prodotti, ma specifica lo stesso alias per le due colonne, ovvero Price. Nel set di righe risultante saranno pertanto presenti due colonne con lo stesso nome.

Caso 1: Entrambi i sottoelementi sono colonne non chiave dello stesso tipo e possono essere NULL

Nella query seguente, entrambi i sottoelementi sono colonne non chiave dello stesso tipo e possono essere NULL.

DROP TABLE T
go
CREATE TABLE T (ProductID int primary key, ListPrice money, DealerPrice money)
go
INSERT INTO T values (1, 1.25, null)
go

SELECT ProductID, ListPrice Price, DealerPrice Price
FROM   T
for    XML RAW, ELEMENTS, XMLSCHEMA

Di seguito è riportato il codice XML generato, nel quale è visualizzata solo una frazione dello schema XSD inline:

…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
  <xsd:element name="row">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="ProductID" type="sqltypes:int" />
        <xsd:element name="Price" type="sqltypes:money" minOccurs="0" maxOccurs="2" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
  <ProductID>1</ProductID>
  <Price>1.2500</Price>
</row>

Nello schema XSD si noti quanto segue:

  • ListPrice e DealerPrice sono dello stesso tipo, money, ed entrambi possono essere NULL nella tabella. Dato che non possono essere restituiti nel codice XML risultante, nella dichiarazione del tipo complesso dell'elemento <row> esiste pertanto un solo elemento figlio <Price> con minOccurs=0 e maxOccurs=2.

  • Poiché il valore DealerPrice è NULL nella tabella, nel risultato viene restituito solo ListPrice come elemento <Price>. Se si aggiunge il parametro XSINIL alla direttiva ELEMENTS, si otterranno entrambi gli elementi con il valore xsi:nil impostato su TRUE per l'elemento <Price> corrispondente a DealerPrice. Si otterranno inoltre due elementi figlio <Price> nella definizione del tipo complesso <row> dello schema XSD inline, con l'attributo nillable impostato su TRUE per entrambi. Di seguito è riportato un frammento che rappresenta un risultato parziale:

…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
  <xsd:element name="row">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="ProductID" type="sqltypes:int" nillable="1" />
        <xsd:element name="Price" type="sqltypes:money" nillable="1" />
        <xsd:element name="Price" type="sqltypes:money" nillable="1" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ProductID>1</ProductID>
  <Price>1.2500</Price>
  <Price xsi:nil="true" />
</row>

Caso 2: Una colonna chiave e una colonna non chiave dello stesso tipo

La query seguente illustra una colonna chiave e una colonna non chiave dello stesso tipo.

CREATE TABLE T (Col1 int primary key, Col2 int, Col3 nvarchar(20))
go
INSERT INTO T VALUES (1, 1, 'test')
go 

La query seguente eseguita sulla tabella T specifica lo stesso alias per Col1 e Col2, dove Col1 è una chiave primaria e non può essere Null e Col2 può essere Null. Vengono pertanto generati due elementi di pari livello che sono figli dell'elemento <row>.

SELECT Col1 as Col, Col2 as Col, Col3
FROM T
FOR XML RAW, ELEMENTS, XMLSCHEMA

Di seguito è riportato il risultato, nel quale è visualizzato solo un frammento dello schema XSD inline.

…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
  <xsd:element name="row">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="Col" type="sqltypes:int" />
        <xsd:element name="Col" type="sqltypes:int" minOccurs="0" />
        <xsd:element name="Col3" minOccurs="0">
          <xsd:simpleType>
            <xsd:restriction base="sqltypes:nvarchar" 
                  sqltypes:localeId="1033" 
                  sqltypes:sqlCompareOptions="IgnoreCase 
                  IgnoreKanaType IgnoreWidth" 
                  sqltypes:sqlSortId="52">
              <xsd:maxLength value="20" />
            </xsd:restriction>
          </xsd:simpleType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
  <Col>1</Col>
  <Col>1</Col>
  <Col3>test</Col3>
</row>

Si noti che per l'elemento <Col> dello schema XSD inline, minOccurs è impostato su 0.

Caso 3: Entrambi gli elementi sono di tipo diverso e le colonne corrispondenti possono essere NULL

La query seguente viene eseguita sulla tabella di esempio illustrata nel caso 2:

SELECT Col1, Col2 as Col, Col3 as Col
FROM T
FOR XML RAW, ELEMENTS, XMLSCHEMA

Nella query seguente, a Col2 e Col3 sono assegnati gli stessi alias. Nel risultato vengono pertanto generati due elementi di pari livello con lo stesso nome ed entrambi figli dell'elemento <raw>. Entrambe le colonne sono di tipo diverso e possono essere NULL. Di seguito è riportato il risultato, nel quale è visualizzato solo uno schema XSD inline parziale.

…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
  <xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
  <xsd:simpleType name="Col1">
    <xsd:restriction base="sqltypes:int" />
  </xsd:simpleType>
  <xsd:simpleType name="Col2">
    <xsd:restriction base="sqltypes:nvarchar" sqltypes:localeId="1033" 
              sqltypes:sqlCompareOptions="IgnoreCase 
              IgnoreKanaType IgnoreWidth" sqltypes:sqlSortId="52">
      <xsd:maxLength value="20" />
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:element name="row">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="Col1" type="sqltypes:int" />
        <xsd:element name="Col" minOccurs="0" maxOccurs="2" type="xsd:anySimpleType" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
  <Col1>1</Col1>
  <Col xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:type="Col1">1</Col>
  <Col xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:type="Col2">test</Col>
</row>

Nello schema XSD si noti quanto segue:

  • Col2 e Col3 possono entrambe essere NULL e pertanto nella dichiarazione dell'elemento <Col> è specificato che minOccurs è 0 e maxOccurs è 2.

  • Poiché gli elementi <Col> sono di pari livello, nello schema è presente una singola dichiarazione di elemento. Inoltre, dato che entrambi gli elementi sono anche di tipo diverso, benché di tipo semplice, il tipo dell'elemento nello schema è xsd:anySimpleType. Nel risultato, ogni tipo di istanza è identificato dall'attributo xsi:type.

  • Nel risultato, ogni istanza dell'elemento <Col> fa riferimento al proprio tipo di istanza con l'attributo xsi:type.