【问题标题】:How to retrieve data from XML column in SQL如何从 SQL 中的 XML 列中检索数据
【发布时间】:2016-08-16 03:12:00
【问题描述】:

我有以下表格布局:

订单详情

ItemID (PK, int, not null)
ItemName (nvarchar(450), null)
OrderID (FK, int, not null)
Discounts (nvarchar(max), null)

Discounts 列被声明为 nvarchar(max) 但数据实际上是 XML - 不知道为什么它没有被声明为 XML。我需要查询表以显示OrderIDItemName、按比例分配的折扣、所有其他折扣以及按比例分配的总和+所有其他折扣。这是一些记录的示例。

ItemID  ItemName  OrderID     Discounts
8610    Item 1    4227        SEE XML 4227 BELOW
8615    Item 2    4227        <DocumentElement></DocumentElement> //no discounts for this row
8620    Item 3    9387        SEE XML 9387 BELOW

XML OrderId = 4227:

<DocumentElement>
  <DiscountsTable>
        <DiscountDisplayName>Bundle A</DiscountDisplayName>
        <DiscountValue>6.00</DiscountValue>
  </DiscountsTable>
  <DiscountsTable>
        <DiscountDisplayName>Bundle B</DiscountDisplayName>
        <DiscountValue>25.00</DiscountValue>
  </DiscountsTable>
</DocumentElement>

OrderId = 9387 的 XML:

<DocumentElement>
  <DiscountsTable>
        <DiscountDisplayName>Prorated Discount</DiscountDisplayName>
        <DiscountValue>6.45</DiscountValue>
  </DiscountsTable>
  <DiscountsTable>
        <DiscountDisplayName>Bundle A</DiscountDisplayName>
        <DiscountValue>5.61</DiscountValue>
  </DiscountsTable>
  <DiscountsTable>
        <DiscountDisplayName>Bundle B</DiscountDisplayName>
        <DiscountValue>23.39</DiscountValue>
  </DiscountsTable>
</DocumentElement>

所以,我需要的是一个查询,它将返回 ItemID、ItemName、汇总的按比例分配的折扣、汇总的捆绑折扣以及加在一起的总折扣(按比例分配 + 捆绑 = 总折扣)。对于上面的 3 条记录,查询结果应该是这样的:

Item ID  Item Name Prorated Discounts    Other Discounts       Total Discounts
8610     Item 1    0.00                  31.00                 31.00
8615     Item 2    0.00                  0.00                  0.00
8620     Item 3    6.45                  29.00                 35.45

我已尝试使用 value() 方法,但我收到一条错误消息,指出它不能与 nvarchar(max) 一起使用。如何解析 XML 列以提取聚合值?

【问题讨论】:

    标签: sql sql-server xml


    【解决方案1】:

    您需要先将列转换为 XML 才能使用 SQLXML 功能。像这样的。

    ;with tbl as (
    select ItemID, ItemName, OrderID,
    convert(xml, Discounts) as Discounts
    from OrderDetails
    )
    select ItemID, ItemName, OrderID,
    t.v.value('DiscountDisplayName[1]','varchar(100)') DiscountDisplayName,
    t.v.value('DiscountValue[1]','float') DiscountValue
    from tbl cross apply Discounts.nodes('DocumentElement/DiscountsTable') t(v)
    

    稍微阐述一下

    ;with tbl as (
    select ItemID, ItemName, OrderID,
    convert(xml, Discounts) as Discounts
    from OrderDetails
    ),
    tbl1 as (
    select ItemID, ItemName, OrderID,
    t.v.value('(DiscountsTable[DiscountDisplayName="Prorated Discount"]/DiscountValue)[1]','float') Prorated, -- filter
    t.v.value('(DiscountsTable[DiscountDisplayName="Bundle A"]/DiscountValue)[1]','float') BundleA,
    t.v.value('(DiscountsTable[DiscountDisplayName="Bundle B"]/DiscountValue)[1]','float') BundleB
    from tbl cross apply Discounts.nodes('DocumentElement') t(v) --do not go deeper
    )
    select ItemID, ItemName, OrderID, isnull(Prorated, 0) Prorated,
    isnull(BundleA, 0) + isnull(BundleB, 0) Other,
    isnull(Prorated, 0) + isnull(BundleA, 0) + isnull(BundleB, 0) Total
    from tbl1
    

    【讨论】:

      【解决方案2】:

      我会这样解决:

      WITH Conv AS
      (
          SELECT ItemID,ItemName,OrderID,CONVERT(xml,Discounts) XmlVal
          FROM Src
      )
      SELECT ItemID, ItemName, OrderID, Prorated, Other, Prorated+Other Total
      FROM Conv
      CROSS APPLY
      (
          SELECT SUM(CASE X.exist('DiscountDisplayName[text() = "Prorated Discount"]')
                     WHEN 1 THEN X.value('DiscountValue[1]', 'decimal(15,2)')
                     ELSE 0 END) Prorated,
                 SUM(CASE X.exist('DiscountDisplayName[text() = "Prorated Discount"]')
                     WHEN 0 THEN X.value('DiscountValue[1]', 'decimal(15,2)')
                     ELSE 0 END) Other
           FROM XmlVal.nodes('/DocumentElement/DiscountsTable') T(X)
      ) T(Prorated, Other)
      

      结果:

      ItemID      ItemName OrderID     Prorated     Other    Total
      ----------- -------- ----------- ------------ -------- ---------
      8610        Item 1   4227        0.00         31.00    31.00
      8615        Item 2   4227        NULL         NULL     NULL
      8620        Item 3   9387        6.45         29.00    35.45
      

      您可以添加COALESCEISNULL 来删除NULLs。

      【讨论】:

      • 感谢您的帮助。你的回答确实得到了我需要的数据。但是,我仍然不确定如何将它与我现有的查询集成。有什么方法可以直接给你留言解释吗?我现有的查询太长,无法在评论部分发布。
      • 或者有没有办法将您的答案中的结果集存储到我可以与现有查询一起加入的变量中?我必须在您的答案中添加一个 ID,但我已经这样做了。所以,我真的需要知道如何将您的查询结果与我现有的查询结合起来。
      • 你可以使用SELECT Columns INTO newTable FROM ...语法。
      • 搞定了。太感谢了。我已经处理这个查询 2 天了。
      猜你喜欢
      • 2013-08-03
      • 1970-01-01
      • 2014-05-21
      • 1970-01-01
      • 2012-04-18
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 2016-03-17
      相关资源
      最近更新 更多