【问题标题】:SQL Max Consecutive Values in a number set using recursion使用递归的数字集中的 SQL 最大连续值
【发布时间】:2021-03-09 17:33:51
【问题描述】:

以下 SQL 查询应该返回集合中的最大连续数字。

WITH RECURSIVE Mystery(X,Y) AS (SELECT A AS X, A AS Y FROM R)
UNION (SELECT m1.X, m2.Y 
    FROM Mystery m1, Mystery m2
    WHERE m2.X = m1.Y + 1)
SELECT MAX(Y-X) + 1 FROM Mystery;

对集合{7, 9, 10, 14, 15, 16, 18} 的此查询返回3,因为{14 15 16} 是最长的连续数字链,并且该链中有三个数字。但是,当我尝试手动完成此操作时,我看不到它是如何得出该结果的。

例如,给定上面设置的数字,我可以创建两列:

m1.x m2.y
7 7
9 9
10 10
14 14
15 15
16 16
18 18

如果我们正在处理行和列,而不是实际数据,据我所知WHERE m2.X = m1.Y + 1 从 Y 中的下一行获取值并将其放在 X 的当前行中,就像这样

m1.X m2.Y
9 7
10 9
14 10
15 14
16 15
18 16
18 Null?

我不确定的主要部分是 SQL 递归实际发生的位置。根据Denis Lukichev,递归是R 部分——或者在这种情况下是RECURSIVE Mystery(X,Y)——并在表为空时停止。但如果上述情况属实,那桌子怎么会空着呢?

由于我不知道如何进行上述操作,让我尝试不同的方向。如果WHERE m2.X = m1.Y + 1实际上是一个比较,结果应该是:

m1.X m2.Y
14 14
15 15
16 16

但在这一点上,它似乎应该继续递归,直到只剩下两行(没有其他可比较的)。如果它在这里停止以获得正确的 3 行计数 (2 + 1),那么实际上停止递归的是什么?

我了解到,对于上述示例,MAX(Y-X) + 1 有效地返回了实际的递归步骤数并加了 1。

但是,如果我有 7 个连续的数字并且递归向下流动到 2 行,那么结果不应该是不正确的 3 吗?我了解 C++ 和其他语言中的递归,但这让我感到困惑。

完全公开,是的,这似乎是一个常见的大学问题,但我退休了,在研究递归供我使用时发现了这一点,并且需要了解在我的项目中使用类似递归的工作原理。

【问题讨论】:

  • 我很好奇这里使用的 SQL 的确切版本。也许您可以将我们指向一个有效的 SQL Fiddle,然后我们可以解释结果。
  • 您使用的是哪个 dbms?这在 mysql、sqlserver 或 oracle 中不起作用。
  • 我使用 Postgres 为此创建了一个 similar DB Fiddle。这是否足够相似以至于您想要解释结果? dbfiddle.uk/…
  • 我所见过的示例都没有指定哪个 dbm。我也无法让它在 MS SQL 或 MySQL 中工作。
  • 如果没有人先到,我明天去看看

标签: sql recursion


【解决方案1】:

基于之前共享的db<>fiddle,您可能会发现更改 CTE 以包含如下迭代编号,然后显示 CTE 的内容而不是最终 SELECT 的输出是有益的。这是递归完成后修改后的 CTE 及其内容:

修改后的 CTE

WITH RECURSIVE Mystery(X,Y) AS ((SELECT A AS X, A AS Y, 1 as Z FROM R)
UNION (SELECT m1.X, m2.A, Z+1 
    FROM Mystery m1
    JOIN R m2 ON  m2.A = m1.Y + 1))

CTE 内容

x y z
7 7 1
9 9 1
10 10 1
14 14 1
15 15 1
16 16 1
18 18 1
9 10 2
14 15 2
15 16 2
14 16 3

Z 字段保存迭代计数。在Z = 1 处,我们只是从表R 中获取了行。值X 和Y 都来自字段A。就我们试图实现的这些表示序列而言,连续数字从X 开始并继续(至少)Y。

其中Z = 2,第二次迭代,我们找到所有行第一次迭代,其中R 中的值比我们的Y 值高一个,或者比我们序列的最后一个成员高一个连续的数字。这成为新的最高数字,我们将迭代次数加一。由于我们的原始数据集中只有三个数字在集合内有后继,因此第二次迭代中只有三行输出。

其中Z = 3,第三次迭代,我们找到第二次迭代的所有行(注意我们不再考虑第一次迭代的所有行),再次在R 中有一个值比我们的Y 值高一个,或者比我们连续数字序列的最后一个成员高一个。这再次成为新的最高数字,我们将迭代次数加一。

该过程将尝试第四次迭代,但由于 R 中没有值比第三次迭代中的 Y 值大一的行,因此不会将额外数据添加到 CTE 并且递归结束。

回到原来的 dbfiddle,该过程然后搜索我们的 CTE 内容以输出 MAX(Y-X) + 1,这是任何连续序列中第一个值和最后一个值之间的最大差值加一。这会从第三次迭代中产生的记录中找到它的值,使用值为3 的((16-14) + 1)。

对于这段特定的代码,输出始终等于 Z 字段中的值,因为通过递归每增加一行,Z 加一,Y 加一。

【讨论】:

  • 我做了根管治疗,所以稍后我得仔细看看,但乍一看,我认为这可以解决问题。谢谢!
  • 没有意识到我没有跟进。道歉。我相信你的解决方案帮助我理解了这个过程,所以再次感谢你。希望我的问题足够清楚,也可以帮助其他人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
  • 2010-09-17
  • 2013-11-04
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
相关资源
最近更新 更多