簡単な質問があります。どういうわけか、決定的な答えを見つけることができませんでした。
WITH RECURSIVE
PostgreSQL の構文はどの程度最適化されていますか? つまり、それは一連の非再帰クエリの構文糖衣にすぎないのでしょうか、それとも、複雑なセマンティクスにもかかわらず全体として最適化された単一のステートメントに近いのでしょうか。フォローアップの質問 - この種の構文を最適化することは、どの程度可能ですか? もちろん、この問題に関するいくつかの具体的なデータは大歓迎です。
簡単な質問があります。どういうわけか、決定的な答えを見つけることができませんでした。
WITH RECURSIVE
PostgreSQL の構文はどの程度最適化されていますか? つまり、それは一連の非再帰クエリの構文糖衣にすぎないのでしょうか、それとも、複雑なセマンティクスにもかかわらず全体として最適化された単一のステートメントに近いのでしょうか。フォローアップの質問 - この種の構文を最適化することは、どの程度可能ですか? もちろん、この問題に関するいくつかの具体的なデータは大歓迎です。
ある程度最適化されていることがわかりました。
さまざまなサブクエリが期待どおりに再利用され、個別に最適化されます。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;
もう 1 つの問題は、再帰的な CTE で FOR UPDATE を使用できないことです (実際、ほとんど同じ理由で)。
私の経験では、実際に非常に最適化されています。
EXPLAIN ANALYZE によって生成されたクエリの実行プランを確認すると、それが実際にどれほど「コストがかかる」かがわかります (そして、それをたとえば自己記述の再帰関数と比較します)。