Create XML with variable element names from a data

2019-01-29 07:16发布

问题:

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

回答1:

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)


回答2:

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.