I am looking for an efficient way to produce output that looks like XML, grouped by columns of original data from SQL Server 2016, targeting a specific output format.
-- DDL and sample data population, start
DECLARE @tbl TABLE
(
ID INT IDENTITY PRIMARY KEY,
FirstName VARCHAR(20),
MiddleName VARCHAR(20),
LastName VARCHAR(20)
);
INSERT @tbl (FirstName, MiddleName, LastName)
VALUES ('Fred', 'A.','Smith'),
('Anna', NULL,'Polack');
-- DDL and sample data population, end
I can do this :
DECLARE @Try XML
SET @Try = (SELECT
('<Answer name = "FirstName">' +
(SELECT FirstName AS 'value'
FROM @tbl
ORDER BY ID
FOR XML PATH(''), ROOT('repeat'), ELEMENTS XSINIL) +
'</Answer>')
+
(SELECT
('<Answer name = "MiddleName">' +
(SELECT MiddleName AS 'value'
FROM @tbl
ORDER BY ID
FOR XML PATH(''), ROOT('repeat'), ELEMENTS XSINIL) +
'</Answer>')
+
(SELECT
('<Answer name = "LastName">' +
(SELECT LastName AS 'value'
FROM @tbl
ORDER BY ID
FOR XML PATH(''), ROOT('repeat'), ELEMENTS XSINIL) +
'</Answer>')
)))
SELECT @Try
to get:
<Answer name="FirstName">
<repeat xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<value>Fred</value>
<value>Anna</value>
</repeat>
</Answer>
<Answer name="MiddleName">
<repeat xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<value>A.</value>
<value xsi:nil="true" />
</repeat>
</Answer>
<Answer name="LastName">
<repeat xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<value>Smith</value>
<value>Polack</value>
</repeat>
</Answer>
I can hack by converting to text and replacing the xmlns stuff to get desired output, which is:
<Answer name="FirstName">
<repeat>
<value>Fred</value>
<value>Anna</value>
</repeat>
</Answer>
<Answer name="MiddleName">
<repeat>
<value>A.</value>
<value xsi:nil="true" />
</repeat>
</Answer>
<Answer name="LastName">
<repeat>
<value>Smith</value>
<value>Polack</value>
</repeat>
</Answer>
But there must be a more efficient way of generating this output without explicitly querying every column (my actual data set has many columns).
The following code will do what you want, if not very efficiently.
Answer/repeat
nodes./row/*[local-name() = local-name($header)]
is likely to be particularly costlySELECT
x.xml.query('
for $header in /row[1]/*
return
<Answer name="{local-name($header)}">
<repeat>{
for $val in /row/*[local-name() = local-name($header)]
return <value>{data($val)}</value>
}</repeat>
</Answer>')
FROM (
SELECT *
FROM @tbl
FOR XML PATH('row'), TYPE
) x(xml);
It's probably more efficient to use your existing code and query the data multiple times.