Comment «faire allusion» à la cardinalité d'un CTE récursif?

10

J'utilise le CTE récursif suivant comme exemple minimal, mais en général, l'optimiseur doit utiliser les cardinalités «devinées» par défaut pour les CTE récursifs:

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

Notez les cardinalités rows=31estimées et rows=5réelles dans le plan ci-dessus. Dans certains cas, 100 semble être utilisé comme estimation, je ne suis pas sûr de la logique exacte derrière les suppositions.

Dans mon problème réel, la mauvaise estimation de la cardinalité empêche le choix d'un plan rapide de «boucles imbriquées». Comment puis-je «suggérer» la cardinalité de l'optimiseur pour un CTE récursif pour contourner ce problème?

Jack dit d'essayer topanswers.xyz
la source
5
C'est l'un des nombreux cas où des conseils statistiques seraient vraiment agréables. Il y a des COSTfonctions, mais pas grand-chose d'autre. Je suggérerais de le soulever sur pgsql-hackers, mais vous vous retrouveriez juste pris dans la nième itération du débat sur les "indices", gaspillant des masses d'air chaud et n'obtenant rien :-(
Craig Ringer

Réponses:

8

J'ai travaillé autour de la question comme ça, mais j'espère qu'il y a une façon moins maladroite:

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 dit d'essayer topanswers.xyz
la source