这种递归SQL CTE如何正常工作?

时间:2016-04-03 22:08:07

标签: sql common-table-expression recursive-query

有人可以向我解释这个SQL查询是如何工作的吗?

WITH recursive n(n) AS (
  SELECT 2 n
  UNION ALL
  SELECT n+1 FROM n WHERE n<1000
)
SELECT a.n
FROM n a
  LEFT JOIN n b
  ON b.n < sqrt(a.n)
GROUP BY a.n
HAVING a.n=2 OR MIN(a.n % b.n) > 0;

这将在PostgresQL中生成以下内容:

 n
====
251
887
601
647
577
...
9
(177 rows)

我对逐行细分的理解:

SELECT 2 n - 选择数字2作为n [CTE的锚点成员]

UNION ALL - 与n&lt; 1000中的递归分量n + 1结合使用,因此显示2到1000之间的所有数字

SELECT a.n FROM n a - 运行上述查询[CTE的递归成员]

LEFT JOIN n b - 左边用数字2-1000连接第二组数字

ON b.n < sqrt(a.n) - 第二组数字是否小于第一列数字的平方根?

GROUP BY a.n - 仅显示第一列数字

HAVING a.n=2 OR MIN(a.n. % b.n) > 0 - ...其中A = 2或A模B的最小值是否大于0?

这是一个愚蠢的查询,但任何解密的帮助都将不胜感激。

1 个答案:

答案 0 :(得分:4)

您的查询经过适当修复后,会生成1000以下的素数列表:

WITH recursive n(n) AS (
  SELECT 2 n
  UNION ALL
  SELECT n+1 FROM n WHERE n<1000
)
SELECT a.n
FROM n a
  LEFT JOIN n b
  ON b.n <= sqrt(a.n)                        -- Fix #1
GROUP BY a.n
HAVING a.n=2 OR a.n=3 OR MIN(a.n % b.n) > 0  -- Fix #2
ORDER BY a.n ASC

Demo.

解释相当简单:查询的递归部分只是一种方法,可以为您提供从2(包括)到1000(不包括)的数字列表。您可以使用填充了连续整数的实际表替换recursive子句。

然后将这些数字输入查询的非CTE部分,并在b.n < sqrt(a.n)条件下加入自己。 a侧代表候选素数; b方代表候选除数。

以下是查询中的第一个错误:<必须更改为<=,否则素数正方形的平方根将包含在输出中。

GROUP BY将潜在素数与其候选除数组合成一个组。 HAVING子句会删除所有具有一个或多个候选除数的所有除数,均衡地划分候选素数,即MIN(a.n % b.n)为零。

这是您需要第二次修复的地方,因为素数3的平方根小于2,即列表中最小的候选除数。因此,3最终根本没有候选除数,并被HAVING子句抛出;您需要添加OR a.n=3才能保留它。