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 WHERE
clausola 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 EXISTS
e NOT EXISTS
domande.
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_cost
per 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 LIMIT
o OFFSET
non 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 WITH
query) 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_barrier
viste 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 0
hack. Se trovi un caso del genere, segnala un bug del pianificatore di query.
Se sei davvero appassionato, puoi anche usare l' debug_print_plans
opzione per vedere il piano di query non elaborato, ma ti prometto che non vuoi leggerlo. Veramente.