Wie kann ich die Kardinalität eines rekursiven CTE andeuten?


10

Ich verwende den folgenden rekursiven CTE als minimales Beispiel, aber im Allgemeinen muss der Optimierer standardmäßige "erratene" Kardinalitäten für rekursive CTEs verwenden:

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

Beachten Sie die rows=31geschätzten und rows=5tatsächlichen Kardinalitäten im obigen Plan. In einigen Fällen scheint 100 als Schätzung verwendet zu werden, ich bin mir nicht sicher, welche Logik hinter den Vermutungen steckt.

In meinem realen Problem verhindert die schlechte Kardinalitätsschätzung, dass ein schneller Plan für verschachtelte Schleifen ausgewählt wird. Wie kann ich die Kardinalität des Optimierers für einen rekursiven CTE andeuten, um dies zu umgehen?


5
Dies ist einer der vielen Fälle, in denen statistische Hinweise sehr schön wären. Es gibt COSTFunktionen, aber sonst nicht viel. Ich würde vorschlagen, es auf pgsql-Hacker zu bringen, aber Sie würden nur in die n-te Wiederholung der "Hinweise" -Debatte verwickelt sein, eine Menge heißer Luft verschwenden und nichts erreichen :-(
Craig Ringer

Antworten:


8

Ich habe das Problem so umgangen, aber ich hoffe, dass es einen weniger klobigen Weg gibt:

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)
*/
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.