关于 WITH RECURSIVE Query Postgres 的可能解释

Possible explanation on WITH RECURSIVE Query Postgres

我一直在阅读 Postgres 中的 With Query。这就是我感到惊讶的地方

WITH RECURSIVE t(n) AS (
    VALUES (1)
  UNION ALL
    SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;

我无法理解查询的评估是如何工作的。

关于 SQL 中的递归语句如何发生故障的任何见解。

这称为通用 table 表达式,是 SQL 中表达递归查询的一种方式:

t(n) 将 CTE 的名称定义为 t,其中包含一个名为 n 的列。它类似于派生 table:

的别名
select ... 
from (
  ...
) <b><i>as t(n)</i></b>;

递归从值 1(即 values (1) 部分)开始,然后递归地对其加一,直到达到 99。所以它生成从 1 到 99 的数字。然后最终查询然后总结所有这些数字。

n 是列名,不是,"variable" 并且 "assignment" 的发生方式与任何数据检索相同。

WITH RECURSIVE t(n) AS (
    VALUES (1) --<b><< this is the recursion "root"</b>
  UNION ALL
    SELECT n+1 FROM t WHERE n < 100 --<b><< this is the "recursive part"</b>
)
SELECT sum(n) FROM t;

如果你 "unroll" 递归(实际上是迭代)那么你会得到这样的结果:

select x.n + 1
from (
  select x.n + 1
  from (
    select x.n + 1
    from (
      select x.n + 1
      from (
         values (1)
      ) as x(n) 
    ) as x(n)
  ) as x(n)
) as x(n)

手册中的更多详细信息:
https://www.postgresql.org/docs/current/static/queries-with.html

如果您正在寻找它的计算方式,递归分两个阶段进行。

  1. 根执行一次。
  2. 执行递归部分,直到没有返回行。文档在这一点上有点含糊。

现在,通常在数据库中,我们考虑 "function" 的方式与我们在进行命令式编程时考虑的方式不同。在数据库方面,思考函数的最佳方式是 "a correspondence where for every domain value you have exactly one corresponding value." 因此,直接的挑战之一是停止从编程函数的角度思考。即使是用户定义的函数也最好以这种方式考虑,因为它避免了很多关于 运行 查询和查询计划器的交集的潜在麻烦......所以它可能看起来像一个函数,但事实并非如此正确。

相反,WITH 子句使用不同的、几乎相反的表示法。这里有集合名称 t,后跟(在本例中为可选)元组结构 (n)。所以这不是一个有参数的函数,而是一个结构体的关系。

那么这是如何分解的:

SELECT 1 as n where n < 100
UNION ALL
SELECT n + 1 FROM (SELECT 1 as n) where n < 100
UNION ALL
SELECT n + 1 FROM (SELECT n + 1 FROM (SELECT 1 as n)) where n < 100

当然这是一种简化,因为在内部我们会跟踪 cte 状态并继续加入最后一次迭代,所以在实践中这些会折回到接近线性的复杂度(虽然上图表明性能比那个)。

所以实际上你得到的更像:

 SELECT 1 as n where 1 < 100
 UNION ALL
 SELECT 1 + 1 as n where 1 + 1 < 100
 UNION ALL
 SELECT 2 + 1 AS n WHERE 2 + 1 < 100
 ...

本质上,以前的值会沿用。