Er zijn altijd 2 dingen waarmee u rekening moet houden bij het optimaliseren van zoekopdrachten:
- Welke indexen kunnen worden gebruikt (mogelijk moet u indexen maken)
- Hoe de query is geschreven (mogelijk moet u de query wijzigen om de query-optimizer in staat te stellen de juiste indexen te vinden en gegevens niet redundant opnieuw te lezen)
Een paar opmerkingen:
-
U voert datummanipulaties uit voordat u zich bij uw dates aansluit. Als algemene regel zal dit voorkomen dat een query-optimizer een index gebruikt, zelfs als deze bestaat. U moet proberen uw uitdrukkingen zo te schrijven dat geïndexeerde kolommen ongewijzigd bestaan aan één kant van de uitdrukking.
-
Uw subquery's filteren naar dezelfde periode als
generate_series
. Dit is een duplicatie en het beperkt het vermogen van de optimser om de meest efficiënte optimalisatie te kiezen. Ik vermoed dat dit is geschreven om de prestaties te verbeteren, omdat de optimser geen index kon gebruiken in de datumkolom (body_time
)? -
OPMERKING :We zouden eigenlijk heel graag een index willen gebruiken op
Body.body_time
-
ORDER BY
binnen de subquery's is op zijn best overbodig. In het slechtste geval zou het de query-optimizer kunnen dwingen om de resultatenset te sorteren voordat deze wordt toegevoegd; en dat is niet per se goed voor het queryplan. Pas de bestelling liever pas aan het einde toe voor de uiteindelijke weergave. -
Gebruik van
LEFT JOIN
in uw subquery's is ongepast. Ervan uitgaande dat u ANSI-conventies gebruikt voorNULL
gedrag (en dat zou je ook moeten zijn), elke buitenste voegt zich bijenvelope
zouenvelope_command=NULL
. teruggeven , en deze zouden bijgevolg worden uitgesloten door de voorwaardeenvelope_command=?
. -
Subquery's
o
eni
zijn bijna identiek, behalve voor deenvelope_command
waarde. Dit dwingt de optimser om dezelfde onderliggende tabellen twee keer te scannen. U kunt een draaitabel gebruiken techniek om de gegevens één keer samen te voegen en de waarden in 2 kolommen te splitsen.
Probeer het volgende, waarbij gebruik wordt gemaakt van de spiltechniek:
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
BEWERKEN :Filter toegevoegd voorgesteld door Tom H.