У меня есть такой запрос, где объединяются ~ 6000 значений
SELECT DISTINCT ON(user_id)
user_id,
finished_at as last_deposit_date,
CASE When currency = 'RUB' Then amount_cents END as last_deposit_amount_cents
FROM payments
JOIN (VALUES (5),(22),(26)) --~6000 values
AS v(user_id) USING (user_id)
WHERE action = 'deposit'
AND success = 't'
AND currency IN ('RUB')
ORDER BY user_id, finished_at DESC
ПЛАН ЗАПРОСА для запроса со многими ЗНАЧЕНИЯМИ:
Unique (cost=444606.97..449760.44 rows=19276 width=24) (actual time=6129.403..6418.317 rows=5991 loops=1)
Buffers: shared hit=2386527, temp read=7807 written=7808
-> Sort (cost=444606.97..447183.71 rows=1030695 width=24) (actual time=6129.401..6295.457 rows=1877039 loops=1)
Sort Key: payments.user_id, payments.finished_at DESC
Sort Method: external merge Disk: 62456kB
Buffers: shared hit=2386527, temp read=7807 written=7808
-> Nested Loop (cost=0.43..341665.35 rows=1030695 width=24) (actual time=0.612..5085.376 rows=1877039 loops=1)
Buffers: shared hit=2386521
-> Values Scan on "*VALUES*" (cost=0.00..75.00 rows=6000 width=4) (actual time=0.002..4.507 rows=6000 loops=1)
-> Index Scan using index_payments_on_user_id on payments (cost=0.43..54.78 rows=172 width=28) (actual time=0.010..0.793 rows=313 loops=6000)
Index Cond: (user_id = "*VALUES*".column1)
Filter: (success AND ((action)::text = 'deposit'::text) AND ((currency)::text = 'RUB'::text))
Rows Removed by Filter: 85
Buffers: shared hit=2386521
Planning time: 5.886 ms
Execution time: 6429.685 ms
Я использую PosgreSQL 10.8.0.Есть ли шанс ускорить этот запрос?
Я пытался заменить DISTINCT на рекурсию:
WITH RECURSIVE t AS (
(SELECT min(user_id) AS user_id FROM payments)
UNION ALL
SELECT (SELECT min(user_id) FROM payments
WHERE user_id > t.user_id
) AS user_id FROM
t
WHERE t.user_id IS NOT NULL
)
SELECT payments.* FROM t
JOIN (VALUES (5),(22),(26)) --~6000 VALUES
AS v(user_id) USING (user_id)
, LATERAL (
SELECT user_id,
finished_at as last_deposit_date,
CASE When currency = 'RUB' Then amount_cents END as last_deposit_amount_cents FROM payments
WHERE payments.user_id=t.user_id
AND action = 'deposit'
AND success = 't'
AND currency IN ('RUB')
ORDER BY finished_at DESC LIMIT 1
) AS payments
WHERE t.user_id IS NOT NULL;
Но оказалось, что даже медленнее .
Hash Join (стоимость = 418.67..21807.22 строк = 3000 width = 24) (фактическое время = 16.804..10843.174 строк = 5991 циклов = 1) Hash Cond: (t.user_id = " VALUES". column1) Буферы: общее попадание = 6396763 CTE t -> Рекурсивный союз (стоимость = 0.46..53.73 строк = 101 ширина = 8) (фактическое время = 0.142..1942.351 строк = 237029 циклов = 1) Буферы: общее попадание= 864281 -> Результат (стоимость = 0.46..0.47 строк = 1 ширина = 8) (фактическое время = 0.141..0.142 строк = 1 петля = 1) Буферы: общий доступ = 4 InitPlan 3 (возвращает $ 1) -> Лимит (стоимость = 0.43..0.46 строк = 1 ширина = 8) (фактическое время = 0.138..0.139 строк = 1 петля = 1) Буферы: общее попадание = 4 -> Сканирование только по индексу с использованием index_payments_on_user_id для платежей платежей_2 (стоимость = 0,43 ..155102,74 строк = 4858092 ширины = 8) (фактическое время = 0,137..0.138 строк = 1 циклов = 1) Индекс Cond: (user_id IS NOT NULL) Выборки из кучи: 0 Буферы: общий доступ = 4 -> Сканирование WorkTable для t t_1 (стоимость = 0,00..5.12 строк = 10 по ширине = 8) (фактическое время = 0,008..0.008 строк = 1 циклов = 237029) Фильтр: (user_id IS NOT NULL) Строк, удаленных фильтром: 0 Буферы: общий доступ = 864277 Подплан 2-> Результат (стоимость = 0,48..0.49 строк = 1 ширина = 8) (фактическое время = 0.007..0.007 строк = 1 цикл = 237028) Буферы: общий доступ = 864277 InitPlan 1 (возвращает $ 3) -> Лимит (стоимость =0.43..0.48 строк = 1 ширина = 8) (фактическое время = 0,007..0.007 строк = 1 цикл = 237028) Буферы: общее попадание = 864277 -> Сканирование только по индексу с использованием index_payments_on_user_id для платежей платежей_1 (стоимость = 0,43..80786,25 строк= 1619364 ширина = 8) (фактическое время = 0,007..0.007 строк = 1 цикл = 237028) Индекс Cond: ((user_id IS NULL) И (user_id> t_1.user_id)) Выборки кучи: 46749 Буферы: общий доступ = 864277-> Вложенный цикл (стоимость = 214.94..21498.23 строки = 100 ширина = 32) (фактическое время = 0.475..10794.535 строк = 167333 цикла = 1) Буферы: общее попадание = 6396757 -> Сканирование CTE на t (стоимость = 0.00..2.02 строки = 100 ширина = 8) (фактическое время = 0.145..1998.788 строк = 237028 циклов = 1) Фильтр: (user_id IS NOT NULL) Строки, удаленные фильтром: 1 Buffers: общее попадание = 864281 -> предел (стоимость = 214.94..214.94 строк = 1 ширина = 24) (фактическое время = 0.037..0.037 строк = 1 цикл = 237028) Буферы: общее попадание = 5532476 -> сортировка (стоимость =214.94..215.37 строк = 172 ширины = 24) (фактическое время = 0.036..0.036 строк = 1 петля = 237028) Ключ сортировки: payment.finished_at DESC Метод сортировки: быстрая сортировка Память: 25 КБ Буферы: общий доступ = 5532476 -> Сканирование индексаиспользование index_payments_on_user_id для платежей (стоимость = 0.43..214.08 строк = 172 ширины = 24) (фактическое время = 0.003..0.034 строк = 15 циклов = 237028) Индекс Cond: (user_id = t.user_id) Фильтр: (success AND ((action) :: text = 'deposit' :: text) AND ((валюта) :: text = 'RUB' :: text)) Строк, удаленных фильтром: 6 буферов: общее попадание = 5532473 -> Хеш (стоимость = 75,00..75.00 строк = 6000 по ширине = 4) (фактическое время = 2.255..2.255 строк = 6000 циклов = 1) Контейнеры: 8192 Пакеты: 1 Использование памяти: 275 КБ-> Сканирование значений в « VALUES » (стоимость = 0,00..75,00 строк = 6000 ширины = 4) (фактическое время = 0,004..1,206 строк = 6000 циклов = 1) Время планирования: 7,029 мс Время выполнения: 10846,774 мс