XML Parsing in SQL Server

↘锁芯ラ 提交于 2019-12-08 14:20:13

问题


The requirement is to parse all the 27 values related to MarginRevenue/Cost/Value. As a standard scenario, these elements can come in any order and also can come in any number of time. In short, the XML is completely dynamic. The only point is Under Margins/Margin, there can be any number of MarginRevenue, MarginCost and MarginValue.

declare @xml xml = '<Margins>
<Margin type="type1" currencyCode="currencyCode1"> 
<MarginRevenue>1.1</MarginRevenue> 
<MarginRevenue>1.2</MarginRevenue> 
<MarginRevenue>1.3</MarginRevenue> 
<MarginCost>2.1</MarginCost> 
<MarginCost>2.2</MarginCost> 
<MarginCost>2.3</MarginCost> 
<MarginValue>3.1</MarginValue> 
<MarginValue>3.2</MarginValue> 
<MarginValue>3.3</MarginValue> </Margin> 
<Margin type="type2"  currencyCode="currencyCode2"> 
<MarginRevenue>1.4</MarginRevenue> 
<MarginRevenue>1.5</MarginRevenue> 
<MarginRevenue>1.6</MarginRevenue> 
<MarginCost>2.4</MarginCost> 
<MarginCost>2.5</MarginCost> 
<MarginCost>2.6</MarginCost> 
<MarginValue>3.4</MarginValue> 
<MarginValue>3.5</MarginValue> 
<MarginValue>3.6</MarginValue> </Margin> 
<Margin type="type3" currencyCode="currencyCode3"> 
<MarginRevenue>1.7</MarginRevenue> 
<MarginRevenue>1.8</MarginRevenue> 
<MarginRevenue>1.9</MarginRevenue> 
<MarginCost>2.7</MarginCost> 
<MarginCost>2.8</MarginCost> 
<MarginCost>2.9</MarginCost> 
<MarginValue>3.7</MarginValue> 
<MarginValue>3.8</MarginValue> 
<MarginValue>3.9</MarginValue> </Margin>
</Margins>'

回答1:


Not quite clear what you're trying to achieve / what you're looking to get - but this XQuery will return all 27 values for you:

SELECT
    Name = XC.value('local-name(.)', 'varchar(100)'),
    Value = XC.value('.', 'varchar(100)')
FROM
    @xml.nodes('/Margins/Margin/*') AS XT(XC)

Results in SSMS:




回答2:


It seems as if you want to map your data just on their position in the XML. I think so, because I've read and answered your previous question. In the comments I gave you a bunch of hints and questions you should provide as a new question (to avoid chameleons). This question now is very hard on the edge of - uhm - something with "impolite"...

Nevertheless I provide an answer but ask you clearly to invest some more time into your questions. You expect us to invest our time in good answers.

Your xml

declare @xml xml = '<Margins>
<Margin type="type1" currencyCode="currencyCode1"> 
<MarginRevenue>1.1</MarginRevenue> 
<MarginRevenue>1.2</MarginRevenue> 
<MarginRevenue>1.3</MarginRevenue> 
<MarginCost>2.1</MarginCost> 
<MarginCost>2.2</MarginCost> 
<MarginCost>2.3</MarginCost> 
<MarginValue>3.1</MarginValue> 
<MarginValue>3.2</MarginValue> 
<MarginValue>3.3</MarginValue> </Margin> 
<Margin type="type2"  currencyCode="currencyCode2"> 
<MarginRevenue>1.4</MarginRevenue> 
<MarginRevenue>1.5</MarginRevenue> 
<MarginRevenue>1.6</MarginRevenue> 
<MarginCost>2.4</MarginCost> 
<MarginCost>2.5</MarginCost> 
<MarginCost>2.6</MarginCost> 
<MarginValue>3.4</MarginValue> 
<MarginValue>3.5</MarginValue> 
<MarginValue>3.6</MarginValue> </Margin> 
<Margin type="type3" currencyCode="currencyCode3"> 
<MarginRevenue>1.7</MarginRevenue> 
<MarginRevenue>1.8</MarginRevenue> 
<MarginRevenue>1.9</MarginRevenue> 
<MarginCost>2.7</MarginCost> 
<MarginCost>2.8</MarginCost> 
<MarginCost>2.9</MarginCost> 
<MarginValue>3.7</MarginValue> 
<MarginValue>3.8</MarginValue> 
<MarginValue>3.9</MarginValue> </Margin>
</Margins>';

The first approach uses a numbers table (in this case an on-the-fly-tally) to get a set of numbers. The query then uses this number with sql:column() to fetch the elements by their positions:

DECLARE @maxCount INT=
(
    SELECT MAX(marg.value('count(MarginRevenue)','int'))
    FROM @xml.nodes('Margins/Margin') A(marg)
);

WITH Tally AS(SELECT TOP(@maxCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nmbr FROM master..spt_values) --select * from tally
SELECT Tally.Nmbr
      ,marg.value('@type','varchar(100)') AS margin_type
      ,marg.value('(MarginRevenue)[sql:column("Nmbr")][1]','decimal(15,5)') MarginRevenue
      ,marg.value('(MarginCost)[sql:column("Nmbr")][1]','decimal(15,5)') MarginCost
      ,marg.value('(MarginValue)[sql:column("Nmbr")][1]','decimal(15,5)') MarginValue
FROM Tally
OUTER APPLY @xml.nodes('Margins/Margin') A(marg)
ORDER BY marg.value('@type','varchar(100)'),Nmbr;

The second approach uses a serie of CTEs to get each "type" of data in a separate but numbered list. The three sets are combined with a JOIN on their positional index:

WITH margins AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS MarginIndex
          ,marg.query('MarginRevenue') AS MRevenues
          ,marg.query('MarginCost') AS MCosts
          ,marg.query('MarginValue') AS MValues 
    FROM @xml.nodes('Margins/Margin') A(marg)
)
,MRevs AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY MarginIndex ORDER BY (SELECT NULL)) AS RevIndex
          ,MarginIndex
          ,x.value('text()[1]','decimal(15,5)') AS RevValue
    FROM margins
    CROSS APPLY MRevenues.nodes('./*') A(x)
)
,MCosts AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY MarginIndex ORDER BY (SELECT NULL)) AS CostIndex
          ,MarginIndex
          ,x.value('text()[1]','decimal(15,5)') AS CostValue
    FROM margins
    CROSS APPLY MCosts.nodes('./*') A(x)
)
,MValues AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY MarginIndex ORDER BY (SELECT NULL)) AS ValueIndex
          ,MarginIndex
          ,x.value('text()[1]','decimal(15,5)') AS ValueValue
    FROM margins
    CROSS APPLY MValues.nodes('./*') A(x)
)
SELECT m.MarginIndex
      ,r.RevIndex
      ,r.RevValue
      ,c.CostValue
      ,v.ValueValue
FROM margins m
INNER JOIN MRevs r ON m.MarginIndex=r.MarginIndex
INNER JOIN MCosts c ON r.MarginIndex=c.MarginIndex AND r.RevIndex=c.CostIndex
INNER JOIN MValues v ON r.MarginIndex=v.MarginIndex AND r.RevIndex=v.ValueIndex;


来源:https://stackoverflow.com/questions/51616198/xml-parsing-in-sql-server

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!