Дедуплицировать таблицу, оставив одну строку на пользователя — самую свежую по updated_at
Таблица users_raw(user_id, updated_at, email, plan) содержит несколько строк на пользователя из-за повторных синхронизаций. Верните ровно одну строку на user_id — самую свежую по updated_at — сохранив все её столбцы.
-- оставьте одну строку на user_id: самую свежую по updated_at
Напишите запрос.
Ранжируют строки внутри пользователя и берут верхнюю: ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) AS rn, затем фильтр rn = 1. DISTINCT не выберет свежую; обычный GROUP BY user_id требует агрегат на каждый прочий столбец.
- ✗Ждать, что DISTINCT выберет свежую строку на пользователя
- ✗Считать, что MAX() тянет прочие столбцы из той же строки
- ✗Группировать по user_id, выбирая негруппированные столбцы
- →Как разрешить ничьи при равных updated_at?
- →Когда DISTINCT ON проще, чем ROW_NUMBER, здесь?
Number the rows within each user newest-first, then keep rank one. This returns the whole original row, not just the aggregated key.
WITH ranked AS (
SELECT users_raw.*,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) AS rn
FROM users_raw
)
SELECT user_id, updated_at, email, plan
FROM ranked
WHERE rn = 1;
In Postgres, DISTINCT ON is a shorter equivalent:
SELECT DISTINCT ON (user_id) user_id, updated_at, email, plan
FROM users_raw
ORDER BY user_id, updated_at DESC;
Why the alternatives fail: DISTINCT only removes rows that are identical across every selected column, so differing updated_at/email values keep the duplicates. GROUP BY user_id collapses each user to one row but then every other column must be wrapped in an aggregate — and MAX(updated_at) with a bare email does not guarantee they come from the same row.