Я пытаюсь выяснить способ ускорить особенно громоздкий запрос, который агрегировал некоторые данные по дате через несколько таблиц. Полный (ужасный) запрос ниже наряду с EXPLAIN ANALYZE
показать, насколько ужасный это.
Если кто-либо мог бы посмотреть и видеть, могут ли они определить какие-либо главные проблемы (который вероятен, я не парень Пост-ГРЭС), который был бы превосходен.
Таким образом, здесь идет. Запрос:
SELECT
to_char(p.period, 'DD/MM/YY') as period,
coalesce(o.value, 0) AS outbound,
coalesce(i.value, 0) 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
LEFT OUTER JOIN(
SELECT
SUM(b.body_size) AS value,
b.body_time::date AS period
FROM body AS b
LEFT JOIN
envelope e ON e.message_id = b.message_id
WHERE
e.envelope_command = 1
AND b.body_time BETWEEN '2009-10-01'
AND (date '2009-10-31' + INTERVAL '1 DAY')
GROUP BY period
ORDER BY period
) AS o ON p.period = o.period
LEFT OUTER JOIN(
SELECT
SUM(b.body_size) AS value,
b.body_time::date AS period
FROM body AS b
LEFT JOIN
envelope e ON e.message_id = b.message_id
WHERE
e.envelope_command = 2
AND b.body_time BETWEEN '2009-10-01'
AND (date '2009-10-31' + INTERVAL '1 DAY')
GROUP BY period
ORDER BY period
) AS i ON p.period = i.period
EXPLAIN ANALYZE
может быть найден здесь: на explain.depesz.com
Любые комментарии или вопросы ценятся.
Удачи
Здание на предложениях Craig Young , вот измененный запрос, который проходит через ~ 1,8 секунды для набора данных, над которым я работаю. Это небольшое улучшение оригинала ~ 2.0 и огромное улучшение на Craig, который занял ~ 22с.
SELECT
p.period,
/* The pivot technique... */
SUM(CASE envelope_command WHEN 1 THEN body_size ELSE 0 END) AS Outbound,
SUM(CASE envelope_command WHEN 2 THEN body_size ELSE 0 END) AS Inbound
FROM
(
/* Get days range */
SELECT date '2009-10-01' + day AS period
FROM generate_series(0, date '2009-10-31' - date '2009-10-01') AS day
) p
/* Join message information */
LEFT OUTER JOIN
(
SELECT b.body_size, b.body_time::date, e.envelope_command
FROM body AS b
INNER JOIN envelope e ON e.message_id = b.message_id
WHERE
e.envelope_command IN (2, 1)
AND b.body_time::date BETWEEN (date '2009-10-01') AND (date '2009-10-31')
) d ON d.body_time = p.period
GROUP BY p.period
ORDER BY p.period
Я удалил мой PostgreSQL Server пару дней назад, так что вы, вероятно, придется играть с этим, но, надеюсь, это хорошо для вас.
Ключи:
, если ничего Иначе я думаю, что запрос ниже немного яснее.
Я использовал таблицу календаря в моем запросе, но вы можете заменить это с помощью Generate_Series, как вы его использовали.
Также, в зависимости от индексации, может быть лучше сравнивать body_date с> = и <вместо того, чтобы вытащить часть даты и сравнения. Я не знаю достаточно о PostgreSQL, чтобы узнать, как он работает за кулисами, поэтому я бы попробую оба подхода к тому, что сервер может оптимизировать лучше. В псевдо-коде вы будете делать: body_date> = дата (время = полночь) и body_date <дата + 1 (время = полночь).
SELECT
CAL.calendar_date AS period,
SUM(O.body_size) AS outbound,
SUM(I.body_size) AS inbound
FROM
Calendar CAL
INNER JOIN Body OB ON
OB.body_time::date = CAL.calendar_date
INNER JOIN Envelope OE ON
OE.message_id = OB.message_id AND
OE.envelope_command = 1
INNER JOIN Body IB ON
IB.body_time::date = CAL.calendar_date
INNER JOIN Envelope IE ON
IE.message_id = IB.message_id AND
IE.envelope_command = 2
GROUP BY
CAL.calendar_date
Всегда есть 2 вещи, которые следует учитывать при оптимизации запросов:
несколько наблюдений:
Вы выполняете манипуляции дата, прежде чем присоединиться к своим датам. Как правило, это предотвратит оптимистирующее запрос на использование индекса, даже если он существует. Вы должны попытаться написать свои выражения таким образом, чтобы индексированные столбцы существуют неизменными на одной стороне выражения.
Ваши подзапросы фильтруют до того же диапазона дата, что и Generate_Series
. Это дублирование, и он ограничивает способность оптимизации выбрать наиболее эффективную оптимизацию. Я подозреваю, что, возможно, было написано для повышения производительности, потому что Оптимер не смог использовать индекс в столбце даты ( body_time
)?
Примечание : На самом деле мы бы очень хотели использовать Индекс в Body.body_time
порядок по
в подзаюте, находится в лучшем состоянии. В худшем случае он может заставить оптимизатор запроса сортировать набор результатов перед присоединением; И это не обязательно хорошо для плана запроса. Скорее только применяйте заказ прямо в конце для окончательного дисплея.
Использование левого присоединения
В ваших подзапрониках неуместно. Предполагая, что вы используете конвенции ANSI для NULL
поведения (и вы должны быть), любой внешний включения в конверт
вернется Envelope_command = NULL
, и эти, следовательно, будут исключены в условиях envelope_command =?
.
подзапросы o
и I
почти одинаковы сохраняются для значения envelope_command
. Это заставляет Optimser отсканировать одинаковые базовые таблицы дважды. Вы можете использовать методику таблицу Pivot для присоединения к данным один раз и разделить значения на 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
Редактировать : добавленный фильтр, предложенные TOM H.