Bagaimana saya 'mengisyaratkan' kardinalitas CTE rekursif?

10

Saya menggunakan CTE rekursif berikut sebagai contoh minimal, tetapi secara umum, pengoptimal harus menggunakan kardinalitas 'tebakan' default untuk CTE rekursif:

with recursive w(n) as ( select 1 union all select n+1 from w where n<5 ) select * from w;
/*
 n
---
 1
 2
 3
 4
 5
*/

explain analyze
with recursive w(n) as ( select 1 union all select n+1 from w where n<5 ) select * from w;
/*
                                                    QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
 CTE Scan on w  (cost=2.95..3.57 rows=31 width=4) (actual time=0.005..0.020 rows=5 loops=1)
   CTE w
     ->  Recursive Union  (cost=0.00..2.95 rows=31 width=4) (actual time=0.003..0.017 rows=5 loops=1)
           ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
           ->  WorkTable Scan on w w_1  (cost=0.00..0.23 rows=3 width=4) (actual time=0.002..0.002 rows=1 loops=5)
                 Filter: (n < 5)
                 Rows Removed by Filter: 0
*/

Perhatikan rows=31perkiraan dan rows=5kardinalitas aktual dalam rencana di atas. Dalam beberapa kasus 100 tampaknya digunakan sebagai perkiraan, saya tidak yakin logika yang tepat di balik tebakan.

Dalam masalah dunia nyata saya, perkiraan kardinalitas yang buruk mencegah rencana 'loop bersarang' yang cepat untuk dipilih. Bagaimana saya bisa 'mengisyaratkan' kardinalitas pengoptimal untuk CTE rekursif untuk mengatasi ini?

Jack mengatakan coba topanswers.xyz
sumber
5
Ini adalah salah satu dari banyak kasus di mana petunjuk statistik akan sangat bagus untuk dimiliki. Ada COSTfungsi, tapi tidak banyak lagi. Saya sarankan meningkatkannya di pgsql-hacker, tetapi Anda hanya akan terjebak dalam iterasi ke-9 dari debat "petunjuk", membuang-buang udara panas dan tidak mencapai apa-apa :-(
Craig Ringer

Jawaban:

8

Saya telah mengatasi masalah seperti ini, tetapi saya berharap ada cara yang kurang menyenangkan:

explain analyze
with recursive w(n) as ( select 1 union all select n+1 from w where n<5 )
select * from w limit (select count(*) from w);
/*
                                                    QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
 Limit  (cost=3.66..3.72 rows=3 width=4) (actual time=0.032..0.034 rows=5 loops=1)
   CTE w
     ->  Recursive Union  (cost=0.00..2.95 rows=31 width=4) (actual time=0.003..0.019 rows=5 loops=1)
           ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.000..0.000 rows=1 loops=1)
           ->  WorkTable Scan on w w_1  (cost=0.00..0.23 rows=3 width=4) (actual time=0.002..0.002 rows=1 loops=5)
                 Filter: (n < 5)
                 Rows Removed by Filter: 0
   InitPlan 2 (returns $2)
     ->  Aggregate  (cost=0.70..0.71 rows=1 width=0) (actual time=0.029..0.030 rows=1 loops=1)
           ->  CTE Scan on w w_2  (cost=0.00..0.62 rows=31 width=0) (actual time=0.005..0.025 rows=5 loops=1)
   ->  CTE Scan on w  (cost=0.00..0.62 rows=31 width=4) (actual time=0.000..0.002 rows=5 loops=1)
*/
Jack mengatakan coba topanswers.xyz
sumber