Le clausole WHERE sono applicate nell'ordine in cui sono state scritte?


36

Sto cercando di ottimizzare una query che esamina una tabella di grandi dimensioni (37 milioni di righe) e ho una domanda sull'ordine in cui le operazioni vengono eseguite in una query.

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

Le WHEREclausole per l'intervallo di date vengono eseguite prima della query secondaria? È un buon modo per mettere al primo posto le clausole più restrittive per evitare grandi loop per altre clausole, al fine di eseguire un'esecuzione più rapida?

Ora le query richiedono così tanto tempo per essere eseguite.

Risposte:


68

Per approfondire la risposta di @ alci:

A PostgreSQL non importa in quale ordine scrivi le cose

  • PostgreSQL non si preoccupa affatto dell'ordine delle voci in una WHEREclausola e sceglie gli indici e l'ordine di esecuzione basandosi solo sulla stima dei costi e della selettività.

  • Anche l'ordine in cui vengono scritti i join viene ignorato fino alla configurazione join_collapse_limit; se ci sono più join di così, li eseguirà nell'ordine in cui sono scritti.

  • Le subquery possono essere eseguite prima o dopo la query che le contiene, a seconda di ciò che è più veloce, purché la subquery venga eseguita prima che la query esterna abbia effettivamente bisogno delle informazioni. Spesso in realtà la sottoquery viene eseguita in un certo senso nel mezzo o interfogliata con la query esterna.

  • Non esiste alcuna garanzia che PostgreSQL eseguirà effettivamente parti della query. Possono essere completamente ottimizzati via. Questo è importante se chiamate funzioni con effetti collaterali.

PostgreSQL trasformerà la tua query

PostgreSQL trasformerà pesantemente le query mantenendo gli stessi esatti effetti, al fine di renderle più veloci senza cambiare i risultati.

  • Condizioni di fuori di un sottoquery possono ottenere spinto verso il basso nella sottoquery in modo da eseguire come parte della sottoquery non dove li hai scritto nella query esterna

  • I termini nella sottoquery possono essere richiamati nella query esterna in modo che la loro esecuzione venga eseguita come parte della query esterna, non dove sono stati scritti nella sottoquery

  • La subquery può, e spesso è, appiattita in un join sul tavolo esterno. Lo stesso vale per cose come EXISTSe NOT EXISTSdomande.

  • Le viste vengono appiattite nella query che utilizza la vista

  • Le funzioni SQL vengono spesso incorporate nella query chiamante

  • ... e ci sono numerose altre trasformazioni fatte alle query, come la pre-valutazione delle espressioni costanti, la de-correlazione di alcune sottoquery e ogni sorta di altri trucchi planner / ottimizzatore.

In generale PostgreSQL può trasformare e riscrivere in modo massiccio la query, al punto in cui ciascuna di queste query:

select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;

select *
from my_table
where not exists (
  select 1
  from other_table
  where other_table.my_table_id = my_table.id
);

select *
from my_table
where my_table.id not in (
  select my_table_id
  from other_table
  where my_table_id is not null
);

di solito producono esattamente lo stesso piano di query. (Supponendo che non ho fatto errori stupidi in quanto sopra).

Non è raro cercare di ottimizzare una query solo per scoprire che il pianificatore di query ha già capito i trucchi che stai provando e li ha applicati automaticamente, quindi la versione ottimizzata a mano non è migliore dell'originale.

limitazioni

Il planner / ottimizzatore è tutt'altro che onnipotente ed è limitato dal requisito di essere assolutamente certi di non poter modificare gli effetti della query, i dati disponibili con cui prendere decisioni, le regole che sono state implementate e il tempo della CPU può permettersi di spendere meditando sulle ottimizzazioni. Per esempio:

  • Il pianificatore si basa su statistiche mantenute da ANALYZE (di solito tramite autovacuum). Se questi sono obsoleti, la scelta del piano può essere errata.

  • Le statistiche sono solo un campione, quindi possono essere fuorvianti a causa degli effetti di campionamento, specialmente se viene preso un campione troppo piccolo. Potrebbero verificarsi scelte errate del piano.

  • Le statistiche non tengono traccia di alcuni tipi di dati sulla tabella, come le correlazioni tra le colonne. Questo può portare il pianificatore a prendere decisioni sbagliate quando assume che le cose siano indipendenti quando non lo sono.

  • Il pianificatore si basa su parametri di costo come random_page_costper dirgli la velocità relativa di varie operazioni sul particolare sistema su cui è installato. Queste sono solo guide. Se si sbagliano gravemente possono portare a scelte di piano scadenti.

  • Qualsiasi subquery con LIMITo OFFSETnon può essere appiattita o essere soggetta a pullup / pushdown. Ciò non significa che verrà eseguito prima di tutte le parti della query esterna, o anche che verrà eseguito affatto .

  • I termini CTE (le clausole in una WITHquery) vengono sempre eseguiti nella loro interezza, se non vengono affatto eseguiti. Non possono essere appiattiti e i termini non possono essere spinti verso l'alto o verso il basso oltre la barriera del termine CTE. I termini CTE vengono sempre eseguiti prima dell'interrogazione finale. Questo non è standard SQL comportamento non , ma è documentato come PostgreSQL fa le cose.

  • PostgreSQL ha una capacità limitata di ottimizzare tra query su tabelle, security_barrierviste e altri tipi speciali di relazioni esterne

  • PostgreSQL non incorporerà una funzione scritta in nulla tranne che nel semplice SQL, né eseguirà pullup / pushdown tra esso e la query esterna.

  • Il planner / ottimizzatore è davvero stupido sulla selezione degli indici di espressione e sulle differenze di tipo di dati banali tra indice ed espressione.

Anche di più.

La tua domanda

Nel caso della tua richiesta:

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

nulla impedisce che venga appiattito in una query più semplice con un set aggiuntivo di join, e molto probabilmente lo sarà.

Probabilmente si rivelerà qualcosa di simile (non testato, ovviamente):

select 1 
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province  
inner join center cr on cr.id_cr = province.id_cr 
where upper(offer.code_status) <> 'A' 
   and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
   and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
   and day.date_day >= '2014-10-01' 
   and day.date_day <= '2015-09-30';

PostgreSQL ottimizzerà quindi l'ordine dei join e i metodi di join in base alla sua selettività, alle stime del conteggio delle righe e agli indici disponibili. Se questi riflettono ragionevolmente la realtà, allora farà i join ed eseguirà le voci della clausola where in qualunque ordine sia meglio - spesso mescolandoli insieme, quindi fa un po 'di questo, quindi un po' di quello, quindi torna alla prima parte , eccetera.

Come vedere cosa ha fatto l'ottimizzatore

Non puoi vedere l'SQL in cui PostgreSQL ottimizza la tua query, perché converte l'SQL in una rappresentazione ad albero della query interna, quindi lo modifica. È possibile scaricare il piano delle query e confrontarlo con altre query.

Non è possibile "depargare" il piano di query o l'albero del piano interno in SQL.

http://explain.depesz.com/ ha un valido aiuto per il piano di query. Se sei totalmente nuovo ai piani di query, ecc. (Nel qual caso sono sorpreso che tu sia arrivato così lontano attraverso questo post), PgAdmin ha un visualizzatore di piani di query grafico che fornisce molte meno informazioni ma è più semplice.

Lettura correlata:

Le funzionalità di pushdown / pullup e appiattimento continuano a migliorare in ogni versione . PostgreSQL di solito ha ragione sulle decisioni di pull-up / push-down / appiattimento, ma non sempre, quindi occasionalmente devi (ab) usare un CTE o l' OFFSET 0hack. Se trovi un caso del genere, segnala un bug del pianificatore di query.


Se sei davvero appassionato, puoi anche usare l' debug_print_plansopzione per vedere il piano di query non elaborato, ma ti prometto che non vuoi leggerlo. Veramente.


Wow ... risposta abbastanza completa :-) Uno dei casi in cui ho avuto piani lenti con Postgresql (così come con altri noti motori DB, come Oracle), è con correlazioni tra colonne o più join correlati. Spesso finirà per fare cicli annidati, pensando che ci siano solo poche file a questo punto del piano, quando in realtà ce ne sono molte migliaia. Un modo per ottimizzare questo tipo di query è "impostare enable_nestloop = off;" per la durata della query.
alci,

Mi sono imbattuto in una situazione in cui v9.5.5 stava cercando di applicare TO_DATE prima del controllo per verificare se potesse essere applicato, in una semplice query 7 dove clausola. L'ordine contava.
user1133275

@ user1133275 In quel caso ha funzionato solo per te, perché le stime dei costi di calcolo erano le stesse. PostgreSQL potrebbe ancora decidere di essere eseguito to_dateprima del controllo in una versione successiva o a causa di alcune modifiche alle statistiche dell'ottimizzatore. Per eseguire in modo affidabile un controllo prima di una funzione che dovrebbe essere eseguita solo dopo il controllo, utilizzare CASEun'istruzione.
Craig Ringer,

una delle migliori risposte che abbia mai visto su SO! Pollice in alto, amico!
62mkv,

Ho sperimentato situazioni in cui la semplice aggiunta di query ha order byreso l'esecuzione della query molto più veloce di se non ci fosse order by. Questo è uno dei motivi per cui scrivo le mie query con join in modo tale da volerle eseguire: è bello avere un ottimo ottimizzatore, ma penso che non sia saggio fidarsi completamente del tuo destino per il suo risultato e scrivere query senza pensare a come è may beeseguito ... Grande risposta !!
Greg0ry,

17

SQL è un linguaggio dichiarativo: dici quello che vuoi, non come farlo. RDBMS sceglierà il modo in cui eseguirà la query, chiamata piano di esecuzione.

Una volta (5-10 anni fa), il modo in cui era stata scritta una query aveva un impatto diretto sul piano di esecuzione, ma oggigiorno la maggior parte dei motori di database SQL utilizza un ottimizzatore basato sui costi per la pianificazione. Cioè, valuterà diverse strategie per eseguire la query, in base alle sue statistiche sugli oggetti del database, e sceglierà la migliore.

Il più delle volte, è davvero il migliore, ma a volte il motore DB farà delle scelte sbagliate, causando query molto lente.


Va notato che su alcuni RDBMS l'ordine delle query è ancora significativo, ma per quelli più avanzati tutto ciò che dici è vero sia nella pratica che nella teoria. Quando il pianificatore di query sceglie scelte errate nell'ordine di esecuzione, in genere sono disponibili suggerimenti per le query che lo spingono in una direzione più efficiente (come WITH(INDEX(<index>))in MSSQL per forzare la scelta dell'indice per un particolare join).
David Spillett,

La domanda è se date_dayesiste effettivamente qualche indice su . Se non ce n'è nessuno, l'ottimizzatore non ha molti piani da confrontare.
jkavalik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.