¿Cómo puedo combinar CTE con una cláusula FOR XML?
-
09-10-2019 - |
Pregunta
Estoy tratando de generar algo de XML con varios niveles de anidación, y corriendo el riesgo de simplificar en exceso, el XML de salida será ligeramente del formato:
<invoice number="1">
<charge code="foo" rate="123.00">
<surcharge amount="10%" />
</charge>
<charge code="bar" />
</invoice>
El esquema de base He heredado para este pasa a tener cargas almacenadas en tablas diferentes, lo que significa que los recargos se almacenan de forma diferente, según la tabla de donde la carga era.
UNION
s con FOR XML
, he hecho algunos UNION
ing en un CTE, por lo que algo a lo largo del líneas de:
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
Ahora, que es muy cerrar, dando (Nota del <surcharge>
anidada):
<invoice number="1">
<charge code="foo" rate="123.00">
<surcharge>
<surcharge amount="10%" />
</surcharge>
</charge>
<charge code="bar" />
</invoice>
Pero tengo que encontrar una manera de conseguir la consulta final para incluir el valor de una columna XML a ser tratado como el contenido del elemento, en lugar de como un nuevo elemento. Es esto posible, o tengo que tomar un nuevo enfoque?
Solución 2
Parece que el nombramiento de la columna (falso) como "*" utilizará que el contenido de esa columna que el contenido del elemento, por lo que cambiar el SQL de la siguiente manera hace que funcione:
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
Otros consejos
Tiene una consulta que devuelve la columna filas haya numerosas Yo esperaría que la consulta que publicas para dar el error (@charge, @rate, y un tipo XML.):
Sólo una expresión se puede especificar en la lista de selección cuando la subconsulta No se introduce con EXISTS.
Sin embargo, eso es fácil de solucionar moviendo la consulta a un outer apply
. Para quitar el elemento de doble surcharge
, se podía mover los nombres de las columnas XML en cuanto al fondo como sea posible, como:
;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
Esto imprimir, por ejemplo:
<invoice number="1">
<charge code="1" rate="1" />
</invoice>
<invoice number="1">
<charge code="1" rate="1">
<surcharge amount="1" />
</charge>
</invoice>
El primer elemento proviene de la parte superior de la unión, donde surcharge = null
.
creo que se puede hacer esto mediante la omisión del tipo nodo raíz en su "camino para XML ( 'suplemento')" comunicado. Es decir, el uso "para la ruta XML ( '')" en su lugar.