【问题标题】:Can I have SQL incrementally count XML elements while parsing?我可以在解析时让 SQL 递增地计算 XML 元素吗?
【发布时间】:2021-08-17 18:26:57
【问题描述】:

所以这是我第一次尝试解析 XML,我正在尝试弄清楚如何让它按我想要的方式工作。

给定以下 XML 格式:

<Tiles>
   <TileRow>
      <TileValue>2</TileValue>
      <TileValue>3</TileValue>
      <TileValue>4</TileValue>
   </TileRow>
   <TileRow>
      <TileValue>2</TileValue>
      <TileValue>7</TileValue>
   </TileRow>
</Tiles>

我希望它把它放在一个 SQL 表中,如下所示:

| X | Y | Val |
|---|---|-----|
| 1 | 1 |  2  |
| 1 | 2 |  3  |
| 1 | 3 |  4  |
| 2 | 1 |  2  |
| 2 | 2 |  7  |

基本上,想象一个网格,每个“TileRow”在该网格中开始一个新行。每个“TileValue”分配该网格中的列位置,实际的 TileValue 是该网格中“单元格”中的内容。

有没有办法让 SQL 每次通过一个元素时都“计数”,或者类似的东西?

【问题讨论】:

  • 在提出问题时,您需要提供一个可重现的最小示例。请参考以下链接:stackoverflow.com/help/minimal-reproducible-example 请提供以下内容: (1) DDL 和样本数据填充,即 CREATE table(s) 加上 INSERT T-SQL 语句。 (2) 你需要做什么,即逻辑和你的代码尝试在 T-SQL 中实现它。 (3) 期望的输出,基于上面#1 中的样本数据。 (4) 你的 SQL Server 版本 (SELECT @@version;)

标签: sql sql-server xml xml-parsing


【解决方案1】:

请尝试以下解决方案。

它基于 XQuery 中的 节点顺序比较 运算符。

Node Order Comparison Operators

SQL

DECLARE @xml XML =
N'<Tiles>
   <TileRow>
      <TileValue>2</TileValue>
      <TileValue>3</TileValue>
      <TileValue>4</TileValue>
   </TileRow>
   <TileRow>
      <TileValue>2</TileValue>
      <TileValue>7</TileValue>
   </TileRow>
</Tiles>';

SELECT c.value('for $i in . return count(/Tiles/TileRow[. << $i])', 'INT') AS [X]
    , c.value('for $i in . return count(../*[. << $i]) + 1', 'INT') AS [Y]
    , c.value('(./text())[1]', 'INT') as Value
FROM @xml.nodes('/Tiles/TileRow/TileValue') AS t(c);

输出

+---+---+-------+
| X | Y | Value |
+---+---+-------+
| 1 | 1 |     2 |
| 1 | 2 |     3 |
| 1 | 3 |     4 |
| 2 | 1 |     2 |
| 2 | 2 |     7 |
+---+---+-------+

【讨论】:

  • 哇,这真是令人印象深刻的 XQuery-fu。但我认为X 更好地计算为...... return count(parent::TileRow[. &lt;&lt; $i])
  • @Charlieface,请对建议进行投票:feedback.azure.com/forums/908035-sql-server/suggestions/…
  • 很奇怪,当我复制粘贴您的代码时,它可以完美运行。但是,当我稍微调整命名时,它停止工作。唯一的区别是一些名称(我仔细检查过),而且我的 XML 没有正确地标记。后者会是某种原因吗?
  • @user3813250 请发布您的 real XML 和预期输出,以便我们查看
  • 是的,就是这样,区分大小写。非常感谢!
【解决方案2】:

不幸的是,SQL Server 不支持直接返回position(),它只允许在谓词中返回。如果是这样,那么您可以简单地查询:

select
    v.TileValue.value('position(parent::*)[1]','int'),
    v.TileValue.value('./position()[1]','int'),
    v.TileValue.value('text()[1]','int')
from @x.nodes('/Tiles/TileRow/TileValue') v(TileValue)

相反,您可以使用ROW_NUMBER() 模拟它

select
    v1.rn,
    row_number() over (partition by v1.rn order by (select 1)),
    v2.TileValue.value('text()[1]','int')
from (
    select v1.TileRow.query('.') TileRow,
        row_number() over (order by (select 1)) rn
    from @x.nodes('/Tiles/TileRow') v1(TileRow)
) v1
cross apply v1.TileRow.nodes('TileRow/TileValue') v2(TileValue)

【讨论】:

    猜你喜欢
    • 2016-02-19
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多