2012-01-27 12 views
13

ho una visione DB, che consiste essenzialmente di due SELECT query con UNION ALL, in questo modo:query lente su "UNION ALL" vista

CREATE VIEW v AS 
SELECT time, etc. FROM t1 // #1... 
UNION ALL 
SELECT time, etc. FROM t2 // #2... 

Il problema è che seleziona della forma

SELECT ... FROM v WHERE time >= ... AND time < ... 

eseguire davvero molto lento su di esso.

Sia SELEZIONA # 1 e # 2 sono decentemente veloce, opportunamente indicizzati e così via: quando creo Visto v1 e v2 come:

CREATE VIEW v1 AS 
SELECT time, etc. FROM t1 // #1... 

CREATE VIEW v2 AS 
SELECT time, etc. FROM t2 // #2... 

E lo stesso SELECT, con la stessa condizione WHERE come le opere di cui sopra OK su di loro individualmente.

Qualche idea su dove potrebbe essere il problema e su come risolverlo?

(Solo per citare, è uno dei più recenti versioni di Postgres.)

Edit: L'aggiunta di piani di query anonimi (Thaks per @filiprem per il collegamento ad un attrezzo impressionante):

v1:

Aggregate (cost=9825.510..9825.520 rows=1 width=53) (actual time=59.995..59.995 rows=1 loops=1) 
    -> Index Scan using delta on echo alpha (cost=0.000..9815.880 rows=3850 width=53) (actual time=0.039..53.418 rows=33122 loops=1) 
      Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey)) 
      Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf))) 

v2:

Aggregate (cost=15.470..15.480 rows=1 width=33) (actual time=0.231..0.231 rows=1 loops=1) 
    -> Index Scan using yankee on six charlie (cost=0.000..15.220 rows=99 width=33) (actual time=0.035..0.186 rows=140 loops=1) 
      Index Cond: (("juliet" >= 'seven'::uniform bravo oscar whiskey) AND ("juliet" <= 'november'::uniform bravo oscar whiskey)) 
      Filter: (NOT victor) 

v:

0.123.516,41 mila
Aggregate (cost=47181.850..47181.860 rows=1 width=0) (actual time=37317.291..37317.291 rows=1 loops=1) 
    -> Append (cost=42.170..47132.480 rows=3949 width=97) (actual time=1.277..37304.453 rows=33262 loops=1) 
     -> Nested Loop Left Join (cost=42.170..47052.250 rows=3850 width=99) (actual time=1.275..37288.465 rows=33122 loops=1) 
       -> Hash Left Join (cost=42.170..9910.990 rows=3850 width=115) (actual time=1.123..117.797 rows=33122 loops=1) 
         Hash Cond: ((alpha_seven.two)::golf = (quebec_three.two)::golf) 
        -> Index Scan using delta on echo alpha_seven (cost=0.000..9815.880 rows=3850 width=132) (actual time=0.038..77.866 rows=33122 loops=1) 
          Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two)) 
          Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf))) 
        -> Hash (cost=30.410..30.410 rows=941 width=49) (actual time=1.068..1.068 rows=941 loops=1) 
          Buckets: 1024 Batches: 1 Memory Usage: 75kB 
          -> Seq Scan on alpha_india quebec_three (cost=0.000..30.410 rows=941 width=49) (actual time=0.010..0.486 rows=941 loops=1) 
       -> Index Scan using mike on hotel quebec_sierra (cost=0.000..9.630 rows=1 width=24) (actual time=1.112..1.119 rows=1 loops=33122) 
         Index Cond: ((alpha_seven.zulu)::golf = (quebec_sierra.zulu)::golf) 
     -> Subquery Scan on "*SELECT* 2" (cost=34.080..41.730 rows=99 width=38) (actual time=1.081..1.951 rows=140 loops=1) 
       -> Merge Right Join (cost=34.080..40.740 rows=99 width=38) (actual time=1.080..1.872 rows=140 loops=1) 
         Merge Cond: ((quebec_three.two)::golf = (charlie.two)::golf) 
        -> Index Scan using whiskey_golf on alpha_india quebec_three (cost=0.000..174.220 rows=941 width=49) (actual time=0.017..0.122 rows=105 loops=1) 
        -> Sort (cost=18.500..18.750 rows=99 width=55) (actual time=0.915..0.952 rows=140 loops=1) 
          Sort Key: charlie.two 
          Sort Method: quicksort Memory: 44kB 
          -> Index Scan using yankee on six charlie (cost=0.000..15.220 rows=99 width=55) (actual time=0.022..0.175 rows=140 loops=1) 
            Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two)) 
            Filter: (NOT victor) 

juliet è time.

+1

colonna "tempo" nella visualizzazione non è indicizzato. Dovrai indicizzare manualmente quella colonna nella tua vista.Dai un'occhiata al piano di esecuzione –

+0

Le query su questa vista * sempre * saranno limitate dal tempo? –

+0

@ stian.net: Non sei sicuro di cosa suggerisci. Non riesco ad aggiungere indici sulle colonne della vista e entrambe le tabelle sottostanti sono correttamente indicizzate sui campi temporali. –

risposta

-3

penso che non ho più punti di pubblicarlo come commenti in modo che io metto come una risposta

Non so come PostgreSQL lavora dietro la scena, penso che potrebbe ottenere un indizio se sarebbe stato Oracle, quindi è qui come Oracle avrebbe funzionato

tuo UNION ALL vista è più lenta perché, dietro la scena, i record da entrambi SELEZIONE # 1 e # 2 sono combinato in prima una tabella temporanea, che viene creata al volo e quindi il tuo SELECT ... FROM v WHERE time> = ... AND time < ... viene eseguito su questa tabella temporanea. Poiché sia ​​# 1 e # 2 sono indicizzati in modo che funzionino più rapidamente singolarmente come previsto, ma questa tabella temporanea non è indicizzata (ovviamente) e i record finali vengono selezionati da questa tabella temporanea in modo da ottenere una risposta più lenta .

Ora, almeno, non vedo alcun modo per avere più veloce + leggi + non materializzato

Un modo, altro che correre SELEZIONA # 1 e # 2 e UNION in modo esplicito , per rendere più veloce sarebbe utilizzare una stored procedure o una funzione nel linguaggio di programmazione dell'applicazione (se è il caso), e in questa procedura si effettuano chiamate separate a ciascuna tabella indicizzata e quindi si combinano i risultati, il che non è così semplice come SELECT ... FROM v WHERE time> = ... AND time < ... :(

+0

Dubito che questo sia il caso, visto che dal piano di query di esecuzione 'v' puoi vedere che entrambe le subquery sono vincolate dal campo' time' ('julia'), quindi sono abbastanza sicuro che non ci sia _huge temp table_ creato su quale vincolo 'tempo' viene applicato in seguito. –

+0

"Sono abbastanza sicuro che non ci sia una tabella temporanea enorme" Potresti essere corretto, ma un DBA o uno che ha un'idea precisa di ciò che sta accadendo dietro la scena può confermarlo. Lascia che venga la risposta, la risposta che spiegherebbe esattamente perché la query V sta prendendo tempo. – bjan

+0

Oracle> = 8i con l'ottimizzatore basato sul costo (predefinito) NON fa normalmente ciò che dici. Lo farà se l'ottimizzatore pensa che sia l'opzione migliore/unica, ma ciò non accade molto spesso. – gpeche

9

Questo sembra essere un caso di errore del pilota. Il piano di query "v" seleziona da almeno 5 diverse tabelle.

Ora, sei sicuro di essere connesso al database giusto? Forse ci sono alcune impostazioni funky search_path? Forse t1 e t2 sono effettivamente viste (possibilmente in uno schema diverso)? Forse stai selezionando in qualche modo dalla vista sbagliata?

Modificato dopo il chiarimento:

si utilizza un abbastanza nuova funzionalità denominata "join rimozione": http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.0#Join_Removal

http://rhaas.blogspot.com/2010/06/why-join-removal-is-cool.html

Sembra che la funzione non calci in quando tutto è coinvolto unione. Probabilmente dovrai riscrivere la vista usando solo le due tabelle richieste.

un'altra modifica: Sembra che si stia utilizzando un aggregato (come "select count (*) from v" vs. "select * from v"), che potrebbe ottenere piani molto diversi di fronte alla rimozione del join. Immagino che non andremo molto lontano senza pubblicare le query, le definizioni e le tabelle utilizzate e ...

+1

'v' query effettivamente da> 2 diverse tabelle, come' v1' e 'v2' query da> 2 tabelle diverse troppo (per valutare varie colonne). Semplicemente sembra che queste colonne non vengano tralasciate quando si estraggono da 'v1' e' v2' individualmente, ma sono quando si esegue una query su 'v'. –

+0

vedi risposta modificata – maniek

+0

Grazie, sembra che stia conducendo nella giusta direzione. Proverò a fornire maggiori informazioni su come queste domande sono sembrate. –

1

Una possibilità sarebbe di emettere un nuovo SQL dinamicamente ad ogni chiamata invece di creare una vista e di integrare la clausola di cui in ogni SELECT della query di unione

SELECT time, etc. FROM t1 
    WHERE time >= ... AND time < ... 
UNION ALL 
SELECT time, etc. FROM t2 
    WHERE time >= ... AND time < ... 

EDIT:

si può utilizzare una funzione parametrizzata?

CREATE OR REPLACE FUNCTION CallMyView(t1 date, t2 date) 
RETURNS TABLE(d date, etc.) 
AS $$ 
    BEGIN 
     RETURN QUERY 
      SELECT time, etc. FROM t1 
       WHERE time >= t1 AND time < t2 
      UNION ALL 
      SELECT time, etc. FROM t2 
       WHERE time >= t1 AND time < t2; 
    END; 
$$ LANGUAGE plpgsql; 

chiamata

SELECT * FROM CallMyView(..., ...); 
+2

Questo non funziona per me, ho sicuramente bisogno di "una vista per domarli tutti". :) –

5

Credo che la vostra richiesta è in corso di esecuzione simile a:

(
    (SELECT time, etc. FROM t1 // #1...) 
    UNION ALL 
    (SELECT time, etc. FROM t2 // #2...) 
) 
WHERE time >= ... AND time < ... 

che l'ottimizzatore sta avendo difficoltà a ottimizzare. per esempio sta facendo il UNION ALL prima di applicare la clausola WHERE ma, si desidera che applichi la clausola WHEREprima dello il UNION ALL.

Non potresti inserire la tua clausola WHERE nello CREATE VIEW?

CREATE VIEW v AS 
(SELECT time, etc. FROM t1 WHERE time >= ... AND time < ...) 
UNION ALL 
(SELECT time, etc. FROM t2 WHERE time >= ... AND time < ...) 

In alternativa, se la vista non può avere la clausola WHERE, allora, forse, è possibile mantenere i due punti di vista e fare la UNION ALL con la clausola WHERE quando ne hai bisogno:

CREATE VIEW v1 AS 
SELECT time, etc. FROM t1 // #1... 

CREATE VIEW v2 AS 
SELECT time, etc. FROM t2 // #2... 

(SELECT * FROM v1 WHERE time >= ... AND time < ...) 
UNION ALL 
(SELECT * FROM v2 WHERE time >= ... AND time < ...) 
2

non lo faccio Conoscere Postgres, ma alcuni RMDB gestiscono operatori di confronto peggiori di FRA in caso di indici. Farei un tentativo usando BETWEEN.

SELECT ... FROM v WHERE time BETWEEN ... AND ... 
0

Provare a creare la vista utilizzando UNION DISTINCT anziché UNION ALL. Vedi se dà risultati sbagliati. Guarda se offre prestazioni più veloci.

Se fornisce risultati errati, provare e mappare le operazioni SQL sulle tabelle alle operazioni relazionali sulle relazioni. Gli elementi delle relazioni sono sempre distinti. Potrebbe esserci qualcosa di fondamentalmente sbagliato nel tuo modello.

Sono profondamente sospettoso di LEFT JOINS nel piano di query che hai mostrato. Non dovrebbe essere necessario eseguire LEFT JOINS per ottenere i risultati che sembra selezionare.

1

Combina i due tavoli. Aggiungi una colonna per indicare la tabella originale. Se necessario, sostituire i nomi delle tabelle originali con le viste che selezionano solo la parte pertinente. Problema risolto!

Esaminare il modello di progettazione della superclasse/sottoclasse db potrebbe essere utile.

+0

Non credevo che sarebbe stato d'aiuto fino a quando non è stato provato. Ora, quando la condizione WHERE tocca la stessa colonna dalla stessa tabella, la query è molto più veloce. Grazie per questo suggerimento! –

0

Incontrato stesso scenario sul 11g:

Scenario 1:

CREATE VIEW v AS 
    SELECT time, etc. FROM t1 // #1... 

La seguente interrogazione corre veloce, il piano sembra a posto:

SELECT ... FROM v WHERE time >= ... AND time < ... 

Scenario 2:

CREATE VIEW v AS 
    SELECT time, etc. FROM t2 // #2... 

la seguente query corre veloce, il piano sembra a posto:

SELECT ... FROM v WHERE time >= ... AND time < ... 

Scenario 3, con UNION ALL:

CREATE VIEW v AS 
    SELECT time, etc. FROM t1 // #1... 
    UNION ALL 
    SELECT time, etc. FROM t2 // #2... 

I seguenti scorre lenta. Piano rompe T1 e T2 (che erano anche viste) e li assembla come una grande serie di sindacati. I filtri tempo corretta applicazione sui singoli componenti, ma è ancora molto lento:

SELECT ... FROM v WHERE time >= ... AND time < ... 

sarei stato felice di avere solo un tempo nel campo da baseball t1 più t2, ma era più del doppio . L'aggiunta del suggerimento parallel ha fatto il trucco per me in questo caso. Ha ri-organizzato tutto in un piano migliore:

SELECT /*+ parallel */ ... FROM v WHERE time >= ... AND time < ...