【问题标题】:How can I combine CTEs with a FOR XML clause?如何将 CTE 与 FOR XML 子句结合使用?
【发布时间】:2011-06-06 15:07:24
【问题描述】:

我正在尝试生成一些具有各种嵌套级别的 XML,并且冒着过度简化的风险,输出 XML 的格式将是松散的:

<invoice number="1">
 <charge code="foo" rate="123.00">
  <surcharge amount="10%" />
 </charge>
 <charge code="bar" />
</invoice>

我为此继承的数据库架构恰好将费用存储在不同的表中,这意味着附加费的存储方式根据费用来源的表而不同。

鉴于您 cannot use UNIONs with FOR XML,我在 CTE 中做了一些 UNIONing,所以大致如下:

WITH Charges ( [@code], [@rate], surcharge, InvoiceId ) AS (
    SELECT code AS [@Code], amount AS [@rate], NULL as surcharge, InvoiceId
    FROM item.charges
UNION ALL
    SELECT 
        code AS [@Code], 
        amount AS [@rate],
        (
             SELECT amount AS [@amount]
             FROM order.surcharges os
             WHERE oc.ChargeId = os.ChargeId
             FOR XML PATH('surcharge'), TYPE
        ), 
        InvoiceId
    FROM order.charges oc
)
SELECT
    Number AS [@number],
    (
         SELECT
             [@code],
             [@rate],
             surcharge
         FROM Charges
         WHERE Charges.InvoiceId = i.InvoiceId
    )
FROM Invoices i
FOR XML PATH( 'invoice' ), TYPE

现在,难以置信接近了,给出(注意嵌套的&lt;surcharge&gt;):

<invoice number="1">
 <charge code="foo" rate="123.00">
  <surcharge>
   <surcharge amount="10%" />
  </surcharge>
 </charge>
 <charge code="bar" />
</invoice>

但是我需要找到一种方法来让最终查询包含要被视为元素内容而不是新元素的 XML 列的值。这可能吗,还是我需要采取新的方法?

【问题讨论】:

    标签: tsql sql-server-2008 common-table-expression for-xml


    【解决方案1】:

    我认为您可以通过在“for xml path('surcharge')”语句中省略根节点类型来做到这一点。也就是说,改用“for xml path('')”。

    【讨论】:

    • 这给出了一个错误:“行标记省略(空行标记名称)不能与以属性为中心的 FOR XML 序列化一起使用。”
    • +1 附加费由联合顶部的NULL as surcharge 隐式添加,因此 path('surgcharge') 看起来确实重复
    • 我对那个错误感到困惑。如果您按照我建议的方式使用“for xml”子句单独在联合中运行第二个选择会发生什么?
    【解决方案2】:

    您有一个返回多行(@charge、@rate 和 XML 类型)的列查询。我希望您发布的查询会给出错误:

    只能指定一个表达式 在子查询时的选择列表中 不与 EXISTS 一起引入。

    但是,通过将查询移至 outer apply 可以轻松解决此问题。要删除双 surcharge 元素,您可以将 XML 列名尽可能移到底部,例如:

    ;WITH Charges (code, rate, surcharge, InvoiceId) AS 
        (
        SELECT  code, amount, NULL, InvoiceId
        FROM    @charges
        UNION ALL
        SELECT  code
        ,       amount
        ,       (
                SELECT  amount AS [@amount]
                FROM    @surcharges os
                WHERE   oc.ChargeId = os.ChargeId
                FOR XML PATH('surcharge'), TYPE
                )
        ,       InvoiceId
        FROM    @charges oc
    )
    SELECT  Number AS [@number]
    ,       c.code as [charge/@code]
    ,       c.rate as [charge/@rate]
    ,       c.surcharge as [charge]
    FROM    @Invoices i
    outer apply
            (
            SELECT  code
            ,       rate
            ,       surcharge
            FROM    Charges
            WHERE   Charges.InvoiceId = i.InvoiceId
            ) c
    WHERE   i.InvoiceID = 1
    FOR XML PATH( 'invoice' ), TYPE
    

    这将打印,例如:

    <invoice number="1">
      <charge code="1" rate="1" />
    </invoice>
    <invoice number="1">
      <charge code="1" rate="1">
        <surcharge amount="1" />
      </charge>
    </invoice>
    

    第一个元素来自联合体的顶部,surcharge = null

    【讨论】:

      【解决方案3】:

      似乎将(假)列命名为“*”将使用该列的内容作为元素的内容,因此如下更改 SQL 使其工作:

      WITH Charges ( [@code], [@rate], surcharge, InvoiceId ) AS (
          SELECT code AS [@Code], amount AS [@rate], NULL as surcharge, InvoiceId
          FROM item.charges
      UNION ALL
          SELECT 
              code AS [@Code], 
              amount AS [@rate],
              (
                   SELECT amount AS [@amount]
                   FROM order.surcharges os
                   WHERE oc.ChargeId = os.ChargeId
                   FOR XML PATH('surcharge'), TYPE
              ), 
              InvoiceId
          FROM order.charges oc
      )
      SELECT
          Number AS [@number],
          (
               SELECT
                   [@code],
                   [@rate],
                   surcharge AS [*] -- Thsi will embed the contents of the previously generated XML in here.
               FROM Charges
               WHERE Charges.InvoiceId = i.InvoiceId
          )
      FROM Invoices i
      FOR XML PATH( 'invoice' ), TYPE
      

      【讨论】:

      • 当不使用EXISTS引入子查询时,选择列表中只能指定一个表达式。
      • @user3752281 该解决方案对我有用,所以我不明白您的评论?
      猜你喜欢
      • 2018-06-16
      • 1970-01-01
      • 1970-01-01
      • 2021-09-18
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多