¿Cómo puedo 'insinuar' la cardinalidad de un CTE recursivo?

10

Estoy usando el siguiente CTE recursivo como un ejemplo mínimo, pero en general, el optimizador tiene que usar cardinalidades 'adivinadas' predeterminadas para los CTE recursivos:

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

Tenga en cuenta las cardinalidades rows=31estimadas y rows=5reales en el plan anterior. En algunos casos, 100 parece usarse como una estimación, no estoy seguro de la lógica exacta detrás de las conjeturas.

En mi problema del mundo real, la pobre estimación de la cardinalidad impide que se elija un plan rápido de 'bucles anidados'. ¿Cómo puedo 'insinuar' la cardinalidad del optimizador para que un CTE recursivo evite esto?

Jack dice que intente topanswers.xyz
fuente
55
Este es uno de los muchos casos en los que sería muy bueno tener pistas sobre estadísticas. Hay COSTfunciones, pero no mucho más. Sugeriría plantearlo en pgsql-hackers, pero te quedarías atrapado en la enésima iteración del debate de "pistas", desperdiciando masas de aire caliente y sin lograr nada :-(
Craig Ringer

Respuestas:

8

He trabajado sobre el tema de esta manera, pero espero que haya una manera menos torpe:

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 dice que intente topanswers.xyz
fuente