问题
I'm getting some XML data that I need to 'shred' (I think that's the right term). That is I need to put it into a SQL table. Here's an example, and a query that works, but I was told it was inefficient. Please let me know if you know a more efficient way to do this.
Here's some example XML, 2 queries that don't work, and one that does:
DECLARE @XmlReportParameters NVARCHAR (MAX) = N'<?xml version="1.0" encoding="utf-16"?>
<Customers>
<Customer>
<Name>Sri Patel</Name>
<FavColors>
<FavColor>Red</FavColor>
<FavColor>Blue</FavColor>
<FavColor>Green</FavColor>
</FavColors>
</Customer>
<Customer>
<Name>Jane Doe</Name>
<FavColors>
<FavColor>Violet</FavColor>
<FavColor>Mauve</FavColor>
</FavColors>
</Customer>
</Customers>
'
DECLARE @doc XML;
DECLARE @XmlTable TABLE
(
XmlColumn XML NULL
);
SET @doc = @XmlReportParameters;
INSERT INTO @XmlTable
( XmlColumn )
VALUES
( @doc )
-- Wrong Way
SELECT
tbl.col.value('(Name)[1]', 'nvarchar(max)') AS CustomerName
,tbl.col.value('(FavColors/FavColor)[1]', 'nvarchar(max)') AS FavColor
FROM
@XmlTable xt
CROSS APPLY XmlColumn.nodes('/Customers/Customer') tbl(col);
-- Still wrong (but I'm not sure why)
SELECT
tbl.col.value('(../Name)[1]', 'nvarchar(max)') AS CustomerName
,tbl.col.value('(FavColor)[1]', 'nvarchar(max)') AS FavColor
FROM
@XmlTable xt
CROSS APPLY XmlColumn.nodes('/Customers/Customer/FavColors') tbl(col);
-- Right Way
SELECT
tbl.col.value('(../../Name)[1]', 'nvarchar(max)') AS CustomerName
,tbl.col.value('(.)[1]', 'nvarchar(max)') AS FavColor
FROM
@XmlTable xt
CROSS APPLY XmlColumn.nodes('/Customers/Customer/FavColors/FavColor') tbl(col);
Returns:
CustomerName FavColor
------------ ----------
Sri Patel Red
Jane Doe Violet
CustomerName FavColor
------------ ----------
Sri Patel Red
Jane Doe Violet
CustomerName FavColor
------------ ----------
Sri Patel Red
Sri Patel Blue
Sri Patel Green
Jane Doe Violet
Jane Doe Mauve
回答1:
As I've told you at the other question before, the solution is a hierarchical call to .nodes()
SELECT
cust.value('(Name/text())[1]', 'nvarchar(max)') AS CustomerName
,col.value('text()[1]', 'nvarchar(max)') AS FavColor
FROM
@XmlTable xt
CROSS APPLY XmlColumn.nodes('/Customers/Customer') A(cust)
CROSS APPLY cust.nodes('FavColors/FavColor') AS B(col) ;
This will first return all <Customer>
and - below them - their related <FavColor>
:
Customer FavColor
Sri Patel Red
Sri Patel Blue
Sri Patel Green
Jane Doe Violet
Jane Doe Mauve
回答2:
The "ineffecency" here is using the Parent Axis to traverse back up the document to get the name. The preferred way to do this is to use multiple APPLY operators. The first one would project out the Customer nodes, and then a second one to project the FavColors nodes. Like this:
SELECT
CustomerNode.e.value('(Name)[1]', 'nvarchar(max)') AS CustomerName
,FavColorNode.e.value('(.)[1]', 'nvarchar(max)') AS FavColor
FROM
@XmlTable xt
CROSS APPLY XmlColumn.nodes('/Customers/Customer') CustomerNode(e)
CROSS APPLY CustomerNode.e.nodes('FavColors/FavColor') FavColorNode(e);
Although the difference between them is probably not material for most scenarios.
来源:https://stackoverflow.com/questions/48950651/trouble-getting-xml-data-into-relational-format-in-sql-server-2014