#sql #postgresql #optimization
#sql #postgresql #оптимизация
Вопрос:
SELECT *
FROM trips,
dates
WHERE places_number_on_flag > 0
AND places_number > 0
AND ( places_number - ( (SELECT Count(id)
FROM resrv_customers
WHERE trip = trips.id
AND ow > 0)
(SELECT Count(id)
FROM resrv_customers
WHERE trip = trips.id
AND extra_seat = 1
AND ow > 0) ) < 20 )
AND dates.id = trips.trip_date
AND dates.from_date > 2458553;
Он возвращает 5 строк примерно через минуту и 50 секунд.
Я хочу, чтобы это было быстрее.
Комментарии:
1. Вы ознакомились с планом выполнения?
2. Пожалуйста, опубликуйте соответствующие инструкции
CREATE TABLE
иCREATE INDEX
, а также план. Вам также следует переработать запрос, используя явный синтаксис join для лучшей читаемости.3. @dodekja . . . Вы должны объяснить, какую логику реализует запрос.
4. Вредные привычки, от которых следует отказаться: использование объединений в старом стиле — этот стиль списка таблиц, разделенных запятыми , в старом стиле был заменен на правильный синтаксис ANSI
JOIN
в стандарте ANSI- 92 SQL ( 25 лет назад), и его использование не рекомендуется
Ответ №1:
Одной из оптимизаций было бы запустить подзапрос только один раз и использовать условную агрегацию для подсчета мест:
SELECT *
FROM trips
JOIN dates on dates.id = trips.trip_date
WHERE places_number_on_flag > 0
AND places_number > 0
AND ( places_number - ( (SELECT Count(id) count(id) filter (where extra_seat = 1)
FROM resrv_customers
WHERE trip = trips.id
AND ow > 0)) < 20 )
AND dates.from_date > 2458553;
Обратите внимание, что я заменил ваши древние и хрупкие неявные объединения в предложении WHERE на явный JOIN
оператор. Это не оказывает никакого влияния на производительность, это просто лучший стиль кодирования.
Комментарии:
1. Я бы добавил: используйте имена переменных вместо «*». И помните, Explain Analyse поможет вам узнать стоимость каждого шага.
Ответ №2:
Мы можем попробовать переписать ваш запрос, используя соединение с resrv_customers
таблицей, вместо использования дорогостоящих коррелированных подзапросов:
WITH cte AS (
SELECT
trip,
COUNT(CASE WHEN ow > 0 THEN 1 END) AS cnt1,
COUNT(CASE WHEN extra_seat = 1 AND ow > 0 THEN 1 END) AS cnt2
FROM resrv_customers
GROUP BY trip
)
SELECT *
FROM trips t
INNER JOIN dates d
ON t.trip_date = d.id
LEFT JOIN cte r
ON r.trip = t.id
WHERE
places_number > 0 AND
places_number - (r.cnt1 r.cnt2) < 20 AND
d.from_date > 2458553;
Индексирование здесь может быть сложным, потому что вы выполняете SELECT *
. Чтобы заставить Postgres использовать любой индекс, вам, возможно, придется выполнить большое покрытие столбцов, то есть создать большие индексы.
Комментарии:
1. Просто комментарий к оптимизации, что CTE, скорее всего, будет узким местом процесса, поскольку ему нужно будет сгруппировать и подсчитать всю
resrv_customers
таблицу, когда вам нужно только сопоставитьtrip.id
. Такжеow > 0
может быть в предложении where, поскольку этого требуют оба параметра, плюс использованиеfilter
предложения ( postgresql.org/docs/current / … ) обычно выполняется быстрее, чемcase
внутри count.
Ответ №3:
Попробуйте это:
with rc_count as
(
select trip, count(*) as ow_count, sum( (extra_seat = 1)::int ) as ow_count_with_extra_seat
from resrv_customers
where ow > 0
group by trip
)
SELECT *
FROM trips
join dates on dates.id = trips.trip_date
join rc_count rc on trips.id = trip
WHERE places_number_on_flag > 0
AND places_number > 0
AND dates.from_date > 2458553
and places_number - (ow_count ow_count_with_extra_seat) < 20
Комментарии:
1. Примечание: Этот ответ почти идентичен тому, что я опубликовал ранее.
2. @TimBiegeleisen этого не заметил. Я сосредоточился на написании собственного ответа, а также перенес общее условие
ow > 0
в предложение where