Reputation: 3
Is it possible, using SQL Server 2014 Standard Edition, to PIVOT on the value of an XML column name? Ultimately I'd like to take this:
<table>
<id>{3d2699c4-3159-4e8b-b48c-c2c4c9b5bd77}</id>
<rows>
<row>
<columns>
<column name="DESC" value="DACS" type="System.String" />
<column name="ec_amount" value="5000" type="System.Decimal" />
<column name="ec_exrate" value="1" type="System.Decimal" />
<column name="ec_total" value="5000.00" type="System.Decimal" />
<column name="ItemNo" value="PVT-C30" type="System.String" />
<column name="UOM" value="EA" type="System.String" />
<column name="DefaultKey" value="1" type="System.Int32" /><----THIS IS THE COLUMN ON WHICH I WOULD LIKE TO PIVOT
</columns>
</row>
<row>
<columns>
<column name="DESC" value="DACS" type="System.String" />
<column name="ec_amount" value="1500" type="System.Decimal" />
<column name="ec_exrate" value="5" type="System.Decimal" />
<column name="ec_total" value="7500.00" type="System.Decimal" />
<column name="ItemNo" value="PVT-C30" type="System.String" />
<column name="UOM" value="EA" type="System.String" />
<column name="DefaultKey" value="2" type="System.Int32" />
</columns>
</row>
</rows>
<key>DefaultKey</key>
<total>12500.00</total>
<data />
<parameters />
</table>
....and create these results:
DefaultKey DESC ec_amount ec_exrate ec_total ItemNo UOM
1 DACS 5000 1 5000 PVT-C30 EA
2 DACS 1500 5 7500 PVT-C30 EA
I've searched the Stack Overflow site and of everything I came across, these two posts came close, but they don't quite get me there:
How do I Pivot on an XML column's attributes in T-SQL
My apologies if this has already been addressed and I simply gave up too soon.
Upvotes: 0
Views: 3169
Reputation: 67291
No need for pivoting... Try this
DECLARE @xml XML=N'<table>
<id>{3d2699c4-3159-4e8b-b48c-c2c4c9b5bd77}</id>
<rows>
<row>
<columns>
<column name="DESC" value="DACS" type="System.String" />
<column name="ec_amount" value="5000" type="System.Decimal" />
<column name="ec_exrate" value="1" type="System.Decimal" />
<column name="ec_total" value="5000.00" type="System.Decimal" />
<column name="ItemNo" value="PVT-C30" type="System.String" />
<column name="UOM" value="EA" type="System.String" />
<column name="DefaultKey" value="1" type="System.Int32" />
</columns>
</row>
<row>
<columns>
<column name="DESC" value="DACS" type="System.String" />
<column name="ec_amount" value="1500" type="System.Decimal" />
<column name="ec_exrate" value="5" type="System.Decimal" />
<column name="ec_total" value="7500.00" type="System.Decimal" />
<column name="ItemNo" value="PVT-C30" type="System.String" />
<column name="UOM" value="EA" type="System.String" />
<column name="DefaultKey" value="2" type="System.Int32" />
</columns>
</row>
</rows>
<key>DefaultKey</key>
<total>12500.00</total>
<data />
<parameters />
</table>';
SELECT r.value(N'(columns/column[@name="DefaultKey"]/@value)[1]',N'int') AS DefaultKey
,r.value(N'(columns/column[@name="DESC"]/@value)[1]',N'nvarchar(max)') AS [DESC]
,r.value(N'(columns/column[@name="ec_amount"]/@value)[1]',N'decimal(10,4)') AS ec_amount
--similar with your other elements
FROM @xml.nodes(N'/table/rows/row') AS A(r)
Upvotes: 1
Reputation: 2874
You essentially need to assign a row number to each "row" of XML (or figure out some other way of distinguishing each "row"). One way you could go about this is by extracting the default key from each "row" (by using a window function and partitioning):
SELECT *
FROM
(
SELECT Name = C2.X.value('@name', 'varchar(max)'),
Val = C2.X.value('@value', 'varchar(max)'),
DefaultKey = MAX(CASE WHEN C2.X.value('@name', 'varchar(max)') = 'DefaultKey' THEN C2.X.value('@value', 'varchar(max)') END) OVER(PARTITION BY C1.X)
FROM myTable
CROSS APPLY myXMLColumn.nodes('table/rows/row/columns') AS C1(X)
CROSS APPLY C1.X.nodes('column') AS C2(X)
) AS T
PIVOT (MAX(Val) FOR Name IN ([DESC], [ec_amount], [ec_exrate], [ec_total], [ItemNo], [UOM])) AS P;
If DefaultKey weren't unique, you could use a row number or rank window function instead to each a similar result:
SELECT *
FROM
(
SELECT Name = C2.X.value('@name', 'varchar(max)'),
Val = C2.X.value('@value', 'varchar(max)'),
RowInXML = DENSE_RANK() OVER (ORDER BY C1.X)
FROM myTable
CROSS APPLY myXMLColumn.nodes('table/rows/row/columns') AS C1(X)
CROSS APPLY C1.X.nodes('column') AS C2(X)
) AS T
PIVOT (MAX(Val) FOR Name IN ([DefaultKey], [DESC], [ec_amount], [ec_exrate], [ec_total], [ItemNo], [UOM])) AS P;
Upvotes: 1