Como 'sugerir' a cardinalidade de uma CTE recursiva?

10

Estou usando a seguinte CTE recursiva como um exemplo mínimo, mas, em geral, o otimizador precisa usar cardinalidades 'adivinhadas' padrão para CTEs recursivas:

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
*/

Observe as cardinalidades rows=31estimadas e rows=5reais no plano acima. Em alguns casos, 100 parece ser usado como uma estimativa, não tenho certeza da lógica exata por trás das suposições.

No meu problema do mundo real, a fraca estimativa de cardinalidade está impedindo a escolha de um plano rápido de 'loops aninhados'. Como posso 'sugerir' a cardinalidade do otimizador para uma CTE recursiva contornar isso?

Jack diz que tenta topanswers.xyz
fonte
5
Este é um dos muitos casos em que seria bom ter dicas de estatísticas. Há COSTfunções, mas não muito mais. Eu sugiro aumentá-lo para pgsql-hackers, mas você seria pego na n-ésima iteração do debate "dicas", desperdiçando grandes quantidades de ar quente e sem conseguir nada :-(
Craig Ringer

Respostas:

8

Eu trabalhei com a questão dessa maneira, mas espero que exista uma maneira menos complicada:

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 diz que tenta topanswers.xyz
fonte