Det finns alltid två saker att tänka på när du optimerar frågor:
- Vilka index kan användas (du kan behöva skapa index)
- Hur frågan skrivs (du kan behöva ändra frågan för att tillåta frågeoptimeraren att kunna hitta lämpliga index och inte läsa om data redundant)
Några observationer:
-
Du utför datummanipulationer innan du går med på dina dejter. Som en allmän regel kommer detta att förhindra en frågeoptimerare från att använda ett index även om det finns. Du bör försöka skriva dina uttryck på ett sådant sätt att indexerade kolumner finns oförändrade på ena sidan av uttrycket.
-
Dina underfrågor filtreras till samma datumintervall som
generate_series
. Detta är en dubblering, och det begränsar optimerarens möjlighet att välja den mest effektiva optimeringen. Jag misstänker att det kan ha skrivits in för att förbättra prestandan eftersom optimeraren inte kunde använda ett index i datumkolumnen (body_time
)? -
OBS :Vi skulle faktiskt väldigt gärna vilja använda ett index på
Body.body_time
-
ORDER BY
inom underfrågorna är i bästa fall överflödig. I värsta fall kan det tvinga frågeoptimeraren att sortera resultatuppsättningen innan den går med; och det är inte nödvändigtvis bra för frågeplanen. Använd hellre bara beställning direkt i slutet för slutlig visning. -
Användning av
LEFT JOIN
i dina underfrågor är olämpligt. Förutsatt att du använder ANSI-konventioner förNULL
beteende (och det borde du vara), vilket som helst yttre ansluter tillenvelope
skulle returneraenvelope_command=NULL
, och dessa skulle följaktligen uteslutas av villkoretenvelope_command=?
. -
Underfrågor
o
ochi
är nästan identiska med undantag förenvelope_command
värde. Detta tvingar optimeraren att skanna samma underliggande tabeller två gånger. Du kan använda en pivottabell teknik för att ansluta till data en gång och dela upp värdena i 2 kolumner.
Prova följande som använder pivottekniken:
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
REDIGERA :Lade till filter föreslagit av Tom H.