Při optimalizaci dotazů je vždy třeba zvážit 2 věci:
- Jaké indexy lze použít (možná budete muset indexy vytvořit)
- Jak je dotaz napsán (možná budete muset dotaz změnit, aby optimalizátor dotazů mohl najít vhodné indexy a aby se data zbytečně nečetla)
Několik postřehů:
-
Než se k datům připojíte, provádíte manipulace s datem. Jako obecné pravidlo to zabrání optimalizátoru dotazů v použití indexu, i když existuje. Měli byste se pokusit napsat své výrazy tak, aby indexované sloupce existovaly beze změny na jedné straně výrazu.
-
Vaše poddotazy jsou filtrovány na stejné časové období jako
generate_series
. Toto je duplikace a omezuje možnost optimalizátora zvolit nejúčinnější optimalizaci. Mám podezření, že to mohlo být zapsáno za účelem zlepšení výkonu, protože optimalizátor nedokázal použít index ve sloupci data (body_time
)? -
POZNÁMKA :Ve skutečnosti bychom velmi rádi použili index na
Body.body_time
-
ORDER BY
v rámci poddotazů je přinejlepším redundantní. V nejhorším případě by to mohlo přinutit optimalizátor dotazů seřadit sadu výsledků před připojením; a to není nutně dobré pro plán dotazů. Pro konečné zobrazení raději použijte objednávku přímo na konci. -
Použití
LEFT JOIN
ve vašich poddotazech je nevhodné. Za předpokladu, že proNULL
používáte konvence ANSI chování (a měli byste být), jakékoli vnější se připojí kenvelope
vrátíenvelope_command=NULL
a ty by následně byly vyloučeny podmínkouenvelope_command=?
. -
Dílčí dotazy
o
ai
jsou téměř totožné kroměenvelope_command
hodnota. To přinutí optimser skenovat stejné podkladové tabulky dvakrát. Můžete použít kontingenční tabulku technika pro jedno připojení k datům a rozdělení hodnot do 2 sloupců.
Vyzkoušejte následující postup, který využívá techniku pivotování:
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
UPRAVIT :Přidán filtr navržený Tomem H.