Der er altid 2 ting at overveje, når du optimerer forespørgsler:
- Hvilke indekser kan bruges (du skal muligvis oprette indekser)
- Hvordan forespørgslen er skrevet (du skal muligvis ændre forespørgslen for at tillade forespørgselsoptimereren at være i stand til at finde passende indekser og ikke genlæse data redundant)
Et par observationer:
-
Du udfører datomanipulationer, før du tilmelder dig dine dates. Som en generel regel vil dette forhindre en forespørgselsoptimer i at bruge et indeks, selvom det eksisterer. Du bør prøve at skrive dine udtryk på en sådan måde, at indekserede kolonner eksisterer uændret på den ene side af udtrykket.
-
Dine underforespørgsler filtrerer til det samme datointerval som
generate_series
. Dette er en duplikering, og det begrænser optimerens mulighed for at vælge den mest effektive optimering. Jeg formoder, at det kan være skrevet ind for at forbedre ydeevnen, fordi optimeringsprogrammet ikke var i stand til at bruge et indeks på datokolonnen (body_time
)? -
BEMÆRK :Vi vil faktisk meget gerne bruge et indeks på
Body.body_time
-
ORDER BY
inden for underforespørgslerne er i bedste fald overflødig. I værste fald kunne det tvinge forespørgselsoptimeringsværktøjet til at sortere resultatsættet, før det sluttede sig til; og det er ikke nødvendigvis godt for forespørgselsplanen. Anvend hellere kun bestilling lige til sidst for endelig visning. -
Brug af
LEFT JOIN
i dine underforespørgsler er upassende. Forudsat at du bruger ANSI-konventioner forNULL
adfærd (og det burde du være), enhver ydre slutter sig tilenvelope
ville returnereenvelope_command=NULL
, og disse ville derfor blive udelukket af betingelsenenvelope_command=?
. -
Underforespørgsler
o
ogi
er næsten identiske med undtagelse afenvelope_command
værdi. Dette tvinger optimeren til at scanne de samme underliggende tabeller to gange. Du kan bruge en pivottabel teknik til at slutte sig til dataene én gang og opdele værdierne i 2 kolonner.
Prøv følgende, der bruger pivotteknikken:
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
REDIGER :Tilføjet filter foreslået af Tom H.