【问题标题】:How do I PIVOT on an XML column?如何在 XML 列上进行 PIVOT?
【发布时间】:2017-11-16 23:09:47
【问题描述】:

是否可以使用 SQL Server 2014 标准版对 XML 列名的值进行 PIVOT?最终我想接受这个:

<table>
  <id>{3d2699c4-3159-4e8b-b48c-c2c4c9b5bd77}</id>
  <rows>
    <row>
      <columns>
        <column name="DESC" value="DACS" type="System.String" />
        <column name="ec_amount" value="5000" type="System.Decimal" />
        <column name="ec_exrate" value="1" type="System.Decimal" />
        <column name="ec_total" value="5000.00" type="System.Decimal" />
        <column name="ItemNo" value="PVT-C30" type="System.String" />
        <column name="UOM" value="EA" type="System.String" />
        <column name="DefaultKey" value="1" type="System.Int32" /><----THIS IS THE COLUMN ON WHICH I WOULD LIKE TO PIVOT
      </columns>
    </row>
    <row>
      <columns>
        <column name="DESC" value="DACS" type="System.String" />
        <column name="ec_amount" value="1500" type="System.Decimal" />
        <column name="ec_exrate" value="5" type="System.Decimal" />
        <column name="ec_total" value="7500.00" type="System.Decimal" />
        <column name="ItemNo" value="PVT-C30" type="System.String" />
        <column name="UOM" value="EA" type="System.String" />
        <column name="DefaultKey" value="2" type="System.Int32" />
      </columns>
    </row>
  </rows>
  <key>DefaultKey</key>
  <total>12500.00</total>
  <data />
  <parameters />
</table>

....并创建这些结果:

DefaultKey  DESC    ec_amount   ec_exrate   ec_total    ItemNo      UOM
1           DACS    5000        1           5000        PVT-C30     EA
2           DACS    1500        5           7500        PVT-C30     EA

我搜索了 Stack Overflow 网站,在我遇到的所有内容中,这两个帖子都很接近,但它们并没有完全让我到达那里:

SQL Pivot using an XML column

How do I Pivot on an XML column's attributes in T-SQL

如果这个问题已经得到解决,我很抱歉,我只是太早放弃了。

【问题讨论】:

  • 我认为您不需要在此处调整任何内容。您只需要获取值。
  • 问题在于,可以有任意数量的 DefaultKeys(我想在其上旋转的字段)。如果你能解释我如何才能“获得价值观”,我当然会试一试。
  • 别介意我之前的评论,看来只做一个 PIVOT 可能是最简单的。我将在答案中解释一种可能的方法,但基本上你需要一些东西来区分 XML 中的行。

标签: sql sql-server xml tsql


【解决方案1】:

无需旋转...试试这个

DECLARE @xml XML=N'<table>
  <id>{3d2699c4-3159-4e8b-b48c-c2c4c9b5bd77}</id>
  <rows>
    <row>
      <columns>
        <column name="DESC" value="DACS" type="System.String" />
        <column name="ec_amount" value="5000" type="System.Decimal" />
        <column name="ec_exrate" value="1" type="System.Decimal" />
        <column name="ec_total" value="5000.00" type="System.Decimal" />
        <column name="ItemNo" value="PVT-C30" type="System.String" />
        <column name="UOM" value="EA" type="System.String" />
        <column name="DefaultKey" value="1" type="System.Int32" />
      </columns>
    </row>
    <row>
      <columns>
        <column name="DESC" value="DACS" type="System.String" />
        <column name="ec_amount" value="1500" type="System.Decimal" />
        <column name="ec_exrate" value="5" type="System.Decimal" />
        <column name="ec_total" value="7500.00" type="System.Decimal" />
        <column name="ItemNo" value="PVT-C30" type="System.String" />
        <column name="UOM" value="EA" type="System.String" />
        <column name="DefaultKey" value="2" type="System.Int32" />
      </columns>
    </row>
  </rows>
  <key>DefaultKey</key>
  <total>12500.00</total>
  <data />
  <parameters />
</table>';

SELECT r.value(N'(columns/column[@name="DefaultKey"]/@value)[1]',N'int') AS DefaultKey
      ,r.value(N'(columns/column[@name="DESC"]/@value)[1]',N'nvarchar(max)') AS [DESC]
      ,r.value(N'(columns/column[@name="ec_amount"]/@value)[1]',N'decimal(10,4)') AS ec_amount
      --similar with your other elements
FROM @xml.nodes(N'/table/rows/row') AS A(r)

【讨论】:

    【解决方案2】:

    您基本上需要为 XML 的每个“行”分配一个行号(或找出区分每个“行”的其他方法)。您可以解决此问题的一种方法是从每个“行”中提取默认键(通过使用窗口函数和分区):

    SELECT *
    FROM
    (
        SELECT Name = C2.X.value('@name', 'varchar(max)'),
               Val = C2.X.value('@value', 'varchar(max)'),
               DefaultKey = MAX(CASE WHEN C2.X.value('@name', 'varchar(max)') = 'DefaultKey' THEN C2.X.value('@value', 'varchar(max)') END) OVER(PARTITION BY C1.X)
        FROM myTable
        CROSS APPLY myXMLColumn.nodes('table/rows/row/columns') AS C1(X)
        CROSS APPLY C1.X.nodes('column') AS C2(X)
    ) AS T
    PIVOT (MAX(Val) FOR Name IN ([DESC], [ec_amount], [ec_exrate], [ec_total], [ItemNo], [UOM])) AS P;
    

    如果 DefaultKey 不是唯一的,您可以使用行号或排名窗口函数来代替每个类似的结果:

    SELECT *
    FROM
    (
        SELECT Name = C2.X.value('@name', 'varchar(max)'),
               Val = C2.X.value('@value', 'varchar(max)'),
               RowInXML = DENSE_RANK() OVER (ORDER BY C1.X)
        FROM myTable
        CROSS APPLY myXMLColumn.nodes('table/rows/row/columns') AS C1(X)
        CROSS APPLY C1.X.nodes('column') AS C2(X)
    ) AS T
    PIVOT (MAX(Val) FOR Name IN ([DefaultKey], [DESC], [ec_amount], [ec_exrate], [ec_total], [ItemNo], [UOM])) AS P;
    

    【讨论】:

    • 谢谢ZLK。这很好用。还要感谢 Shnugo,但由于我已经有了 PIVOT 结构,所以我选择了 ZLK 的解决方案
    猜你喜欢
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    • 2018-06-28
    • 2012-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多