Reputation: 8678
I am trying to parse below xml into table data. However, I am only getting one row. What do I need to do to get all the data? I have a link to demonstrate this as well.
INSERT BATCHES (BatchID, RawXML)
VALUES (1, '
<ParamData>
<moduleRole>
<moduleId>1</moduleId>
<bmRoleId>4</bmRoleId>
<moduleId>2</moduleId>
<bmRoleId>8</bmRoleId>
<moduleId>3</moduleId>
<bmRoleId>255</bmRoleId>
<moduleId>8</moduleId>
<bmRoleId>4</bmRoleId>
<moduleId>16</moduleId>
<bmRoleId>4</bmRoleId>
<moduleId>64</moduleId>
<bmRoleId>4</bmRoleId>
<moduleId>128</moduleId>
<bmRoleId>4</bmRoleId>
</moduleRole>
</ParamData>
');
SELECT
b.BatchID,
x.XmlCol.value('(moduleId)[1]','INT') AS moduleId,
x.XmlCol.value('(bmRoleId)[1]','INT') AS bmRoleId
FROM
Batches b
CROSS APPLY
b.RawXml.nodes('/ParamData/moduleRole') x(XmlCol);
Upvotes: 2
Views: 81
Reputation: 67311
I'd rather solve this with a numbers table. In my example I'll create one on-the-fly from master..spt_values
, which contains about 2.500 entries. This should suffice. We do not need the values, just the return of ROW_NUMBER()
:
WITH Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nr FROM master..spt_values)
SELECT b.BatchID
,b.RawXml.value(N'(/ParamData/moduleRole/moduleId)[sql:column("Nr")][1]','int') AS moduleId
,b.RawXml.value(N'(/ParamData/moduleRole/bmRoleId)[sql:column("Nr")][1]','int') AS bmRoleId
FROM Numbers
CROSS JOIN [BATCHES] AS b
WHERE b.RawXml.exist(N'(/ParamData/moduleRole/moduleId)[sql:column("Nr")]')=1;
The result
1 1 4
1 2 8
1 3 255
1 8 4
1 16 4
1 64 4
1 128 4
It would help - if performance matters - to use TOP
before ROW_NUMBER
to limit the numbers for the CROSS JOIN
to an appropriate count...
Upvotes: 0
Reputation: 89285
I would think to shred the XML on moduleId
, then select current element and the nearest following sibling bmRoleId
element :
SELECT b.BatchID,
x.XmlCol.value('.','INT') AS moduleId,
x.XmlCol.value('following-sibling::bmRoleId)[1]','INT') AS bmRoleId
FROM Batches b
CROSS APPLY b.RawXml.nodes('/ParamData/moduleRole/moduleId') x(XmlCol);
Unfortunately, following-sibling
axis isn't supported by SQL Server, so we need to use the trick mentioned in this post :
SELECT b.BatchID,
x.XmlCol.value('.','INT') AS moduleId,
x.XmlCol.value('let $c := . return (../bmRoleId[. >> $c])[1]','INT') AS bmRoleId
FROM Batches b
CROSS APPLY b.RawXml.nodes('/ParamData/moduleRole/moduleId') x(XmlCol);
This part return (../bmRoleId[. >> $c])[1]
, get all bmRoleId
that located after current moduleId
(referenced by $c
), and then limit the result to the first of such bmRoleId
in document order
Upvotes: 3