2013-02-20 9 views
23

Perché l'operatore "IN" è lento quando utilizzato con la sottoquery?Operatore PostgreSQL IN con prestazioni insufficienti delle subquery

select * 
from view1 
where id in (1,2,3,4,5,6,7,8,9,10) 
order by somedata; 

eseguito in 9 ms.

select * 
from view1 
where id in (select ext_id 
      from aggregate_table 
      order by somedata limit 10) 
order by somedata; 

esegue in 25000ms e sembra utilizzare la scansione sequenziale sulla vista (view1) invece di scansione indice chiavi primarie restituiti dalla subquery come fa in prima interrogazione.

La sottoquery select ext_id from aggregate_table order by somedata limit 10 esegue in 0,1 ms

così la lentezza della seconda query è causato da scansione sequenziale su view1 che è una vista contenente tre UNIONS e circa tre join in ogni UNION. La prima UNION contiene circa 1 milione di righe, altre molto meno. Si unisce a tabelle con alcune righe da 100K. Non è così rilevante, però, volevo solo capire il comportamento dell'operatore IN.

Quello che sto cercando di ottenere è di prendere il risultato della subquery (un set di chiavi primarie) e selezionare i dati da una vista complessa (view1) usando solo loro.

anche io non posso usare

select v1.* 
from view1 v1, 
    aggregate_table at 
where v1.id = at.ext_id 
order by at.somedata 
limit 10 

perché non voglio ordinare la grande join da somedata. Voglio solo selezionare 10 risultati dalla vista con le chiavi primarie e quindi ordinare solo questi.

La domanda è: perché l'operatore IN esegue rapidamente quando elencho esplicitamente queste chiavi e così lento quando utilizzo una subquery veloce che restituisce lo stesso set di chiavi?

EXPLAIN ANALYZE come richiesto

prima interrogazione - select * from view1 where id in (1,2,3,4,5,6,7,8,9,10) order by somedata;

Sort (cost=348.480..348.550 rows=30 width=943) (actual time=14.385..14.399 rows=10 loops=1) 
    Sort Key: "india".three 
    Sort Method: quicksort Memory: 30kB 
    -> Append (cost=47.650..347.440 rows=30 width=334) (actual time=11.528..14.275 rows=10 loops=1) 
     -> Subquery Scan "*SELECT* 1" (cost=47.650..172.110 rows=10 width=496) (actual time=11.526..12.301 rows=10 loops=1) 
       -> Nested Loop (cost=47.650..172.010 rows=10 width=496) (actual time=11.520..12.268 rows=10 loops=1) 
        -> Hash Join (cost=47.650..87.710 rows=10 width=371) (actual time=11.054..11.461 rows=10 loops=1) 
          Hash Cond: (hotel.alpha_five = juliet_xray.alpha_five) 
          -> Bitmap Heap Scan on sierra hotel (cost=42.890..82.800 rows=10 width=345) (actual time=10.835..11.203 rows=10 loops=1) 
            Recheck Cond: (four = ANY ('quebec'::integer[])) 
           -> Bitmap Index Scan on seven (cost=0.000..42.890 rows=10 width=0) (actual time=0.194..0.194 rows=10 loops=1) 
             Index Cond: (four = ANY ('quebec'::integer[])) 
          -> Hash (cost=4.340..4.340 rows=34 width=30) (actual time=0.184..0.184 rows=34 loops=1) 
           -> Seq Scan on six juliet_xray (cost=0.000..4.340 rows=34 width=30) (actual time=0.029..0.124 rows=34 loops=1) 
        -> Index Scan using charlie on juliet_two zulu (cost=0.000..8.390 rows=1 width=129) (actual time=0.065..0.067 rows=1 loops=10) 
          Index Cond: (zulu.four = hotel.victor_whiskey) 
     -> Subquery Scan "*SELECT* 2" (cost=4.760..97.420 rows=10 width=366) (actual time=0.168..0.168 rows=0 loops=1) 
       -> Hash Join (cost=4.760..97.320 rows=10 width=366) (actual time=0.165..0.165 rows=0 loops=1) 
         Hash Cond: (alpha_xray.alpha_five = juliet_xray2.alpha_five) 
        -> Nested Loop (cost=0.000..92.390 rows=10 width=340) (actual time=0.162..0.162 rows=0 loops=1) 
          -> Seq Scan on lima_echo alpha_xray (cost=0.000..8.340 rows=10 width=216) (actual time=0.159..0.159 rows=0 loops=1) 
            Filter: (four = ANY ('quebec'::integer[])) 
          -> Index Scan using charlie on juliet_two xray (cost=0.000..8.390 rows=1 width=128) (never executed) 
            Index Cond: (zulu2.four = alpha_xray.victor_whiskey) 
        -> Hash (cost=4.340..4.340 rows=34 width=30) (never executed) 
          -> Seq Scan on six uniform (cost=0.000..4.340 rows=34 width=30) (never executed) 
     -> Subquery Scan "*SELECT* 3" (cost=43.350..77.910 rows=10 width=141) (actual time=1.775..1.775 rows=0 loops=1) 
       -> Hash Join (cost=43.350..77.810 rows=10 width=141) (actual time=1.771..1.771 rows=0 loops=1) 
         Hash Cond: (golf.alpha_five = juliet_xray3.alpha_five) 
        -> Bitmap Heap Scan on lima_golf golf (cost=38.590..72.910 rows=10 width=115) (actual time=0.110..0.110 rows=0 loops=1) 
          Recheck Cond: (four = ANY ('quebec'::integer[])) 
          -> Bitmap Index Scan on victor_hotel (cost=0.000..38.590 rows=10 width=0) (actual time=0.105..0.105 rows=0 loops=1) 
            Index Cond: (four = ANY ('quebec'::integer[])) 
        -> Hash (cost=4.340..4.340 rows=34 width=30) (actual time=0.118..0.118 rows=34 loops=1) 
          -> Seq Scan on six victor_kilo (cost=0.000..4.340 rows=34 width=30) (actual time=0.007..0.063 rows=34 loops=1) 
Total runtime: 14.728 ms 

seconda query - select * from view1 where id in (select ext_id from aggregate_table order by somedata limit 10) order by somedata;

Sort (cost=254515.780..254654.090 rows=55325 width=943) (actual time=24687.475..24687.488 rows=10 loops=1) 
    Sort Key: "five".xray_alpha 
    Sort Method: quicksort Memory: 30kB 
    -> Hash Semi Join (cost=54300.820..250157.370 rows=55325 width=943) (actual time=11921.783..24687.308 rows=10 loops=1) 
      Hash Cond: ("five".lima = "delta_echo".lima) 
     -> Append (cost=54298.270..235569.720 rows=1106504 width=494) (actual time=3412.453..23091.938 rows=1106503 loops=1) 
       -> Subquery Scan "*SELECT* 1" (cost=54298.270..234227.250 rows=1100622 width=496) (actual time=3412.450..20234.122 rows=1100622 loops=1) 
        -> Hash Join (cost=54298.270..223221.030 rows=1100622 width=496) (actual time=3412.445..17078.021 rows=1100622 loops=1) 
          Hash Cond: (three_victor.xray_hotel = delta_yankee.xray_hotel) 
          -> Hash Join (cost=54293.500..180567.160 rows=1100622 width=470) (actual time=3412.251..12108.676 rows=1100622 loops=1) 
            Hash Cond: (three_victor.tango_three = quebec_seven.lima) 
           -> Seq Scan on india three_victor (cost=0.000..104261.220 rows=1100622 width=345) (actual time=0.015..3437.722 rows=1100622 loops=1) 
           -> Hash (cost=44613.780..44613.780 rows=774378 width=129) (actual time=3412.031..3412.031 rows=774603 loops=1) 
             -> Seq Scan on oscar quebec_seven (cost=0.000..44613.780 rows=774378 width=129) (actual time=4.142..1964.036 rows=774603 loops=1) 
          -> Hash (cost=4.340..4.340 rows=34 width=30) (actual time=0.149..0.149 rows=34 loops=1) 
           -> Seq Scan on alpha_kilo delta_yankee (cost=0.000..4.340 rows=34 width=30) (actual time=0.017..0.095 rows=34 loops=1) 
       -> Subquery Scan "*SELECT* 2" (cost=4.760..884.690 rows=104 width=366) (actual time=7.846..10.161 rows=104 loops=1) 
        -> Hash Join (cost=4.760..883.650 rows=104 width=366) (actual time=7.837..9.804 rows=104 loops=1) 
          Hash Cond: (foxtrot.xray_hotel = delta_yankee2.xray_hotel) 
          -> Nested Loop (cost=0.000..877.200 rows=104 width=340) (actual time=7.573..9.156 rows=104 loops=1) 
           -> Seq Scan on four_india foxtrot (cost=0.000..7.040 rows=104 width=216) (actual time=0.081..0.311 rows=104 loops=1) 
           -> Index Scan using three_delta on oscar alpha_victor (cost=0.000..8.350 rows=1 width=128) (actual time=0.077..0.078 rows=1 loops=104) 
             Index Cond: (quebec_seven2.lima = foxtrot.tango_three) 
          -> Hash (cost=4.340..4.340 rows=34 width=30) (actual time=0.216..0.216 rows=34 loops=1) 
           -> Seq Scan on alpha_kilo quebec_foxtrot (cost=0.000..4.340 rows=34 width=30) (actual time=0.035..0.153 rows=34 loops=1) 
       -> Subquery Scan "*SELECT* 3" (cost=4.760..457.770 rows=5778 width=141) (actual time=0.264..58.353 rows=5777 loops=1) 
        -> Hash Join (cost=4.760..399.990 rows=5778 width=141) (actual time=0.253..39.062 rows=5777 loops=1) 
          Hash Cond: (four_uniform.xray_hotel = delta_yankee3.xray_hotel) 
          -> Seq Scan on whiskey four_uniform (cost=0.000..315.780 rows=5778 width=115) (actual time=0.112..15.759 rows=5778 loops=1) 
          -> Hash (cost=4.340..4.340 rows=34 width=30) (actual time=0.117..0.117 rows=34 loops=1) 
           -> Seq Scan on alpha_kilo golf (cost=0.000..4.340 rows=34 width=30) (actual time=0.005..0.059 rows=34 loops=1) 
     -> Hash (cost=2.430..2.430 rows=10 width=4) (actual time=0.303..0.303 rows=10 loops=1) 
       -> Subquery Scan "ANY_subquery" (cost=0.000..2.430 rows=10 width=4) (actual time=0.092..0.284 rows=10 loops=1) 
        -> Limit (cost=0.000..2.330 rows=10 width=68) (actual time=0.089..0.252 rows=10 loops=1) 
          -> Index Scan using tango_seven on zulu romeo (cost=0.000..257535.070 rows=1106504 width=68) (actual time=0.087..0.227 rows=10 loops=1) 
Total runtime: 24687.975 ms 
+1

Puoi mostrarci la SELEZIONE ANALIZZA SELEZIONA ...? Forse usando [depesz] (http://explain.depesz.com/) – MatheusOl

+0

proverei a mettere i risultati delle subquery nella tabella temporanea e a fare IN (selezionare id da temptable). La differenza è la clausola 'limite' che potrebbe causare all'ottimizzatore l'esecuzione della sottoselezione per ogni singola riga in table1. PS sembra efficacemente uguale a quanto suggerito da @Clodoaldo – rootkit

+0

provato 'creare tabella temporanea aggregate_table_tmp come select ext_id dall'ordine aggregate_table by somedata limit 10' e quindi utilizzarlo in sottoquery' select * da table1 dove id in (select ext_id from aggregate_table_tmp) order da somedata' - senza fortuna. Lo stesso 25000ms. – Snifff

risposta

29

Sembra che ho finalmente trovato una soluzione:

select * 
    from view1 
    where view1.id = ANY(
         (select array(select ext_id 
            from aggregate_table 
            order by somedata limit 10) 
         )::integer[] 
        ) 
    order by view1.somedata; 

Dopo elaborando @ idea di Dukeling:

Ho il sospetto dove id in (1, 2,3,4,5,6,7,8,9,10) può essere ottimizzato e dove id in (selezionare ...) non può, il motivo è che (1,2,3,4,5,6,7,8,9,10) è un'espressione costante, mentre la selezione è non.

e la localizzazione di questi nel piano di query più veloce

Recheck Cond: (id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])) 
Index Cond: (id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])) 

questo funziona anche più veloce della prima query in questione, circa 1.2ms, e ora utilizza

Recheck Cond: (id = ANY ($1)) 
Index Cond: (id = ANY ($1)) 

e bitmap scansioni nel piano.

+11

usando' ARRAY' è un bel trucco per istruire i PG da utilizzare indice in sottoquery BTW, quanto sopra 'clausola di ANY' può essere semplificata a questa' QUALSIASI (array ()) ' –

+0

Non ho ancora un grande dato per sperimentare, ma perché' seleziona array (seleziona ...) 'invece di quello proposto da @BlueSmith' (seleziona .. .) '' Inoltre ':: integer []' fa la differenza? Ad esempio, se ho valori stringa, devo lanciare qualsiasi tipo per prestazioni più veloci? – akostadinov

3

Ho il sospetto where id in (1,2,3,4,5,6,7,8,9,10) può essere ottimizzato e where id in (select ...) non può, la ragione essendo che (1,2,3,4,5,6,7,8,9,10) è una costante e xpression, mentre lo select non lo è.

ne dite:

WITH myCTE AS 
(
    SELECT ext_id 
    FROM aggregate_table 
    ORDER BY somedata 
    LIMIT 10 
) 
SELECT * 
FROM myCTE 
LEFT JOIN table1 
    ON myCTE.ext_id = table1.id 
ORDER BY somedata 
+0

uguale alla variante di @ Clodoaldo, 24000 ms – Snifff

+0

@Snifff Modificato su' SINISTRA JOIN', può fare la differenza. La linea di fondo sembra essere che PostgreSQL sta facendo un lavoro terribile per l'ottimizzazione, mi piacerebbe vedere le prestazioni di MySQL o SQL Server sugli stessi dati. – Dukeling

+2

'SINISTRA JOIN' fa la differenza -. Tempo fino a 65000ms :( – Snifff

Problemi correlati