2011-01-10 15 views
4

Ich versuche, einige XML mit verschiedenen Ebenen der Verschachtelung und auf die Gefahr von Über Vereinfachung der XML-Ausgabe wird lose von dem Format zu generieren:Wie kann ich CTEs mit einer FOR XML-Klausel kombinieren?

<invoice number="1"> 
<charge code="foo" rate="123.00"> 
    <surcharge amount="10%" /> 
</charge> 
<charge code="bar" /> 
</invoice> 

Das Datenbankschema I für diese geerbt haben passiert, dass Gebühren in unterschiedlichen Tabellen gespeichert werden, was bedeutet, dass die Aufschläge auf der Grundlage der Tabelle, von der die Gebühr stammt, unterschiedlich gespeichert werden.

Da Sie cannot use UNIONs with FOR XML ich einige UNION getan haben in einem WAK-ing, so etwas entlang der Linien von:

WITH Charges ([@code], [@rate], surcharge, InvoiceId) AS (
    SELECT code AS [@Code], amount AS [@rate], NULL as surcharge, InvoiceId 
    FROM item.charges 
UNION ALL 
    SELECT 
     code AS [@Code], 
     amount AS [@rate], 
     (
      SELECT amount AS [@amount] 
      FROM order.surcharges os 
      WHERE oc.ChargeId = os.ChargeId 
      FOR XML PATH('surcharge'), TYPE 
     ), 
     InvoiceId 
    FROM order.charges oc 
) 
SELECT 
    Number AS [@number], 
    (
     SELECT 
      [@code], 
      [@rate], 
      surcharge 
     FROM Charges 
     WHERE Charges.InvoiceId = i.InvoiceId 
    ) 
FROM Invoices i 
FOR XML PATH('invoice'), TYPE 

Nun, das ist unglaublich schließen, so dass (Beachten Sie die verschachtelte <surcharge>) :

<invoice number="1"> 
<charge code="foo" rate="123.00"> 
    <surcharge> 
    <surcharge amount="10%" /> 
    </surcharge> 
</charge> 
<charge code="bar" /> 
</invoice> 

Aber ich brauche einen Weg, um die End-Abfrage zu finden, den Wert einer XML-Spalte zu schließen, wenn der Gehalt des Elements behandelt zu werden, und nicht als ein neues Element. Ist das möglich, oder muss ich einen neuen Ansatz wählen?

Antwort

1

Es scheint, dass die (falschen) Spalte als Benennung „*“, dass der Inhalt dieser Spalte als Inhalt des Elements zu verwenden, so die SQL ändern, wie unter ihm arbeiten macht:

WITH Charges ([@code], [@rate], surcharge, InvoiceId) AS (
    SELECT code AS [@Code], amount AS [@rate], NULL as surcharge, InvoiceId 
    FROM item.charges 
UNION ALL 
    SELECT 
     code AS [@Code], 
     amount AS [@rate], 
     (
      SELECT amount AS [@amount] 
      FROM order.surcharges os 
      WHERE oc.ChargeId = os.ChargeId 
      FOR XML PATH('surcharge'), TYPE 
     ), 
     InvoiceId 
    FROM order.charges oc 
) 
SELECT 
    Number AS [@number], 
    (
     SELECT 
      [@code], 
      [@rate], 
      surcharge AS [*] -- Thsi will embed the contents of the previously generated XML in here. 
     FROM Charges 
     WHERE Charges.InvoiceId = i.InvoiceId 
    ) 
FROM Invoices i 
FOR XML PATH('invoice'), TYPE 
+0

In der Auswahlliste kann nur ein Ausdruck angegeben werden, wenn die Unterabfrage nicht mit EXISTS eingeführt wird. – user3752281

+0

@ user3752281 diese Lösung hat für mich funktioniert, also verstehe ich Ihren Kommentar nicht? –

0

Ich denke, Sie können dies tun, indem Sie den Stammknotentyp in Ihrer Anweisung "für XML-Pfad ('Zuschlag')" weglassen. Verwenden Sie stattdessen "für XML-Pfad ('')".

+0

Das gibt ein Fehler: "Row-Tag-Auslassung (leerer Zeilen-Tag-Name) kann nicht mit attributzentrierter FOR XML-Serialisierung verwendet werden." –

+0

+1 Der Zuschlag wird implizit um 'NULL als Zuschlag' im oberen Teil der Union hinzugefügt, so dass Pfad ('surgcharge') doppelt aussieht – Andomar

+0

Ich bin verwirrt durch diesen Fehler. Was passiert, wenn Sie die zweite Auswahl in Ihrer Union selbst mit der Klausel "for xml" ausführen, wie ich es vorgeschlagen habe? –

1

Sie eine Spalte Abfrage hat, die mulitple Zeilen zurückgibt Ich würde erwarten, dass die Abfrage, die Sie schreiben, den Fehler geben (@charge, @rate und einen XML-Typen.):

Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.

Allerdings ist das leicht fest Verschieben Sie die Abfrage zu outer apply. Um die Doppel surcharge Element zu entfernen, können Sie die XML-Spaltennamen so weit nach unten wie möglich bewegen, wie:

;WITH Charges (code, rate, surcharge, InvoiceId) AS 
    (
    SELECT code, amount, NULL, InvoiceId 
    FROM @charges 
    UNION ALL 
    SELECT code 
    ,  amount 
    ,  (
      SELECT amount AS [@amount] 
      FROM @surcharges os 
      WHERE oc.ChargeId = os.ChargeId 
      FOR XML PATH('surcharge'), TYPE 
      ) 
    ,  InvoiceId 
    FROM @charges oc 
) 
SELECT Number AS [@number] 
,  c.code as [charge/@code] 
,  c.rate as [charge/@rate] 
,  c.surcharge as [charge] 
FROM @Invoices i 
outer apply 
     (
     SELECT code 
     ,  rate 
     ,  surcharge 
     FROM Charges 
     WHERE Charges.InvoiceId = i.InvoiceId 
     ) c 
WHERE i.InvoiceID = 1 
FOR XML PATH('invoice'), TYPE 

Dies würde drucken, zum Beispiel:

<invoice number="1"> 
    <charge code="1" rate="1" /> 
</invoice> 
<invoice number="1"> 
    <charge code="1" rate="1"> 
    <surcharge amount="1" /> 
    </charge> 
</invoice> 

Das erste Element kommen aus der obere Teil der Union, wo surcharge = null.