在 SQL 中提取 XML 数据 - 过多的交叉应用语句

Posted

技术标签:

【中文标题】在 SQL 中提取 XML 数据 - 过多的交叉应用语句【英文标题】:Extracting XML data in SQL - too many cross apply statements 【发布时间】:2012-09-26 18:16:22 【问题描述】:

我有一个包含声明详细信息的 xml 文档:

<Statement>
<Id />

<Invoices>
    <Invoice>
        <Id />
        <Date />
        <AmountDue />
        etc.
    </Invoice>

    <Invoice>
        <Id />
        <Date />
        <AmountDue />
        etc.
    </Invoice>

    <Invoice>
        <Id />
        <Date />
        <AmountDue />
        etc.
    </Invoice>
</Invoices>

</Statement>

这适用于声明特定细节:

SET @statementId = @xml.value('(Id)[1]', 'UNIQUEIDENTIFIER');

但它需要一个单例,并且只返回第一个值。我需要发票的所有值,而不仅仅是第一个值,所以单例行不通。

我可以使用这样的交叉应用语句获取信息:

SELECT 
@statementId AS STATEMENT_ID
Id.value('.', 'uniqueidentifier') AS INVOICE_ID
Date.value('.', 'smalldatetime') AS INVOICE_DATE
Due.value('.', 'decimal') AS INVOICE_AMOUNT_DUE

FROM @xml.nodes('Statement') A(S)
cross apply S.nodes('Invoices/Invoice') B(InvoiceD)
cross apply InvoiceD.nodes('Id') C(Id)
cross apply InvoiceD.nodes('Date') D(Date)
cross apply InvoiceD.nodes('AmountDue') E(Due)

这将返回报表中每张发票的 ID、日期和金额 - 完美。

当我尝试提取所有发票详细信息时,我的问题就出现了。我目前有七个交叉应用语句,我收到以下消息:

“查询处理器用尽了内部资源,无法 生成查询计划。这是一个罕见的事件,只预计 极其复杂的查询或引用非常大的查询 表或分区的数量。请简化查询。如果你 相信您错误地收到了此消息,请联系客户 支持服务了解更多信息。”

我想要做的是对发票进行一次交叉申请并缩小选择语句中的确切字段,但除非我使用“。”我必须让语句返回一个单例,但我没有得到我需要的所有数据。

我已经对在 select 语句中指定命名空间进行了一些研究,但是所有示例都将命名空间设置为 http 地址,而不是 xml 文档中的节点,我还没有得到任何返回的东西接近。

我正在寻找的结果是这样的,但包含更多发票详细信息:

STATEMENT_ID      INVOICE_ID      INVOICE_DATE      INVOICE_AMOUNT_DUE     ...
Statement-1-Id    Invoice-1-Id    Invoice-1-Date    Invoice-1-AmountDue    ...
Statement-1-Id    Invoice-2-Id    Invoice-2-Date    Invoice-2-AmountDue    ...
Statement-1-Id    Invoice-3-Id    Invoice-3-Date    Invoice-3-AmountDue    ...

我应该从这里去哪里?

编辑:我删除了一些不必要的信息。获取所有发票特定的详细信息是我的目标。

【问题讨论】:

您希望结果是什么样的?付款和发票是 XML 中的兄弟,所以我很难弄清楚您希望如何在一个查询中返回它。 XML 中您拥有* 的部分有点令人困惑。数据是否存在于 XML 中带有值的单独元素中? 您不必一直对值元素进行交叉应用。您可以使用InvoiceD.value('ID[1]'. ....ID 指定为单例值。 感谢您提出这些问题 - 我已经用更好的信息编辑了我的帖子。 当我只需要一条记录时,我知道如何在 select 语句中指定路径,但是单身人士不会从所有发票中获取信息,而只是第一条。如果我误解了其中一些概念,请告诉我,我是新手。谢谢! 【参考方案1】:
select @XML.value('(Statement/Id/text())[1]', 'uniqueidentifier') as StatementId, 
       T.N.value('(Id/text())[1]', 'uniqueidentifier') as InvoiceId,
       T.N.value('(Date/text())[1]', 'smalldatetime') as InvoiceDate,
       T.N.value('(AmountDue/text())[1]', 'decimal') as AmountDue
from @XML.nodes('/Statement/Invoices/Invoice') as T(N)

.nodes 会将您的 XML 分解为行,以便每一行 T.N 指向它自己的 Invoice 节点。在该节点上只有一个 Id 节点,因此获取指定单例 Id[1] 的值是可行的。

您可以使用Id[1](Id/text())[1],但后者会为您提供更高效的执行计划。

【讨论】:

太棒了。非常感谢!是“/text()”让它全部返回吗? @JacobDanks 不,不是。为答案添加了更多解释。

以上是关于在 SQL 中提取 XML 数据 - 过多的交叉应用语句的主要内容,如果未能解决你的问题,请参考以下文章

SQL - 从源表中提取某些记录,但也交叉引用该表

动态 SQL 使用多列交叉应用来反透视数据

从 SQL 中的 XML 数据中提取属性详细信息

如何使用 PL/SQL 从 XML 文件中提取数据

从sql server中的xml中提取数据

从SQL Server的XML数据类型字段中提取数据