Podczas optymalizacji zapytań zawsze należy wziąć pod uwagę 2 rzeczy:
- Jakich indeksów można użyć (może być konieczne utworzenie indeksów)
- Jak napisane jest zapytanie (może być konieczna zmiana zapytania, aby umożliwić optymalizatorowi zapytań znalezienie odpowiednich indeksów i uniknięcie ponownego odczytywania danych zbędnych)
Kilka obserwacji:
-
Manipulujesz datami, zanim dołączysz do swoich randek. Zasadniczo uniemożliwia to optymalizatorowi zapytań korzystanie z indeksu, nawet jeśli on istnieje. Powinieneś spróbować pisać swoje wyrażenia w taki sposób, aby indeksowane kolumny istniały niezmienione po jednej stronie wyrażenia.
-
Twoje podzapytania są filtrowane do tego samego zakresu dat, co
generate_series
. Jest to duplikacja i ogranicza możliwość wyboru przez optymalizatora najbardziej wydajnej optymalizacji. Podejrzewam, że mogło to zostać napisane w celu poprawy wydajności, ponieważ optymalizator nie był w stanie użyć indeksu w kolumnie daty (body_time
)? -
UWAGA :Właściwie bardzo chcielibyśmy użyć indeksu w
Body.body_time
-
ORDER BY
w podzapytaniach jest w najlepszym razie zbędne. W najgorszym przypadku może to zmusić optymalizator zapytań do posortowania zbioru wyników przed dołączeniem; i to niekoniecznie jest dobre dla planu zapytań. Zamiast tego stosuj zamawianie tylko na końcu w celu ostatecznego wyświetlenia. -
Użycie
LEFT JOIN
w podzapytaniach jest nieodpowiednie. Zakładając, że używasz konwencji ANSI dlaNULL
zachowanie (a powinno być), każdy zewnętrzny dołącza doenvelope
zwrócienvelope_command=NULL
, a te w konsekwencji zostałyby wykluczone przez warunekenvelope_command=?
. -
Zapytania podrzędne
o
ii
są prawie identyczne, z wyjątkiemenvelope_command
wartość. Zmusza to optymalizatora do dwukrotnego skanowania tych samych tabel bazowych. Możesz użyć tabeli przestawnej technika jednokrotnego połączenia z danymi i podzielenia wartości na 2 kolumny.
Wypróbuj następujące, które wykorzystują technikę obrotu:
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
EDYTUJ :Dodano filtr sugerowany przez Toma H.