递归 CTE 中的自联接:`递归引用不能出现多次`?

tin*_*lyx 6 postgresql recursive postgresql-9.6 self-join

这是用 PostgreSQL 9.6 测试过的,但这是一个通用的 SQL 问题。

是否可以在递归 CTE (rCTE) 中使用递归表的自联接?

我尝试了以下包含递归项自联接的 rCTE x

WITH RECURSIVE
x (id) AS (
  SELECT 1 id UNION ALL SELECT x1.id+x2.id FROM x x1, x x2
  WHERE x1.id < 5 AND x2.id < 5 AND x1.id = x2.id
  )
SELECT * FROM x;
Run Code Online (Sandbox Code Playgroud)

,希望应该等同于:

WITH RECURSIVE
x (id) AS (
  SELECT 1 id UNION ALL SELECT id+id FROM x WHERE id < 5
  )
SELECT * FROM x;
Run Code Online (Sandbox Code Playgroud)

但是之前的 rCTE 会产生一个错误:

ERROR:  recursive reference to query "x" must not appear more than once 
LINE 3:   SELECT 1 id UNION ALL SELECT x1.id+x2.id FROM x x1, x x2
Run Code Online (Sandbox Code Playgroud)

递归引用不能多次出现是否有根本原因?或者这只是 PostgreSQL 实现的一个限制?

另外,有解决办法吗?

小智 8

事实上,错误就像recursive reference to query "x" must not appear more than once是在 postgres 中应用的一些奇怪的限制。我假设这是因为他们的解析器只是通过该表的存在来简单区分查询的递归和非递归部分。同时,对于目前的好解决方法 - 您可以使用嵌套CTEWITH语句),并为此类表指定另一个名称。对于您的初始示例,它将如下所示:

WITH RECURSIVE
x (id) AS (
  SELECT 1 id
  UNION ALL
  SELECT * FROM (
    WITH x_inner AS ( -- Workaround of error: recursive reference to query "x" must not appear more than once
      SELECT * FROM x
    )
    SELECT x1.id+x2.id
    FROM x_inner x1, x_inner x2
    WHERE x1.id < 5 AND x2.id < 5 AND x1.id = x2.id
  )t
)
SELECT * FROM x;
Run Code Online (Sandbox Code Playgroud)

您可以在SQL fiddle 中尝试。

  • 这是伏都教。 (2认同)

ind*_*iri 1

对于第一个问题:通过将 x 连接到 x,您将创建该对象的两个单独的实例。当 Postgres 尝试递归时,它不知道要遵循哪条路径(实例)。

本质上,您的第二个查询就是解决方法。虽然我假设你有一个更复杂的情况,但这是一个简化的情况?

  • 另外,这是一个关于递归可以做什么和不能做什么的有趣练习:https://sourceforge.net/p/postgres-xl/postgres-xl/ci/56008fbd7d08d3e8719ca2c91013507fdd011d3f/tree/src/test/regress/expected/with 。出去 (2认同)