Verwenden des EXPLICIT-Modus

Wie im Thema Erstellen von XML mithilfe von FOR XML beschrieben wird, bieten die Modi RAW und AUTO kaum Steuerungsmöglichkeiten für die Form des aus einem Abfrageergebnis generierten XML-Codes. Der EXPLICIT-Modus hingegen ermöglicht größte Flexibilität beim Generieren der gewünschten XML-Ausgabe aus einem Abfrageergebnis.

Die Abfrage im EXPLICIT-Modus muss so geschrieben werden, dass die zusätzlichen Informationen zu der erforderlichen XML-Ausgabe, wie z. B. die erwartete Schachtelung, explizit als Teil der Abfrage angegeben werden. Das Schreiben von Abfragen im EXPLICIT-Modus kann jedoch aufwendig sein, je nachdem, welche XML-Daten erforderlich sind. Möglicherweise ist das Verwenden des PATH-Modus mit Schachtelung dem Schreiben von Abfragen im EXPLICIT-Modus als einfachere Alternative vorzuziehen.

Da Sie die gewünschten XML-Daten als Teil der Abfrage im EXPLICIT-Modus beschreiben, müssen Sie sicherstellen, dass die generierten XML-Daten wohlgeformt und gültig sind.

Verarbeiten von Rowsets im EXPLICIT-Modus

Der EXPLICIT-Modus transformiert das Rowset, das beim Ausführen der Abfrage ausgegeben wird, in ein XML-Dokument. Das Rowset muss ein bestimmtes Format aufweisen, damit der EXPLICIT-Modus das XML-Dokument erstellt. Dies erfordert, dass Sie die zum Erstellen des Rowsets, universal table, erforderliche SELECT-Abfrage in einem bestimmten Format schreiben, damit die Verarbeitungslogik die gewünschten XML-Daten erstellt.

Zuerst muss die Abfrage die folgenden beiden Metadatenspalten erstellen:

  • Die erste Spalte muss die Tagnummer des aktuellen Elements als Integer-Typ bereitstellen; der Name der Spalte muss Tag lauten. Die Abfrage muss eine eindeutige Tagnummer für jedes aus dem Rowset konstruierte Element bereitstellen.
  • Die zweite Spalte muss die Tagnummer des übergeordneten Elements bereitstellen; der Name dieser Spalte muss Parent lauten. So stellen die Tag- und die Parent-Spalte Informationen zur Hierarchie bereit.

Die Werte der Metadatenspalten sowie die Informationen in den Spaltennamen werden dann zum Erstellen der gewünschten XML-Ausgabe verwendet. Beachten Sie, dass die Abfrage die Spaltennamen in einer bestimmten Art bereitstellen muss. Beachten Sie außerdem, dass der Wert 0 oder NULL in der Parent-Spalte anzeigt, dass das entsprechende Element kein übergeordnetes Element besitzt. Das Element wird der XML-Ausgabe auf der obersten Ebene hinzugefügt.

Zum besseren Verständnis dazu, wie die von der Abfrage generierte Universaltabelle zu einem XML-Ergebnis verarbeitet wird, nehmen Sie einmal an, Sie haben eine Abfrage geschrieben, die die folgende Universaltabelle erstellt:

Beispieluniversaltabelle

Beachten Sie hinsichtlich dieser Universaltabelle Folgendes:

  • Die ersten beiden Spalten sind Metadatenspalten mit der Bezeichnung Tag und Parent. Diese Werte bestimmen die Hierarchie.
  • Die Spaltennamen werden in einer bestimmten Art angegeben, wie weiter unten in diesem Thema beschrieben.
  • Beim Generieren der XML-Ausgabe aus dieser Universaltabelle werden die Daten dieser Tabelle vertikal in Spaltengruppen partitioniert. Die Gruppierung wird aufgrund des Tag-Wertes und der Spaltennamen bestimmt. Beim Konstruieren der XML-Ausgabe wählt die Verarbeitungslogik jeweils eine Spaltengruppe für jede Zeile aus und konstruiert so ein Element. In diesem Beispiel gilt Folgendes:
    • Für den Wert 1 in der ersten Zeile der Tag-Spalte bilden die Spalten, deren Namen dieselbe Tagnummer enthalten, also Customer!1!cid und Customer!1!name, eine Gruppe. Diese Spalten werden nun beim Verarbeiten der Zeile verwendet. Beachten Sie auch die Form des generierten Elements: <Customer id=... name=...>. Das Format der Spaltennamen wird weiter unten in diesem Thema beschrieben.
    • Für die Zeilen mit dem Wert 2 in der Tag-Spalte bilden die Spalten Order!2!id und Order!2!date eine Gruppe, die dann zum Konstruieren der Elemente <Order id=... date=... /> verwendet wird.
    • Für die Zeilen mit dem Wert 3 in der Tag-Spalte bilden die Spalten OrderDetail!3!id!id und OrderDetail!3!pid!idref ebenfalls eine Gruppe. Jede dieser Zeilen generiert ein Element, <OrderDetail id=... pid=...> aus diesen Spalten.
  • Beachten Sie, dass die Zeilen beim Generieren der XML-Hierarchie der Reihe nach verarbeitet werden. Die XML-Hierarchie wird wie folgt bestimmt:
    • Die erste Zeile gibt den Tag-Wert 1 und den Parent-Wert NULL an. Das entsprechende Element, <Customer>, wird dem XML-Dokument folglich auf der obersten Ebene hinzugefügt.

      <Customer cid="C1" name="Janine">
      
    • Die zweite Zeile identifiziert den Tag-Wert 2 und den Parent-Wert 1. Das entsprechende Element, <Order>, wird folglich als untergeordnetes Element von <Customer> hinzugefügt.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
      
    • Die nächsten beiden Zeilen identifizieren den Tag-Wert 3 und den Parent-Wert 2. Die beiden <OrderDetail>-Elemente werden folglich als untergeordnete Elemente des <Order>-Elements hinzugefügt.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
      
    • Die letzte Zeile identifiziert die Tag-Nummer 2 und die Parent-Tagnummer 1. Folglich wird dem übergeordneten <Customer>-Element ein weiteres untergeordnetes <Order>-Element hinzugefügt.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
         </Order>
         <Order id="O2" date="3/29/1997">
      </Customer>
      

Zusammenfassend sind also beim Verwenden des EXPLICIT-Modus zum Erstellen einer XML-Ausgabe die Werte in der Tag- und Parent-Metadatenspalte, die in den Spaltennamen bereitgestellten Informationen sowie die richtige Reihenfolge der Zeilen erforderlich.

Anordnung der Zeilen in der Universaltabelle

Beim Konstruieren der XML-Daten werden die Zeilen in der Universaltabelle der Reihe nach verarbeitet. Daher müssen die Zeilen des Rowsets so angeordnet werden, dass jedem übergeordneten Knoten unmittelbar dessen untergeordnete Elemente folgen, damit die den einzelnen übergeordneten Elementen zugeordneten untergeordneten Elemente ordnungsgemäß abgerufen werden können.

Angeben von Spaltennamen in einer Universaltabelle

Beim Schreiben von Abfragen im EXPLICIT-Modus müssen die Spaltennamen des resultierenden Rowsets im folgenden Format angegeben werden. Die Spaltennamen stellen Transformationsinformationen wie Element- und Attributnamen sowie weitere, in Direktiven angegebene, zusätzliche Informationen bereit.

Das allgemeine Format sieht folgendermaßen aus:

ElementName!TagNumber!AttributeName!Directive

Es folgt die Beschreibung der einzelnen Teile des Formats:

  • ElementName
    Der allgemeine Bezeichner des Elements im Ergebnis. Wenn als ElementName beispielsweise Customers angegeben wurde, wird das <Customers>-Element generiert.
  • TagNumber
    Ein eindeutiger, einem Element zugewiesener Tagwert. Dieser Wert bestimmt mithilfe der beiden Metadatenspalten Tag und Parent die Schachtelung der Elemente in der XML-Ausgabe.
  • AttributeName
    Stellt den Namen des zu konstruierenden Attributs im angegebenen ElementName-Element bereit. Dieses Verhalten gilt, wenn kein Wert für Directive angegeben wird.

    Wenn einer der Werte xml, cdata oder element für Directive angegeben wird, wird dieser Wert zum Konstruieren eines untergeordneten Elements von ElementName verwendet, und der Wert der Spalte wird diesem hinzugefügt.

    Wenn Sie einen Wert für Directive angeben, kann AttributeName leer sein. Beispiel: ElementName!TagNumber!!Directive. In diesem Fall ist der Spaltenwert direkt in ElementName enthalten.

  • Directive
    Directive ist optional und wird zum Bereitstellen zusätzlicher Informationen für das Konstruieren der XML-Ausgabe verwendet. Directive hat zwei Aufgaben.

    Die erste besteht darin, Werte als ID, IDREF und IDREFS zu codieren. Sie können die Schlüsselwörter ID, IDREF und IDREFS als Werte für Directive angeben. Diese Direktiven überschreiben die Attributtypen. Außerdem ermöglichen sie, dokumentinterne Verknüpfungen zu erstellen.

    Sie können Directive auch verwenden, um anzugeben, wie die Zeichenfolgendaten der XML-Ausgabe zugeordnet werden sollen. Als Werte für Directive können die Schlüsselwörter hide, element, elementxsinil, xml, xmltext und cdata verwendet werden. Die hide-Direktive blendet den Knoten aus. Dies kann sich als nützlich erweisen, wenn Sie Werte nur zu Sortierzwecken abrufen, jedoch nicht in der resultierenden XML-Ausgabe verwenden möchten.

    Die element-Direktive generiert ein enthaltenes Element anstelle eines Attributs. Die enthaltenen Daten sind als Entität codiert. So wird z. B. das Zeichen < zu &lt;. Bei Spaltenwerten von NULL wird kein Element generiert. Wenn Sie möchten, dass für NULL-Spaltenwerte Elemente generiert werden, können Sie die elementxsinil-Direktive angeben. Damit wird ein Element mit dem Attribut xsi:nil=TRUE generiert.

    Die xml-Direktive ist mit der element-Direktive identisch, mit der Ausnahme, dass keine Entitätscodierung vorgenommen wird. Beachten Sie, dass die element-Direktive mit ID, IDREF oder IDREFS kombiniert werden kann; die xml-Direktive hingegen ist mit keiner anderen Direktive außer hide zulässig.

    Bei der cdata-Direktive werden die Daten von einem CDATA-Abschnitt umgeben. Es findet keine Entitätscodierung des Inhalts statt. Der ursprüngliche Datentyp muss vom Typ Text sein (varchar, nvarchar, text oder ntext). Diese Direktive kann nur mit hide verwendet werden. Wird diese Direktive verwendet, darf AttributeName nicht angegeben werden.

    Das Kombinieren von Direktiven dieser beiden Gruppen ist in den meisten Fällen zulässig, nicht jedoch das Kombinieren der Direktiven untereinander.

    Wenn weder Directive noch AttributeName angegeben wird, wie z. B. in Customer!1, ist eine element-Direktive implizit enthalten, wie z. B. in Customer!1!!element, und die Spaltendaten sind in ElementName enthalten.

    Wird die xmltext-Direktive angegeben, wird der Spalteninhalt von einem einzelnen Tag umgeben, das in den Rest des Dokuments integriert wird. Diese Direktive erweist sich beim Abrufen von nicht verbrauchten XML-Überlaufdaten, die in einer Spalte mithilfe von OPENXML gespeichert wurden, als nützlich. Weitere Informationen finden Sie unter Abfragen von XML mithilfe von OPENXML.

    Wird AttributeName angegeben, wird der Tagname durch den angegebenen Namen ersetzt. Anderenfalls wird das Attribut an die aktuelle Liste der Attribute der einschließenden Elemente angefügt und der Inhalt ohne Entitätscodierung an den Anfang der Eingrenzung gesetzt. Die Spalte mit dieser Direktive muss vom Typ Text sein (varchar, nvarchar, char, nchar, text oder ntext). Diese Direktive kann nur mit hide verwendet werden. Diese Direktive ist zum Abrufen von in einer Spalte gespeicherten Überlaufdaten nützlich. Wenn der Inhalt kein wohlgeformtes XML-Dokument ist, ist das Verhalten nicht definiert.

Beispiele:

Die folgenden Beispiele veranschaulichen die Verwendung des EXPLICIT-Modus:

A. Abrufen von Informationen zu Mitarbeitern

In diesem Beispiel werden die ID und der Name jedes Mitarbeiters abgerufen. In der AdventureWorks-Datenbank kann employeeID aus der Employee-Tabelle abgerufen werden. Die Namen der Mitarbeiter werden aus der Contact-Tabelle abgerufen. Die ContactID-Spalte kann zum Verknüpfen der Tabellen verwendet werden.

Angenommen, Sie möchten mithilfe einer FOR XML EXPLICIT-Transformation ein XML-Dokument generieren, wie im Folgenden gezeigt:

<Employee EmpID="1" >
  <Name FName="Guy" LName="Gilbert" />
</Employee>
...

Nachdem die Hierarchie zwei Ebenen aufweist, schreiben Sie zwei SELECT-Abfragen und wenden UNION ALL an. Im Folgenden wird die erste Abfrage gezeigt, die die Werte für das <Employee>-Element sowie dessen Attribute abruft. In der Abfrage wird dem <Employee>-Element der Tag-Wert 1 und der Parent-Wert NULL zugewiesen, da es sich um das Element auf oberster Ebene handelt.

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

Im Folgenden wird die zweite Abfrage gezeigt. Sie ruft die Werte für das <Name>-Element ab. In der Abfrage wird dem <Name>-Element der Tag-Wert 2 und der Parent-Tagwert 1 zugewiesen, wodurch <Employee> als übergeordnetes Element identifiziert wird.

SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

Kombinieren Sie diese Abfragen nun mit UNION ALL, wenden Sie FOR XML EXPLICIT an, und geben Sie die erforderliche ORDER BY-Klausel an. Sie müssen das Rowset zuerst nach EmployeeID und dann nach Namen sortieren, sodass die NULL-Werte in den Namen an erster Stelle aufgeführt werden. Führen Sie die folgende Abfrage ohne FOR XML-Klausel aus, um die generierte Universaltabelle anzuzeigen.

So sieht die endgültige Abfrage aus:

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName]
FOR XML EXPLICIT

Dies ist das Teilergebnis:

<Employee EmpID="1">
  <Name FName="Guy" LName="Gilbert" />
</Employee>
<Employee EmpID="2">
  <Name FName="Kevin" LName="Brown" />
</Employee>
...

Die erste SELECT-Anweisung gibt die Spaltennamen des Rowsets des Ergebnisses an. Diese Namen bilden zwei Spaltengruppen. Die Gruppe mit dem Tag-Wert 1 im Spaltennamen identifiziert Employee als Element und EmpID als Attribut. Die andere Spaltengruppe besitzt den Tagwert 2 im Spaltennamen und identifiziert <Name> als Element und FName und LName als Attribute.

In der folgenden Tabelle wird das von der Abfrage generierte partielle Rowset gezeigt:

Tag Parent  Employee!1!EmpID Name!2!FName Name!2!LName
----------- ----------- ---------------- -------------------
1    NULL     1                NULL          NULL
2     1       1                Guy           Gilbert
1    NULL     2                NULL          NULL
2     1       2                Kevin         Brown
1    NULL     3                NULL          NULL
2     1       3                Roberto       Tamburello 
...

Die Zeilen in der Universaltabelle werden auf folgende Weise verarbeitet, um die resultierende XML-Struktur zu erzeugen:

Die erste Zeile identifiziert den Tag-Wert 1. Folglich wird die Spaltengruppe mit dem Tag-Wert 1, Employee!1!EmpID, identifiziert. Diese Spalte identifiziert Employee als Elementname. Ein <Employee>-Element mit EmpID-Attributen wird nun erstellt. Diesen Attributen werden entsprechende Spaltenwerte zugewiesen.

Die zweite Zeile weist den Tag-Wert 2 auf. Folglich wird die Spaltengruppe mit dem Tag-Wert 2 im Spaltennamen, Name!2!FName, Name!2!LName, identifiziert. Diese Spaltennamen identifizieren Name als Elementname. Ein <Name>-Element mit FName- und LName-Attributen wird nun erstellt. Diesen Attributen werden entsprechende Spaltenwerte zugewiesen. Diese Zeile identifiziert 1 als Parent-Element. Dieses untergeordnete Element wird dem vorhergehenden <Employee>-Element hinzugefügt.

Dieser Prozess wird für die restlichen Zeilen des Rowsets wiederholt. Beachten Sie, dass die Anordnung der Zeilen in der Universaltabelle wichtig ist, damit FOR XML EXPLICIT das Rowset in der richtigen Reihenfolge verarbeitet und die erwünschte XML-Ausgabe generiert.

B. Angeben der element-Direktive

Dieses Beispiel ist Beispiel A ähnlich, nur dass nun eine elementzentrierte XML-Ausgabe generiert wird, wie im Folgenden gezeigt:

<Employee EmpID=...>
  <Name>
    <FName>...</FName>
    <LName>...</LName>
  </Name>
</Employee>

Die Abfrage bleibt dieselbe, mit der Ausnahme, dass Sie nun den Spaltennamen die ELEMENT-Direktive hinzufügen. Folglich werden dem <Name>-Element anstelle von Attributen die untergeordneten Elemente <FName> und <LName> hinzugefügt. Die Employee!1!EmpID-Spalte gibt keine ELEMENT-Direktive an; daher wird EmpID als Attribut des <Employee>-Elements hinzugefügt.

SELECT 1 as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName!ELEMENT],
       NULL       as [Name!2!LName!ELEMENT]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName!ELEMENT]
FOR XML EXPLICIT

Dies ist das Teilergebnis.

<Employee EmpID="1">
  <Name>
    <FName>Guy</FName>
    <LName>Gilbert</LName>
  </Name>
</Employee>
<Employee EmpID="2">
  <Name>
    <FName>Kevin</FName>
    <LName>Brown</LName>
  </Name>
</Employee>
...

C. Angeben der elementxsinil-Direktive

Wenn Sie zum Abrufen einer elementzentrierten XML-Ausgabe die ELEMENT-Direktive angeben, und eine Spalte einen NULL-Wert aufweist, wird das entsprechende Element im EXPLICIT-Modus nicht generiert. Sie können optional die ELEMENTXSINIL-Direktive angeben, um das Generieren von Elementen für NULL-Werte anzufordern, wenn das xsi:nil-Attribut auf TRUE festgelegt ist.

Die folgende Abfrage konstruiert eine XML-Ausgabe, die die Adressen der Mitarbeiter enthält. Für die Spalten AddressLine2 und City ist in den Spaltennamen die ELEMENTXSINIL-Direktive angegeben. So werden für NULL-Werte in der AddressLine2- und City-Spalte des Rowsets Elemente generiert.

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID  as [Employee!1!EmpID],
       E.AddressID as [Employee!1!AddressID],
       NULL        as [Address!2!AddressID],
       NULL        as [Address!2!AddressLine1!ELEMENT],
       NULL        as [Address!2!AddressLine2!ELEMENTXSINIL],
       NULL        as [Address!2!City!ELEMENTXSINIL]
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.ContactID = A.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       E.AddressID,
       A.AddressID,
       AddressLine1, 
       AddressLine2,
       City 
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.AddressID = A.AddressID
ORDER BY [Employee!1!EmpID],[Address!2!AddressID]
FOR XML EXPLICIT

Dies ist das Teilergebnis:

<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        EmpID="1" AddressID="61">
  <Address AddressID="61">
    <AddressLine1>7726 Driftwood Drive</AddressLine1>
    <AddressLine2 xsi:nil="true" />
    <City>Monroe</City>
  </Address>
</Employee>
...

D. Konstruieren von gleichgeordneten Elementen im EXPLICIT-Modus

Angenommen, Sie möchten eine XML-Ausgabe mit Informationen zu Bestellungen konstruieren. Beachten Sie dabei, dass das <SalesPerson>- und das <OrderDetail>-Element gleichgeordnet sind. Jede Bestellung (Order) besitzt ein <OrderHeader>-Element, ein <SalesPerson>-Element und mindestens ein <OrderDetail>-Element.

<OrderHeader SalesOrderID=... OrderDate=... CustomerID=... >
  <SalesPerson SalesPersonID=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=.../>
      ...
</OrderHeader>
<OrderHeader ...</OrderHeader>

Die folgende Abfrage im EXPLICIT-Modus konstruiert diese XML-Ausgabe. Beachten Sie, dass die Abfrage den Tag-Wert 1 für das <OrderHeader>-Element, den Wert 2 für das <SalesPerson>-Element und den Wert 3 für das <OrderDetail>-Element angibt. Nachdem <SalesPerson> und <OrderDetail> gleichgeordnet sind, gibt die Abfrage für beide denselben Parent-Tagwert 1 für das <OrderHeader>-Element an.

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT

Dies ist das Teilergebnis:

<OrderHeader SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  <OrderDetail SalesOrderID="43659" LineTotal="28.840400" ProductID="716" OrderQty="1" />
  <OrderDetail SalesOrderID="43659" LineTotal="34.200000" ProductID="709" OrderQty="6" />
   ...
</OrderHeader>
<OrderHeader SalesOrderID="43661" OrderDate="2001-07-01T00:00:00" CustomerID="442">
  <SalesPerson SalesPersonID="282" />
  <OrderDetail SalesOrderID="43661" LineTotal="20.746000" ProductID="712" OrderQty="4" />
  <OrderDetail SalesOrderID="43661" LineTotal="40.373000" ProductID="711" OrderQty="2" />
   ...
</OrderHeader>

E. Angeben der ID- und IDREF-Direktiven

Dieses Beispiel ist mit Beispiel C identisch, mit der Ausnahme, dass die Abfrage hier die ID- und IDREF-Direktiven angibt. Diese Direktiven überschreiben die Typen des SalesPersonID-Attributs der <OrderHeader>- und <OrderDetail>-Elemente. Auf diese Weise werden dokumentinterne Verknüpfungen gebildet. Sie benötigen das Schema, um die überschriebenen Typen anzuzeigen. Um das Schema abzurufen, gibt die Abfrage die Option XMLDATA in der FOR XML-Klausel an.

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID!id],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID!idref],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID, 
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID!id], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID!idref],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT, XMLDATA

Dies ist das Teilergebnis. Beachten Sie, dass die ID- und die IDREF-Direktive die Datentypen des SalesOrderID-Attributs im <OrderHeader>- und <OrderDetail>-Element überschrieben haben. Wenn Sie diese Direktiven entfernen, gibt das Schema wieder die ursprünglichen Datentypen dieser Attribute zurück.

<Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="OrderHeader" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="id" />
    <AttributeType name="OrderDate" dt:type="dateTime" />
    <AttributeType name="CustomerID" dt:type="i4" />
    <attribute type="SalesOrderID" />
    <attribute type="OrderDate" />
    <attribute type="CustomerID" />
  </ElementType>
  <ElementType name="SalesPerson" content="mixed" model="open">
    <AttributeType name="SalesPersonID" dt:type="i4" />
    <attribute type="SalesPersonID" />
  </ElementType>
  <ElementType name="OrderDetail" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="idref" />
    <AttributeType name="LineTotal" dt:type="number" />
    <AttributeType name="ProductID" dt:type="i4" />
    <AttributeType name="OrderQty" dt:type="i2" />
    <attribute type="SalesOrderID" />
    <attribute type="LineTotal" />
    <attribute type="ProductID" />
    <attribute type="OrderQty" />
  </ElementType>
</Schema>
<OrderHeader xmlns="x-schema:#Schema1" SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  ...
</OrderHeader>
...

F. Angeben der ID- und IDREFS-Direktiven

Ein Elementattribut kann als Attribut vom Typ ID angegeben werden, wobei das IDREFS-Attribut dann verwendet werden kann, um darauf zu verweisen. Dies ermöglicht dokumentinterne Verknüpfungen. Das Verfahren ist der Beziehung zwischen Primärschlüssel und Fremdschlüssel in relationalen Datenbanken ähnlich.

Dieses Beispiel veranschaulicht, wie die ID- und die IDREFS-Direktive zum Erstellen von Attributen vom Typ ID und IDREFS verwendet werden können. Da IDs keine ganzzahligen Werte sein können, werden die ID-Werte in diesem Beispiel konvertiert, d. h. sie werden einer Typumwandlung unterzogen, und es werden Präfixe für die ID-Werte verwendet.

Angenommen, Sie möchten folgende XML-Ausgabe generieren:

<Customer CustomerID="C1" SalesOrderIDList=" O11 O22 O33..." >
    <SalesOrder SalesOrderID="O11" OrderDate="..." />
    <SalesOrder SalesOrderID="O22" OrderDate="..." />
    <SalesOrder SalesOrderID="O33" OrderDate="..." />
    ...
</Customer>

Das SalesOrderIDList-Attribut des < Customer >-Elements ist ein mehrwertiges Attribut, das auf die SalesOrderID-Attribute des < SalesOrder >-Elements verweist. Um diese Verknüpfung herzustellen, muss das SalesOrderID-Attribut als ID-Typ und das SalesOrderIDList-Attribut des < Customer>-Elements als IDREFS-Typ deklariert werden. Da ein Kunde mehrere Bestellungen aufgeben kann, wird IDREFS verwendet.

IDREFS kann mehrere Werte annehmen. Daher müssen Sie jeweils eine SELECT-Klausel verwenden, die dieselben Tag-, Parent- und Schlüsselspalteninformationen wiederverwendet. Mit ORDER BY stellen Sie dann sicher, dass die Zeilensequenz, aus der die IDREFS-Werte bestehen, unter dem jeweiligen übergeordneten Element gruppiert wird.

Im Folgenden wird die Abfrage gezeigt, die die gewünschte XML-Ausgabe erstellt. Die Abfrage verwendet die ID- und die IDREFS-Direktive, um die Datentypen der Spaltennamen SalesOrder!2!SalesOrderID!ID und Customer!1!SalesOrderIDList!IDREFS zu überschreiben.

SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID       [Customer!1!CustomerID],
        NULL               [Customer!1!SalesOrderIDList!IDREFS],
        NULL               [SalesOrder!2!SalesOrderID!ID],
        NULL               [SalesOrder!2!OrderDate]
FROM   Sales.Customer C 
UNION ALL 
SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID,
        'O-'+CAST(SalesOrderID as varchar(10)), 
        NULL,
        NULL
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
        C.CustomerID,
        NULL,
        'O-'+CAST(SalesOrderID as varchar(10)),
        OrderDate
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
ORDER BY [Customer!1!CustomerID] ,
         [SalesOrder!2!SalesOrderID!ID],
         [Customer!1!SalesOrderIDList!IDREFS]
FOR XML EXPLICIT

G. Verwenden der hide-Direktive zum Ausblenden von Elementen und Attributen aus XML-Ergebnissen

In diesem Beispiel wird die Verwendung der hide-Direktive veranschaulicht. Diese Direktive erweist sich als hilfreich, wenn die Abfrage ein Attribut zum Sortieren der Zeilen in der Universaltabelle zurückgeben soll, das Attribut jedoch nicht im endgültigen XML-Dokument enthalten sein soll.

Diese Abfrage konstruiert diese XML-Ausgabe:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
           <Summary> element from XML stored in CatalogDescription column
    </SummaryDescription>
  </Summary>
</ProductModel>

Diese Abfrage generiert die gewünschte XML-Ausgabe. Die Abfrage identifiziert zwei Spaltengruppen, deren Spaltennamen die Tagwerte 1 und 2 aufweisen.

Diese Abfrage verwendet die query()-Methode (xml-Datentyp) vom Datentyp xml, um die CatalogDescription-Spalte vom Typ xml abzufragen und daraus die Zusammenfassungsbeschreibung abzurufen. Die Abfrage verwendet auch die value()-Methode (xml-Datentyp) vom Datentyp xml, um den ProductModelID-Wert aus der CatalogDescription-Spalte abzurufen. Dieser Wert wird benötigt, um die Rowsets des Ergebnisses zu sortieren, ist im XML-Ergebnis jedoch nicht erforderlich. Folglich enthält der Spaltenname, [Summary!2!ProductModelID!hide], die hide-Direktive. Wenn diese Spalte nicht in der SELECT-Anweisung enthalten ist, müssen Sie das Rowset nach [ProductModel!1!ProdModelID] und [Summary!2!SummaryDescription] vom Datentyp xml sortieren und können die Spalte vom Typ xml in ORDER BY nicht verwenden. Daher wird die zusätzliche Spalte [Summary!2!ProductModelID!hide] hinzugefügt und in der ORDER BY-Klausel angegeben.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID     as [ProductModel!1!ProdModelID],
        Name               as [ProductModel!1!Name],
        NULL               as [Summary!2!ProductModelID!hide],
        NULL               as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
        CatalogDescription.value('
         declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
       (/PD:ProductDescription/@ProductModelID)[1]', 'int'),
        CatalogDescription.query('
         declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
         /pd:ProductDescription/pd:Summary')
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],[Summary!2!ProductModelID!hide]
FOR XML EXPLICIT
go

Dies ist das Ergebnis:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <pd:Summary xmlns:pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription" >
        <p1:p xmlns:p1="http://www.w3.org/1999/xhtml">Our top-of-the-line competition mountain bike. Performance-enhancing options include the innovative HL Frame, super-smooth front suspension, and traction for all terrain. </p1:p>
      </pd:Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

H. Angeben der element-Direktive und Entitätscodierung

Dieses Beispiel veranschaulicht den Unterschied zwischen der element- und der xml-Direktive. Bei der element-Direktive erfolgt eine Änderung der Daten in Entitäten, während dies bei der xml-Direktive nicht der Fall ist. Dem <Summary>-Element wird in der Abfrage XML zugewiesen: <Summary>This is summary description</Summary>.

Angenommen, die folgende Abfrage wird ausgeführt:

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription!ELEMENT]
FROM    Production.ProductModel
WHERE   ProductModelID=19
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        NULL,
       '<Summary>This is summary description</Summary>'
FROM   Production.ProductModel
WHERE  ProductModelID=19
FOR XML EXPLICIT

Dies ist das Ergebnis. Die Zusammenfassungsbeschreibung wird im Ergebnis in eine Entität geändert.

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>&lt;Summary&gt;This is summary description&lt;/Summary&gt;</SummaryDescription>
  </Summary>
</ProductModel>

Wenn Sie nun im Spaltennamen die xml-Direktive angeben, also Summary!2!SummaryDescription!xml anstelle der element-Direktive, erhalten Sie eine Zusammenfassungsbeschreibung, ohne dass die Daten in Entitäten geändert werden.

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <Summary>This is summary description</Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

Die folgende Abfrage weist der Zusammenfassungsbeschreibung keinen statischen XML-Wert zu, sondern verwendet die query()-Methode vom Typ xml, um die Produktmodell-Zusammenfassungsbeschreibung aus der CatalogDescription-Spalte vom Typ xml abzurufen. Da bekannt ist, dass das Ergebnis vom Typ xml ist, werden die Daten nicht in Entitäten geändert.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
       (SELECT CatalogDescription.query('
            declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
          /pd:ProductDescription/pd:Summary'))
FROM     Production.ProductModel
WHERE    CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],Tag
FOR XML EXPLICIT

I. Angeben der cdata-Direktive

Wird die Direktive auf cdata festgelegt, werden die enthaltenen Daten nicht entitätscodiert, sondern in den CDATA-Abschnitt eingefügt. Die cdata-Attribute müssen namenlos sein.

In der folgenden Abfrage wird die Produktmodell-Zusammenfassungsbeschreibung von einem CDATA-Abschnitt umgeben.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        '<Summary>This is summary description</Summary>'   
            as [ProductModel!1!!cdata] -- no attribute name so ELEMENT assumed
FROM    Production.ProductModel
WHERE   ProductModelID=19
FOR XML EXPLICIT

Dies ist das Ergebnis:

<ProductModel ProdModelID="19" Name="Mountain-100">
   <![CDATA[<Summary>This is summary description</Summary>]]>
</ProductModel>

J. Angeben der xmltext-Direktive

Dieses Beispiel veranschaulicht, wie Daten in der Überlaufspalte mithilfe der xmltext-Direktive in einer SELECT-Anweisung im EXPLICIT-Modus verarbeitet werden.

Sie sehen hier die Person-Tabelle. In dieser Tabelle speichert die Overflow-Spalte den unverbrauchten Teil des XML-Dokuments.

CREATE TABLE Person(PersonID varchar(5), PersonName varchar(20), Overflow nvarchar(200))
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P">content</SomeTag>')

Diese Abfrage ruft Spalten aus der Person-Tabelle ab. AttributeName ist für die Overflow-Spalte nicht angegeben, directive ist jedoch auf den Wert xmltext festgelegt, um einen Spaltennamen der Universaltabelle bereitzustellen.

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- No AttributeName; xmltext directive
FROM Person
FOR XML EXPLICIT

Im resultierenden XML-Dokument:

  • Da für die Overflow-Spalte kein AttributeName, sondern die xmltext-Direktive angegeben ist, werden die Attribute des <overflow>-Elements an die Attributliste des einschließenden <Parent>-Elements angehängt.
  • Da das PersonID-Attribut des <xmltext>-Elements in Konflikt zu dem auf derselben Elementebene abgerufenen PersonID-Attribut steht, wird das Attribut des <xmltext>-Elements ignoriert, sogar wenn PersonID NULL ist. Im Allgemeinen setzt ein Attribut ein Attribut mit demselben Namen in der Überlaufspalte außer Kraft.

Dies ist das Ergebnis:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">content</Parent>

Wird dieselbe Abfrage angegeben, und die Überlaufdaten besitzen Unterelemente, werden die Unterelemente in der Overflow-Spalte als Unterelemente des einschließenden <Parent>-Elements hinzugefügt.

So werden in diesem Beispiel die Daten in der Person-Tabelle so geändert, dass die Overflow-Spalte nun Unterelemente besitzt:

TRUNCATE TABLE Person
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P"><name>PersonName</name></SomeTag>')

Beim Ausführen derselben Abfrage werden die Unterelemente des <xmltext>-Elements als Unterelemente des einschließenden Parent>-Elements hinzugefügt:

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- no AttributeName, xmltext directive
FROM Person
FOR XML EXPLICIT

Dies ist das Ergebnis:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">
  <name>PersonName</name>
</Parent>

Wird AttributeName mit der xmltext-Direktive angegeben, werden die Attribute des <overflow>-Elements als Attribute der Unterelemente des einschließenden <Parent>-Elements hinzugefügt. Der für AttributeName angegebene Name wird zum Namen des Unterelements.

In dieser Abfrage wird AttributeName, <overflow> zusammen mit der xmltext-Direktive angegeben*:*

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!overflow!xmltext] -- overflow is AttributeName
                      -- xmltext is directive
FROM Person
FOR XML EXPLICIT

Dies ist das Ergebnis:

<Parent PersonID="P1" PersonName="Joe">
  <overflow attr1="data">content</overflow>
</Parent>
<Parent PersonID="P2" PersonName="Joe">
  <overflow attr2="data" />
</Parent>
<Parent PersonID="P3" PersonName="Joe">
  <overflow attr3="data" PersonID="P">
    <name>PersonName</name>
  </overflow>
</Parent>

In dieser Abfrage wird die element-Direktive für das PersonName-Attribut angegeben. Dadurch wird PersonName als Unterelement des einschließenden <Parent>-Elements hinzugefügt. Die Attribute von <xmltext> werden auch hier an das einschließende <Parent>-Element angefügt. Der Inhalt des <overflow>-Elements (Unterelemente usw.) wird den anderen Unterelementen der einschließenden Parent>-Elemente vorangestellt.

SELECT 1      as Tag, NULL as parent,
       PersonID   as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName!element], -- element directive
       overflow   as [Parent!1!!xmltext]
FROM Person
FOR XML EXPLICIT

Dies ist das Ergebnis:

<Parent PersonID="P1" attr1="data">content<PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P2" attr2="data">
  <PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P3" attr3="data">
  <name>PersonName</name>
  <PersonName>Joe</PersonName>
</Parent>

Wenn die Daten der xmltext-Spalte Attribute für das Stammelement enthalten, werden diese Attribute nicht im XML-Datenschema angezeigt, und der MSXML-Parser führt keine Überprüfung des resultierenden XML-Dokumentfragments aus. Beispiel:

SELECT 1 as Tag,
       0 as Parent,
       N'<overflow a="1"/>' as 'overflow!1!!xmltext'
FOR XML EXPLICIT, xmldata

Dies ist das Ergebnis. Beachten Sie, dass das overflow-Attribut a im zurückgegebenen Schema fehlt:

<Schema name="Schema2" 
        xmlns="urn:schemas-microsoft-com:xml-data" 
        xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="overflow" content="mixed" model="open">
  </ElementType>
</Schema>
<overflow xmlns="x-schema:#Schema2" a="1">
</overflow> 

Siehe auch

Verweis

Verwenden des RAW-Modus
Verwenden des AUTO-Modus
Erstellen von XML mithilfe von FOR XML

Konzepte

Verwenden des PATH-Modus

Andere Ressourcen

SELECT (Transact-SQL)

Hilfe und Informationen

Informationsquellen für SQL Server 2005