2013-02-15 13 views
7

Ecco una query lenta su Postgres 9.1.6, anche se il numero massimo è 2, con entrambe le righe già individuati dai loro chiavi primarie: (4,5 secondi)Come posso impedire a Postgres di inlining di una subquery?

EXPLAIN ANALYZE SELECT COUNT(*) FROM tbl WHERE id IN ('6d48fc431d21', 'd9e659e756ad') AND data ? 'building_floorspace' AND data ?| ARRAY['elec_mean_monthly_use', 'gas_mean_monthly_use']; 
                    QUERY PLAN                  
---------------------------------------------------------------------------------------------------------------------------------------------------- 
Aggregate (cost=4.09..4.09 rows=1 width=0) (actual time=4457.886..4457.887 rows=1 loops=1) 
    -> Index Scan using idx_tbl_on_data_gist on tbl (cost=0.00..4.09 rows=1 width=0) (actual time=4457.880..4457.880 rows=0 loops=1) 
     Index Cond: ((data ? 'building_floorspace'::text) AND (data ?| '{elec_mean_monthly_use,gas_mean_monthly_use}'::text[])) 
     Filter: ((id)::text = ANY ('{6d48fc431d21,d9e659e756ad}'::text[])) 
Total runtime: 4457.948 ms 
(5 rows) 

Hmm, forse se faccio una subquery con solo la parte chiave primaria prima ...: (no, ancora 4.5+ secondi)

EXPLAIN ANALYZE SELECT COUNT(*) FROM ( SELECT * FROM tbl WHERE id IN ('6d48fc431d21', 'd9e659e756ad') ) AS t WHERE data ? 'building_floorspace' AND data ?| ARRAY['elec_mean_monthly_use', 'gas_mean_monthly_use']; 
                    QUERY PLAN                  
---------------------------------------------------------------------------------------------------------------------------------------------------- 
Aggregate (cost=4.09..4.09 rows=1 width=0) (actual time=4854.170..4854.171 rows=1 loops=1) 
    -> Index Scan using idx_tbl_on_data_gist on tbl (cost=0.00..4.09 rows=1 width=0) (actual time=4854.165..4854.165 rows=0 loops=1) 
     Index Cond: ((data ? 'building_floorspace'::text) AND (data ?| '{elec_mean_monthly_use,gas_mean_monthly_use}'::text[])) 
     Filter: ((id)::text = ANY ('{6d48fc431d21,d9e659e756ad}'::text[])) 
Total runtime: 4854.220 ms 
(5 rows) 

Come posso evitare che Postgres dalla inlining subquery?

Sfondo: Ho una tabella Postgres 9.1 utilizzando hstore e con un GiST index su di esso.

+1

La stima dei costi sulla scansione dell'indice dei gist sembra essere abbastanza errata (a 4.09, è inferiore a quella dell'indice della chiave primaria). Potresti controllare se puoi fare qualcosa al riguardo, come garantire nuove statistiche e/o aumentare il [target delle statistiche] (http://www.postgresql.org/docs/9.1/static/runtime-config-query.html#GUC -default-Statistics-destinazione). –

+0

Ho aumentato il target a 9999 e ho eseguito 'VACUUM ANALYZE' ... ... ottenendo sempre la stessa stima dei costi rispetto alla discrepanza della realtà. Grazie per il tuo suggerimento, però! –

risposta

4

A quanto pare c'è un way to tell Postgres not to inline: (0.223ms!)

EXPLAIN ANALYZE SELECT COUNT(*) FROM ( SELECT * FROM tbl WHERE id IN ('6d48fc431d21', 'd9e659e756ad') OFFSET 0) AS t WHERE data ? 'building_floorspace' AND data ?| ARRAY['elec_mean_monthly_use', 'gas_mean_monthly_use']; 
                   QUERY PLAN                 
------------------------------------------------------------------------------------------------------------------------------------------ 
Aggregate (cost=8.14..8.15 rows=1 width=0) (actual time=0.165..0.166 rows=1 loops=1) 
    -> Subquery Scan on t (cost=4.14..8.14 rows=1 width=0) (actual time=0.160..0.160 rows=0 loops=1) 
     Filter: ((t.data ? 'building_floorspace'::text) AND (t.data ?| '{elec_mean_monthly_use,gas_mean_monthly_use}'::text[])) 
     -> Limit (cost=4.14..8.13 rows=2 width=496) (actual time=0.086..0.092 rows=2 loops=1) 
       -> Bitmap Heap Scan on tbl (cost=4.14..8.13 rows=2 width=496) (actual time=0.083..0.086 rows=2 loops=1) 
        Recheck Cond: ((id)::text = ANY ('{6d48fc431d21,d9e659e756ad}'::text[])) 
        -> Bitmap Index Scan on tbl_pkey (cost=0.00..4.14 rows=2 width=0) (actual time=0.068..0.068 rows=2 loops=1) 
          Index Cond: ((id)::text = ANY ('{6d48fc431d21,d9e659e756ad}'::text[])) 
Total runtime: 0.223 ms 
(9 rows) 

Il trucco è OFFSET 0 nel subquery.

+0

... ma PostgreSQL non ha suggerimenti per le query. No davvero. ;-) –

7

Credo OFFSET 0 è l'approccio migliore dal momento che è più, ovviamente, un hack che dimostra che qualcosa di strano sta succedendo, ed è improbabile che ci sarà mai cambiare il comportamento optimiser intorno OFFSET 0 ... wheras spera CTE diventerà inlineable ad un certo punto . La seguente spiegazione è per completezza; usa la risposta di Seamus.

Per subquery non correlati è possibile sfruttare il rifiuto di PostgreSQL inline WITH termini di ricerca di riformulare la query come:

WITH t AS (
    SELECT * FROM tbl WHERE id IN ('6d48fc431d21', 'd9e659e756ad') 
) 
SELECT COUNT(*) 
FROM t 
WHERE data ? 'building_floorspace' 
AND data ?| ARRAY['elec_mean_monthly_use', 'gas_mean_monthly_use']; 

Questo ha più o meno lo stesso effetto del OFFSET 0 hack, e come il offset 0 hackerare sfrutta capricci in Pg di ottimizzatore che le persone usano per aggirare la mancanza di parametri di query di Pg ... usandoli come suggerimenti per le query.

Problemi correlati