Come posso combinare CTE con una clausola FOR XML?
-
09-10-2019 - |
Domanda
Sto cercando di generare qualche XML con vari livelli di nidificazione, e con il rischio di un eccesso di semplificazione, l'XML uscita sarà liberamente del formato:
<invoice number="1">
<charge code="foo" rate="123.00">
<surcharge amount="10%" />
</charge>
<charge code="bar" />
</invoice>
Lo schema del database ho ereditato questo succede ad avere oneri memorizzati nelle tabelle diverse, il che significa che le maggiorazioni vengono memorizzati in modo diverso in base alla tabella da cui l'accusa era da.
Dato che si non è possibile utilizzare UNION
s con FOR XML
, ho fatto un po 'di UNION
ing in un CTE, in modo da qualcosa lungo la linee di:
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
Ora, che è incredibilmente close, dando (Si noti la <surcharge>
nidificato):
<invoice number="1">
<charge code="foo" rate="123.00">
<surcharge>
<surcharge amount="10%" />
</surcharge>
</charge>
<charge code="bar" />
</invoice>
Ma ho bisogno di trovare un modo di ottenere la query fine di includere il valore di una colonna XML da trattare come il contenuto dell'elemento, piuttosto che come un nuovo elemento. Questo è possibile, o ho bisogno di prendere un nuovo approccio?
Soluzione 2
Sembra che la denominazione della colonna (falso), come "*" userà che il contenuto di quella colonna come il contenuto dell'elemento, in modo da cambiare la SQL come di seguito lo fa funzionare:
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
Altri suggerimenti
È una query di colonna, che restituisce le righe mulitple mi aspetterei la query si registra per dare l'errore (@charge, @rate, e un tipo di XML.):
Una sola espressione può essere specificata nell'elenco di selezione quando il subquery Non viene introdotto con EXISTS.
Tuttavia, questo è facilmente risolto spostando la query a un outer apply
. Per rimuovere l'elemento doppio surcharge
, è possibile spostare i nomi delle colonne XML come molto più a fondo possibile, come:
;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
Questa sarebbe la stampa, ad esempio:
<invoice number="1">
<charge code="1" rate="1" />
</invoice>
<invoice number="1">
<charge code="1" rate="1">
<surcharge amount="1" />
</charge>
</invoice>
Il primo elemento viene dalla parte superiore del sindacato, dove surcharge = null
.
Penso che si può fare questo omettendo il tipo di nodo principale nel vostro "per XML Path ( 'a pagamento')" dichiarazione. Cioè, l'uso "per il percorso xml ( '')", invece.