Using SQL Server 2016, primarily.
I have data available to me that is formatted in XML, but I need to be able to join this data to other tables in a SQL Server database.
I have looked around, but the examples I have found use a different data layout in the XML. The examples have distinct node names in the document. My XML has a node named metadata
that defines the name, data type, and length of each data item
. In the data
node, each row
has value
nodes that correspond with the item
nodes in the metadata
node. Also, my XML uses namespacing. None of the examples I have found do. I have had problems with namespacing in other SQL/XML tasks I have worked through, so that is probably significant.
How can I use SQL to convert something like this...
<?xml version="1.0" encoding="utf-8"?>
<dataset xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<!--
<dataset
xmlns="http://developer.cognos.com/schemas/xmldata/1/"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://developer.cognos.com/schemas/xmldata/1/ xmldata.xsd"
>
-->
<metadata>
<item name="Task" type="xs:string" length="-1"/>
<item name="Task Number" type="xs:string" length="-1"/>
<item name="Group" type="xs:string" length="-1"/>
<item name="Work Order" type="xs:string" length="-1"/>
</metadata>
<data>
<row>
<value>3361B11</value>
<value>1</value>
<value>01</value>
<value>MS7579</value>
</row>
<row>
<value>3361B11</value>
<value>2</value>
<value>50</value>
<value>MS7579</value>
</row>
<row>
<value>3361B11</value>
<value>3</value>
<value>02</value>
<value>JA0520</value>
</row>
</data>
</dataset>
...into a tabular format like...
Task | Task Number | Group | Work Order |
---|---|---|---|
3361B11 | 1 | 01 | MS7579 |
3361B11 | 2 | 50 | MS7579 |
3361B11 | 3 | 02 | JA0520 |
...so I can join it to other data.
I think the first step would be to query the metadata
node to get the column name, data type, and length.
input (boiled down)
<?xml version="1.0" encoding="utf-8"?>
<dataset xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<metadata>
<item name="Task" type="xs:string" length="-1"/>
<item name="Task Number" type="xs:string" length="-1"/>
<item name="Group" type="xs:string" length="-1"/>
<item name="Work Order" type="xs:string" length="-1"/>
</metadata>
</dataset>
output
id | name | type | length |
---|---|---|---|
1 | Task | string | -1 |
2 | Task Number | string | -1 |
3 | Group | string | -1 |
4 | Work Order | string | -1 |
Once that is obtained, the next part would be generated dynamically using those values.
In case it is helpful for fiddling, here is a SQL statement that produces the desired result:
with
xlTask as (
select *
from (
values
('3361B11', '1', '01', 'MS7579')
, ('3361B11', '2', '50', 'MS7579')
, ('3361B11', '3', '02', 'JA0520')
) q ([Task], [Task Number], [Group], [Work Order])
)
select *
from xlTask
4条答案
按热度按时间uoifb46i1#
Please try the following solution.
It will work on the condition if all
<value>
XML elements always have the same sequential order, from 1 to 4.After that having data as a relational/rectangular dataset you can join it with real tables/views in the database.
SQL
Output
ukdjmx9f2#
If you don't know the ordering you can pull that out of the
metadata
node, and pass it in to predicates using thesql:column
function.Unfortunately, SQL Server does not support the returning the
position(.)
XQuery function outside a predicate, so we need to hack it by counting nodes.The logic is as follows:
Within a subquery:
In XQuery, take the
metadata
node, and look for a descendantitem
which hasname = "Task"
and so on. Assign that node to$i
.Return the count of
item
nodes that are before$i
plus one.And so on for each column name.
Then shred the rest of the XML as normal.
In the predicates, use
position() = sql:column("YourColumnName")
which gives us the previously calculated position.db<>fiddle
If you want a completely dynamic solution, with dynamic column names, then you need dynamic SQL.
Note that
sp_executesql
is used, in order to pass the@xml
variable all the way through.db<>fiddle
gdx19jrr3#
Here's a dynamic solution from your sample data:
It reads the metadata and generates sql that reads actual rows and creates proper aliases
ldxq2e6h4#
Maybe you can try to create something to convert any XML to a SQL Table and then use SQL to extract the data
You can try the following: