Короткий ответ
Нужно пронумеровать строки по order_id с сортировкой по updated_at DESC через ROW_NUMBER(), оставить только строки с номером 1 (последнюю версию каждого заказа) в CTE, и уже на этом «чистом» наборе строить все дальнейшие агрегаты.
Как это работает подробнее
этот паттерн — частный случай дедупликации (аналогичный вопросу про удаление дублей), но применяется систематически как обязательный первый шаг перед любой агрегацией таких данных, а не разово; логика та же: PARTITION BY order_id ORDER BY updated_at DESC даёт rn = 1 для самой свежей версии.
Пример кода
question-67.sql
WITH latest AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY updated_at DESC) AS rn
FROM raw_orders_feed
)
SELECT status, COUNT(*) AS orders_cnt, SUM(amount) AS total_amount
FROM latest
WHERE rn = 1
GROUP BY status;Что использовать на практике
- Практика. важно убедиться, что
updated_atдействительно монотонно отражает «более новую» версию записи, а не время технической вставки строки в хранилище — если источник присылает исторические правки с запаздыванием, именно бизнес-метка времени обновления должна использоваться для сортировки, а не момент загрузки в хранилище. - Проверка. Проверка:
COUNT(DISTINCT order_id)наlatest WHERE rn = 1должен равнятьсяCOUNT(*)того же запроса — то есть ровно одна строка на заказ.
Подводные камни
- Частая ошибка. агрегируют данные напрямую, не дедуплицируя, из-за чего в финальных метриках учитываются сразу несколько версий одного заказа (например, старая версия со статусом «в обработке» и новая со статусом «доставлен»), что задваивает счётчики и искажает статусную разбивку.