I wasn't able to find a relevant post, so I decided to ask.
I have the following table in my SQL Server database:
ID attname value
---------------------------------
22405543 blktradind N
22405543 brkref IRVTGB2X
22405543 buyamt 104650.2000
22405543 buycurref USD
22405543 Buy53ref
22405543 Buy56ref
22405543 Buy57ref IRVTBEBB
How can I convert this table by using FOR XML variations to a dynamic XML result based on the "attname" that each message has?
For the excerpt above, the desired result would be:
<Message id=22405543>
<blktradind>N</blktradind>
<brkref>IRVTGB2X</brkref>
<buyamt>104650.2000</buyamt>
<buycurref>USD</buycurref>
<buy53ref />
<buy56ref />
<buy57ref>IRVTBEBB</buy57ref>
</Message>
Thanks
This is not possible normally. SQL Server does not support variable column aliases for your output. But there are workarounds:
string concatenation
The approach is a bit ugly, as I normally would not prefer to create XML via string concatenation. But by wrapping the value with a SELECT FOR XML PATH
itself, this is even stable with forbidden characters like <> or &
.
DECLARE @tbl TABLE(ID BIGINT,attname NVARCHAR(100),value NVARCHAR(100));
INSERT INTO @tbl VALUES
(22405543,'blktradind','N')
,(22405543,'brkref','IRVTGB2X')
,(22405543,'buyamt','104650.2000')
,(22405543,'buycurref','USD')
,(22405543,'Buy53ref',NULL)
,(22405543,'Buy56ref',NULL)
,(22405543,'Buy57ref','IRVTBEBB');
WITH DistinctIDs AS
(
SELECT DISTINCT ID FROM @tbl
)
SELECT ID AS [@id]
,(
SELECT CAST(N'<' + attname + N'>' + ISNULL((SELECT value AS [*] FOR XML PATH('')),N'') + N'</' + attname + N'>' AS XML)
FROM @tbl AS tbl
WHERE tbl.ID=DistinctIDs.ID
FOR XML PATH(''),TYPE
)
FROM DistinctIDs
FOR XML PATH('Message')
The result
<Message id="22405543">
<blktradind>N</blktradind>
<brkref>IRVTGB2X</brkref>
<buyamt>104650.2000</buyamt>
<buycurref>USD</buycurref>
<Buy53ref />
<Buy56ref />
<Buy57ref>IRVTBEBB</Buy57ref>
</Message>
dynamic SQL
You could build the full statement dynamically and use EXEC(@cmd)
to execute it. Something like this:
(Attention!!: The SELECT TOP 1
to get the ID is not appropriate for actual data!)
DECLARE @cmd NVARCHAR(MAX)=
(
SELECT 'SELECT ''' + CAST((SELECT TOP 1 ID FROM @tbl) AS NVARCHAR(100)) + ''' AS [@id] '
+ (
SELECT ',''' + ISNULL(value,'') + ''' AS [' + attname + ']'
FROM @tbl
FOR XML PATH('')
)
+ ' FOR XML PATH(''Message'')'
);
EXEC(@cmd)
As far as I know, there is no control over the way the output is generated in the FOR XML output, aside from manipulating the data types and field names in the query.
You would need to generate it as it normally does, then reprocess it possibly with XSLT to make it into what you want.
This could actually be done on the server if it has support for the CLR.