2011-05-02 85 views
10

我有一個簡單的問題。不知何故,我無法找到明確的答案。PostgreSQL WITH RECURSIVE性能

在PostgreSQL中對WITH RECURSIVE語法進行了多少優化?我的意思是:它僅僅是一系列非遞歸查詢的語法糖,或者它更像是一個單一的語句,儘管其複雜的語義作爲一個整體進行了優化。一個後續問題 - 幾乎可以優化這種類型的語法?當然,有關這個問題的一些具體數據是最受歡迎的。

回答

4

我的經驗是它確實非常好的優化。

檢查出所產生查詢的執行計劃EXPLAIN分析和您將看到如何「昂貴」,它確實是(然後是如比較書面自遞歸函數)

+1

在語言層面擁有'WITH RECURSIVE'的全部原因是數據庫知道你正在嘗試做什麼,然後可以根據你的意圖採取行動。 – 2011-05-02 19:32:40

+0

@mu Yea,那就是我所認爲的。然而,即使在數據庫級別上,通過使用索引等來優化它也不是很明顯。一些操作通常很困難。遵循這個建議,我會盡快發佈我的發現。 – julkiewicz 2011-05-02 19:59:10

+0

@julkiewicz傳遞閉包表實際上是這樣的查詢的索引。我沒有理由找不到使用類似技術的索引類型。 – EricS 2013-09-10 08:31:48

16

我已經發現它優化到了一個點。

各個子查詢按預期重新使用並單獨優化,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; 

的另一個問題是,你不能用遞歸CTE的更新使用(對於大同小異的理由,其實)。

+0

鑑於這個答案的年齡,你是否知道你描述的行爲有任何近期的變化? – 2017-07-20 09:56:31

+0

@OliverSalzburg:我真的不知道,但通過比較答案中查詢的查詢計劃,應該很容易進行測試。 – 2017-07-20 10:16:25