Винаги има 2 неща, които трябва да имате предвид, когато оптимизирате заявките:
- Какви индекси могат да се използват (може да се наложи да създадете индекси)
- Как е написана заявката (може да се наложи да промените заявката, за да позволите на оптимизатора на заявки да може да намира подходящи индекси и да не препрочита данните излишно)
Няколко наблюдения:
-
Вие извършвате манипулации с дати, преди да се присъедините към вашите дати. Като общо правило това ще попречи на оптимизатора на заявки да използва индекс, дори ако той съществува. Трябва да опитате да напишете вашите изрази по такъв начин, че индексираните колони да съществуват непроменени от едната страна на израза.
-
Вашите подзаявки се филтрират към същия период от време като
generate_series
. Това е дублиране и ограничава способността на оптимизатора да избере най-ефективната оптимизация. Подозирам, че това може да е записано, за да се подобри производителността, защото оптимизаторът не успя да използва индекс в колоната с дата (body_time
)? -
ЗАБЕЛЕЖКА :Всъщност много бихме искали да използваме индекс на
Body.body_time
-
ORDER BY
в рамките на подзаявките е в най-добрия случай излишен. В най-лошия случай може да принуди оптимизатора на заявки да сортира набора от резултати, преди да се присъедини; и това не е непременно добро за плана на заявката. По-скоро прилагайте подреждане само в края за окончателно показване. -
Използване на
LEFT JOIN
във вашите подзаявки е неподходящо. Ако приемем, че използвате ANSI конвенции заNULL
поведение (и трябва да бъдете), всяко външно се присъединява къмenvelope
ще върнеenvelope_command=NULL
и те следователно биха били изключени от условиетоenvelope_command=?
. -
Подзаявки
o
иi
са почти идентични освен заenvelope_command
стойност. Това принуждава оптимизатора да сканира едни и същи базови таблици два пъти. Можете да използвате осева таблица техника за присъединяване към данните веднъж и разделяне на стойностите в 2 колони.
Опитайте следното, което използва техниката на завъртане:
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
РЕДАКТИРАНЕ :Добавен филтър, предложен от Том Х.