16

我有一个简单的问题。不知何故,我无法找到明确的答案。

WITH RECURSIVEPostgreSQL中的语法优化了多少?我的意思是:它仅仅是一系列非递归查询的语法糖,还是它更像是一个语句,尽管其复杂的语义已经作为一个整体进行了优化。一个后续问题 - 优化这种语法有多少可能?当然,欢迎提供有关此事的具体数据。

4

2 回答 2

19

我发现它优化到了一定程度。

各种子查询按预期重复使用并单独优化,Postgres 优化后者就像任何其他查询一样。

我对它的主要抱怨与它不会在可能的情况下向 CTE 注入约束有关。

例如:

with recursive
parents as (
select node.id,
       node.parent_id
from nodes as node
union all
select node.id,
       parent.parent_id
from parents as node
join nodes as parent on parent.id = node.parent_id
)
select parent_id
from parents
where id = 2;

在上面,Postgres 会理想地理解(因为 node.id 按原样返回它可以做到:

with recursive
parents as (
select node.id,
       node.parent_id
from nodes as node
where id = 2
union all
select node.id,
       parent.parent_id
from parents as node
join nodes as parent on parent.id = node.parent_id
)
select parent_id
from parents;

...并在主键上使用索引扫描。在实践中,它实际上会在 CTE 告诉它的时候执行:递归地提取所有行的所有父项,如果需要,将结果集放在一个未命名的临时表中,然后检查结果集中的每一行是否 id = 2.

换句话说,CTE 不会跟踪它返回的“原始”表/行/列集。在这得到适当优化之前,在递归查询上创建视图充其量是疯狂的。

同时,一个好的解决方法是创建一个 sql 函数:

create function parents(id int) as returns table (id int) $$
    with recursive
    parents as (
    select node.id,
           node.parent_id
    from nodes as node
    where id = $1
    union all
    select node.id,
           parent.parent_id
    from parents as node
    join nodes as parent on parent.id = node.parent_id
    )
    select parent_id
    from parents;
$$ language sql stable strict rows 5 cost 1;

另一个问题是您不能将 FOR UPDATE 与递归 CTE 一起使用(事实上,原因几乎相同)。

于 2011-05-08T14:46:56.750 回答
4

我的经验是它确实优化得很好。

查看由 EXPLAIN ANALYZE 生成的查询的执行计划,会看到它实际上是多么“昂贵”(然后将其与自写的递归函数进行比较)

于 2011-05-02T19:24:17.993 回答