I'm trying to extract data from XML in my SQL column that has multiple lists of data in them that are stored in two different ways.
I'm only familiar with using cross apply to get a single list but how do I do it for multiple nodes, especially if they are in different structures?
DECLARE @XML XML =
'
<root>
<Bundle name="x">
<Profiles>
<Profile>
<ApplicationRef>
<Reference class="a" name="application1"/>
</ApplicationRef>
<Constraints>
<Filter property="e" value="ent1"/>
<Filter property="e" value="ent2"/>
</Constraints>
</Profile>
<Profile>
<ApplicationRef>
<Reference class="a" name="application2"/>
</ApplicationRef>
<Constraints>
<Filter property="e" value="entA"/>
<Filter property="e" value="entB"/>
</Constraints>
</Profile>
</Profiles>
</Bundle>
<Bundle name="y">
<Profiles>
<Profile>
<ApplicationRef>
<Reference class="a" name="application1"/>
</ApplicationRef>
<Constraints>
<Filter property="e" value="ent3"/>
<Filter property="e" value="ent4"/>
</Constraints>
</Profile>
<Profile>
<ApplicationRef>
<Reference class="a" name="application3"/>
</ApplicationRef>
<Constraints>
<Filter property="m" value="entA">
<Value>
<List>
<String>this</String>
<String>that</String>
<String>thus</String>
</List>
</Value>
</Filter>
</Constraints>
</Profile>
</Profiles>
</Bundle>
</root>
'
SELECT
Bun = XC.value('@name', 'VARCHAR(200)'),
App = XC2.value('@name', 'VARCHAR(200)'),
Ent = XC3.value('.', 'VARCHAR(200)')
FROM
@Xml.nodes('//Bundle') AS XT(XC)
CROSS APPLY
xc.nodes('//Bundle/Profiles/Profile/ApplicationRef/Reference') AS XT2(XC2)
CROSS APPLY
xc.nodes('//Bundle/Profiles/Profile/Constraints/Filter/Value/List/String') AS XT3(XC3)
I don't mind if the ent data is in a single column, but the Bun and App Columns need to different
Bun | App | Ent |
---|---|---|
x | application1 | ent1 |
x | application1 | ent2 |
x | application2 | entA |
x | application2 | entB |
y | application1 | ent3 |
y | application1 | ent4 |
y | application3 | this |
y | application3 | that |
y | application3 | thus |
Hopefully, here is what you are looking for.
SQL
DECLARE @xml XML =
N'<root>
<Bundle name="x">
<Profiles>
<Profile>
<ApplicationRef>
<Reference class="a" name="application1"/>
</ApplicationRef>
<Constraints>
<Filter property="e" value="ent1"/>
<Filter property="e" value="ent2"/>
</Constraints>
</Profile>
<Profile>
<ApplicationRef>
<Reference class="a" name="application2"/>
</ApplicationRef>
<Constraints>
<Filter property="e" value="entA"/>
<Filter property="e" value="entB"/>
</Constraints>
</Profile>
</Profiles>
</Bundle>
<Bundle name="y">
<Profiles>
<Profile>
<ApplicationRef>
<Reference class="a" name="application1"/>
</ApplicationRef>
<Constraints>
<Filter property="e" value="ent3"/>
<Filter property="e" value="ent4"/>
</Constraints>
</Profile>
<Profile>
<ApplicationRef>
<Reference class="a" name="application3"/>
</ApplicationRef>
<Constraints>
<Filter property="m" value="entA">
<Value>
<List>
<String>this</String>
<String>that</String>
<String>thus</String>
</List>
</Value>
</Filter>
</Constraints>
</Profile>
</Profiles>
</Bundle>
</root>';
SELECT p.value('@name', 'VARCHAR(200)') AS bundle_name
, c.value('(ApplicationRef/Reference/@name)[1]', 'VARCHAR(200)') AS app_name
, d.value('(.)[1]', 'VARCHAR(200)') AS constraints_filter
FROM @Xml.nodes('/root/Bundle') t1(p)
CROSS APPLY p.nodes('Profiles/Profile') AS t2(c)
CROSS APPLY c.nodes('Constraints/Filter/@value') AS t3(d)
UNION ALL
SELECT p.value('@name', 'VARCHAR(200)') AS bundle_name
, c.value('(ApplicationRef/Reference/@name)[1]', 'VARCHAR(200)') AS app_name
, e.value('(text())[1]', 'VARCHAR(200)') AS constraints_filter_list
FROM @Xml.nodes('/root/Bundle') t1(p)
CROSS APPLY p.nodes('Profiles/Profile') AS t2(c)
CROSS APPLY c.nodes('Constraints/Filter/Value/List/String') AS t4(e);
Output
bundle_name | app_name | constraints_filter |
---|---|---|
x | application1 | ent1 |
x | application1 | ent2 |
x | application2 | entA |
x | application2 | entB |
y | application1 | ent3 |
y | application1 | ent4 |
y | application3 | entA |
y | application3 | this |
y | application3 | that |
y | application3 | thus |