T-SQL解析同一行上具有相同节点的XML

时间:2017-03-01 20:07:32

标签: sql-server xml tsql

我需要解析以下XML: https://www.iftach.org/taxmatrix/charts/4Q2016.xml

每个RECORD节点必须位于不同的行中:

<RECORD>
        <JURISDICTION ID="#15"  >AB</JURISDICTION>
        <COUNTRY>CAN</COUNTRY>
        <FUEL_TYPE>Gasoline</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Special Diesel</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Gasohol</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Propane</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.2700</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.0940</RATE>
        <FUEL_TYPE>LNG</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.0000</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.0000</RATE>
        <FUEL_TYPE>CNG</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.0000</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.0000</RATE>
        <FUEL_TYPE>Ethanol</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Methanol</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>E-85</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>M-85</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>A55</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Biodiesel</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
    </RECORD>

我想在我的集合中跟踪列:

  • 司法管辖权
  • 国家/地区
  • GasolineUSA
  • GasolineCAN
  • SpecialDieselUSA
  • SpecialDieselCAN
  • ...

这在T_SQL中是否可行?在C#中,我只是从上到下读取XML,并根据XML中的位置将TYPE连接到RATE。我在SQL中使用它更方便..

2 个答案:

答案 0 :(得分:3)

因此,就XML而言,我认为这是更难的部分,是的,你可以:

DECLARE @xml xml = '<RECORD>
        <JURISDICTION ID="#15"  >AB</JURISDICTION>
        <COUNTRY>CAN</COUNTRY>
        <FUEL_TYPE>Gasoline</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Special Diesel</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Gasohol</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Propane</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.2700</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.0940</RATE>
        <FUEL_TYPE>LNG</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.0000</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.0000</RATE>
        <FUEL_TYPE>CNG</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.0000</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.0000</RATE>
        <FUEL_TYPE>Ethanol</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Methanol</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>E-85</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>M-85</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>A55</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
        <FUEL_TYPE>Biodiesel</FUEL_TYPE>
        <RATE COUNTRY="US" RATECHANGE="0">0.3734</RATE>
        <RATE COUNTRY="CAN" RATECHANGE="0">0.1300</RATE>
    </RECORD>'


;WITH xCTE AS
(
    SELECT @xml.query('
    <Data jurisdiction="{data(/RECORD/JURISDICTION/text())}" country="{data(/RECORD/COUNTRY/text())}">
        {
            for $x in (/RECORD/*[local-name() = "RATE"]) 
            return
                <FuelType name="{data(/RECORD/FUEL_TYPE[. << $x][last()])}" country="{data($x/@COUNTRY)}" value="{data($x)}" />
        }
    </Data>
        ') AS DocXml
)
SELECT
    x.n.value('../@jurisdiction', 'VARCHAR(10)') as Jurisdiction
    ,x.n.value('../@country', 'VARCHAR(5)') as Country
    ,x.n.value('@name', 'VARCHAR(20)') as Name
    ,x.n.value('@country', 'VARCHAR(5)') as RateCountry
    ,x.n.value('@value', 'DECIMAL(10,4)') as Rate
FROM xCTE
CROSS APPLY DocXml.nodes('/Data/FuelType') x(n)

基本思想:在CTE中使用XQuery将原始文档转换为更易于管理的内容 - 获取当前FUEL_TYPE节点之前的最后一个RATE节点,并将所有内容填充到单个XML元素中(或元素组 - 属性更紧凑,更容易使用IMO)。

这并没有给你想要的输出 - 它提供了这样的输出:

Jurisdiction Country Name                 RateCountry Rate
------------ ------- -------------------- ----------- ---------------------------------------
AB           CAN     Gasoline             US          0.3734
AB           CAN     Gasoline             CAN         0.1300
AB           CAN     Special Diesel       US          0.3734
AB           CAN     Special Diesel       CAN         0.1300
...

如果你想从那里出发,你应该可以做某种CROSS APPLYPIVOT,但在我走这条路之前,我会考虑是否会这样做&{0}} #39; s 真的你想要什么,或者这实际上会更好。

答案 1 :(得分:3)

这是另一种解决方案,可能会更容易一些。这个想法与Dan的方法大致相同(这很棒!)。

差异:我使用Configuration获取所有APPLY .nodes()个节点,并查找他们的上一个 <RATE> *。

<FUEL_TYPE>

结果的第一行:

SELECT @xml.value(N'(/RECORD/JURISDICTION/@ID)[1]',N'nvarchar(max)') AS JURISDICTION_ID
      ,@xml.value(N'(/RECORD/JURISDICTION/text())[1]',N'nvarchar(max)') AS JURISDICTION
      ,@xml.value(N'(/RECORD/COUNTRY/text())[1]',N'nvarchar(max)') AS COUNTRY
      ,r.value(N'let $r:=. return ../FUEL_TYPE[.<<$r][last()]',N'varchar(max)') AS FUEL_TYPE
      ,r.value(N'(./@COUNTRY)[1]',N'nvarchar(max)') AS RATE_COUNTRY
      ,r.value(N'(./@RATECHANGE)[1]',N'int') AS RATE_RATECHANGE
      ,r.value(N'(./text())[1]',N'decimal(8,4)') AS RATE

FROM @xml.nodes(N'/RECORD/RATE') AS A(r)

更新如果硬编码并按固定顺序......

这种暴力方法怎么样?

+-----------------+--------------+---------+----------------+--------------+-----------------+--------+
| JURISDICTION_ID | JURISDICTION | COUNTRY | FUEL_TYPE      | RATE_COUNTRY | RATE_RATECHANGE | RATE   |
+-----------------+--------------+---------+----------------+--------------+-----------------+--------+
| #15             | AB           | CAN     | Gasoline       | US           | 0               | 0.3734 |
+-----------------+--------------+---------+----------------+--------------+-----------------+--------+
| #15             | AB           | CAN     | Gasoline       | CAN          | 0               | 0.1300 |
+-----------------+--------------+---------+----------------+--------------+-----------------+--------+
| #15             | AB           | CAN     | Special Diesel | US           | 0               | 0.3734 |
+-----------------+--------------+---------+----------------+--------------+-----------------+--------+
| #15             | AB           | CAN     | Special Diesel | CAN          | 0               | 0.1300 |
+-----------------+--------------+---------+----------------+--------------+-----------------+--------+
| #15             | AB           | CAN     | Gasohol        | US           | 0               | 0.3734 |