🔥 Live Coding: пользователи с оплаченной суммой > 100000 за 30 дней

Задача: Есть 2 таблицы:

orders • order_id • user_id • order_time • amount

payments • payment_id • order_id • payment_time • status

Нужно найти пользователей, у которых сумма оплаченных заказов за последние 30 дней больше 100000.

Важно: Заказ считаем оплаченным, если по нему есть хотя бы один payment со status = 'success' ✅

На выходе хотим получить:

• user_id • total_paid_amount

Решение:

with paid_orders as ( select distinct order_id from payments where status = 'success' ),

orders_30d as ( select order_id, user_id, amount from orders where order_time >= now() - interval 30 day )

select o.user_id, sum(o.amount) as total_paid_amount from orders_30d o inner join paid_orders p on o.order_id = p.order_id group by o.user_id having sum(o.amount) > 100000

🧠 Как здесь думаем:

Сначала отдельно определяем оплаченные заказы.

Почему через отдельный CTE? Потому что в payments у одного заказа может быть несколько записей. Например: • одна неуспешная • потом успешная • или даже несколько success

Нам нужно не количество платежей, а сам факт: есть success или нет.

Поэтому в paid_orders берем distinct order_id, где status = 'success'.

Дальше: • отбираем заказы только за последние 30 дней • джойним их с оплаченных заказами • суммируем amount по user_id • оставляем только тех, у кого сумма > 100000

⚠️ Где можно ошибиться:

Сделать обычный join сразу на payments без distinct

Тогда один и тот же заказ может продублироваться несколько раз, если у него несколько успешных платежей. В результате сумма будет завышена ❌

Фильтровать 30 дней по payment_time, а не по order_time

В условии задачи сказано: сумма оплаченных заказов за последние 30 дней

Обычно это читается как заказы, созданные за последние 30 дней. Поэтому фильтр здесь ставим по order_time.

Использовать where вместо having

where работает до агрегации, а нам нужно фильтровать уже посчитанную сумму по пользователю. Поэтому здесь нужен having.

💡 Альтернативный вариант:

Можно решить и через exists. Это тоже хороший и безопасный способ, когда нужно проверить сам факт успешной оплаты.

Например так:

select o.user_id, sum(o.amount) as total_paid_amount from orders o where o.order_time >= now() - interval 30 day and exists ( select 1 from payments p where p.order_id = o.order_id and p.status = 'success' ) group by o.user_id having sum(o.amount) > 100000

Этот вариант часто даже читается проще: для каждого заказа просто проверяем, есть ли успешный платеж.

🎯 Вывод:

Это типовая задача, где важно не просто сделать join, а правильно определить бизнес-смысл оплаченного заказа.

Ключевая мысль: если нужно проверить факт существования события, очень часто лучше думать через: • distinct • exists а не через прямой join на сырые события.

Так запрос получается и корректнее, и безопаснее 👌

🔥 Live Coding: пользователи с оплаченной суммой > 100000 за 30 дней
Задача:
Есть 2 таблицы:
orders
• orderid
• userid
• ordertime
• amount
payments
• paymentid
• orderid
• paymenttime
• status
Нужно н... | Сетка — социальная сеть от hh.ru