Ci sono sempre 2 cose da considerare quando si ottimizzano le query:
- Quali indici possono essere utilizzati (potrebbe essere necessario creare indici)
- Come viene scritta la query (potrebbe essere necessario modificare la query per consentire a Query Optimizer di trovare gli indici appropriati e per non rileggere i dati in modo ridondante)
Alcune osservazioni:
-
Stai eseguendo manipolazioni di date prima di unirti alle tue date. Come regola generale, ciò impedirà a un ottimizzatore di query di utilizzare un indice anche se esiste. Dovresti provare a scrivere le tue espressioni in modo tale che le colonne indicizzate esistano inalterate su un lato dell'espressione.
-
Le tue sottoquery filtrano in base allo stesso intervallo di date di
generate_series
. Questa è una duplicazione e limita la capacità dell'ottimizzatore di scegliere l'ottimizzazione più efficiente. Sospetto che potrebbe essere stato scritto per migliorare le prestazioni perché l'ottimizzatore non è stato in grado di utilizzare un indice nella colonna della data (body_time
)? -
NOTA :Ci piacerebbe davvero molto usare un indice su
Body.body_time
-
ORDER BY
all'interno delle sottoquery è nella migliore delle ipotesi ridondante. Nel peggiore dei casi potrebbe forzare l'ottimizzatore di query a ordinare il set di risultati prima di unirsi; e ciò non è necessariamente positivo per il piano di query. Piuttosto, applica l'ordine solo alla fine per la visualizzazione finale. -
Utilizzo di
LEFT JOIN
nelle tue sottoquery è inappropriato. Supponendo che tu stia utilizzando le convenzioni ANSI perNULL
comportamento (e dovresti esserlo), qualsiasi esterno si unisce aenvelope
restituirebbeenvelope_command=NULL
, e questi sarebbero di conseguenza esclusi dalla condizioneenvelope_command=?
. -
Sottoquery
o
ei
sono quasi identici tranne che perenvelope_command
valore. Ciò costringe l'ottimizzatore a scansionare due volte le stesse tabelle sottostanti. Puoi utilizzare una tabella pivot tecnica per unire i dati una volta e dividere i valori in 2 colonne.
Prova quanto segue che utilizza la tecnica pivot:
SELECT p.period,
/*The pivot technique in action...*/
SUM(
CASE WHEN envelope_command = 1 THEN body_size
ELSE 0
END) AS Outbound,
SUM(
CASE WHEN envelope_command = 2 THEN body_size
ELSE 0
END) AS Inbound
FROM (
SELECT date '2009-10-01' + s.day AS period
FROM generate_series(0, date '2009-10-31' - date '2009-10-01') AS s(day)
) AS p
/*The left JOIN is justified to ensure ALL generated dates are returned
Also: it joins to a subquery, else the JOIN to envelope _could_ exclude some generated dates*/
LEFT OUTER JOIN (
SELECT b.body_size,
b.body_time,
e.envelope_command
FROM body AS b
INNER JOIN envelope e
ON e.message_id = b.message_id
WHERE envelope_command IN (1, 2)
) d
/*The expressions below allow the optimser to use an index on body_time if
the statistics indicate it would be beneficial*/
ON d.body_time >= p.period
AND d.body_time < p.period + INTERVAL '1 DAY'
GROUP BY p.Period
ORDER BY p.Period
MODIFICA :Aggiunto filtro suggerito da Tom H.