Тема дубликатов в SQL почти всегда всплывает в аналитике. Иногда нужно просто убрать повторы из результата запроса. Иногда — найти дубли в таблице. А иногда — физически удалить лишние строки и оставить одну правильную.
В этой статье разберем основные способы:
DISTINCTGROUP BYUNIONROW_NUMBER()- удаление дублей из таблицы
Сначала важный вопрос: что именно считать дубликатом
Это самая частая логическая ошибка.
Например, у тебя есть строки:
1, Иван, Москва
1, Иван, Москва
1, Иван, СПб
Что здесь дубликат?
- если сравниваем всю строку, то дублируется только первая и вторая
- если сравниваем только
id, то дубликатов уже три записи на одинid
Поэтому перед удалением дублей всегда надо ответить на вопрос:
- дубликат по всей строке?
- по одному полю?
- по набору полей?
Способ 1. DISTINCT
Самый простой способ убрать одинаковые строки в результате запроса:
SELECT DISTINCT
customer_id
FROM orders
Или по нескольким столбцам:
SELECT DISTINCT
customer_id,
product_id
FROM orders
DISTINCT не удаляет данные из таблицы. Он просто убирает повторы из результата.
Когда подходит
- нужен список уникальных значений
- нужен уникальный набор комбинаций столбцов
- надо быстро очистить результат выборки
Ограничение
Если тебе нужно оставить “одну лучшую строку” из нескольких дублей, DISTINCT не подойдет. Он не умеет выбирать по приоритету.
Способ 2. GROUP BY
GROUP BY тоже может убрать дубли, если тебе нужен один результат на группу.
SELECT
customer_id
FROM orders
GROUP BY customer_id
По сути, для таких случаев это аналог DISTINCT.
Но GROUP BY удобнее, если ты одновременно хочешь что-то посчитать:
SELECT
customer_id,
COUNT(*) AS orders_cnt
FROM orders
GROUP BY customer_id
Когда подходит
- нужно убрать повторы и сразу посчитать количество
- нужно увидеть, где именно есть дубли
Например, найти повторяющиеся email:
SELECT
email,
COUNT(*) AS cnt
FROM users
GROUP BY email
HAVING COUNT(*) > 1
Способ 3. UNION вместо UNION ALL
Когда ты объединяешь результаты нескольких запросов, тоже можно случайно получить дубли.
SELECT email FROM leads
UNION
SELECT email FROM customers
UNION автоматически убирает дубликаты.
Если написать UNION ALL, все строки сохранятся:
SELECT email FROM leads
UNION ALL
SELECT email FROM customers
Когда подходит
- при объединении нескольких источников
- когда нужен единый уникальный список
Важный нюанс
UNION убирает дубли по всей строке, а не по одному столбцу.
Способ 4. ROW_NUMBER() — лучший способ выбрать одну строку из дублей
Если у тебя несколько похожих строк и нужно оставить только одну “правильную”, чаще всего используют ROW_NUMBER().
Например, оставить самую свежую запись по client_id:
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY client_id
ORDER BY updated_at DESC
) AS rn
FROM client_table
) t
WHERE rn = 1
Что происходит:
- строки разбиваются по
client_id - внутри каждой группы сортируются по
updated_at - самой свежей строке присваивается
rn = 1 - остальные строки можно отбросить
Почему это очень полезно
Потому что в реальной жизни дубли часто не полностью одинаковы. У них может отличаться:
- дата обновления
- источник данных
- статус
- приоритет
И тогда нужно не просто убрать повтор, а осознанно выбрать одну запись.
Способ 5. Удаление дублей из таблицы
Если нужно не только выбрать уникальные строки, но и реально удалить лишние записи, часто используют CTE или подзапрос с ROW_NUMBER().
Пример логики:
WITH ranked AS (
SELECT
id,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at DESC
) AS rn
FROM users
)
DELETE FROM users
WHERE id IN (
SELECT id
FROM ranked
WHERE rn > 1
)
Идея такая:
- оставляем одну строку на каждый
email - все остальные удаляем
Очень важная осторожность
Перед таким удалением всегда сначала проверь выборкой:
WITH ranked AS (
SELECT
id,
email,
created_at,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at DESC
) AS rn
FROM users
)
SELECT *
FROM ranked
WHERE rn > 1
Сначала убедись, что удаляться будут именно те строки, которые ты считаешь лишними.
Какой способ когда использовать
Вот удобная шпаргалка:
- нужен список уникальных строк ->
DISTINCT - нужно посчитать дубли ->
GROUP BY ... HAVING COUNT(*) > 1 - нужно объединить источники без повторов ->
UNION - нужно выбрать одну строку из дублей по правилу ->
ROW_NUMBER() - нужно физически удалить лишние записи ->
DELETEчерезROW_NUMBER()
Частая ошибка №1: DISTINCT вместо нормальной логики выбора
Иногда человек пишет:
SELECT DISTINCT *
FROM table_name
Но это не решает задачу, если строки отличаются хотя бы одним полем.
Например, два заказа одного клиента с разным временем — это уже не одинаковые строки. DISTINCT их не уберет.
Частая ошибка №2: удалять дубли без ORDER BY
Если используешь ROW_NUMBER(), обязательно подумай, какая строка должна остаться:
- самая новая?
- самая старая?
- с максимальным приоритетом?
- с ненулевым статусом?
Без продуманного ORDER BY ты удалишь строки случайным образом.
Частая ошибка №3: сначала удалять, потом проверять
Сначала всегда:
- напиши
SELECT - убедись, что нашел именно лишние строки
- только потом делай
DELETE
Это правило спасает много нервов.
Итог
Универсального “одного способа удалить дубликаты в SQL” не существует, потому что задачи бывают разные.
DISTINCT— для уникального результатаGROUP BY— для анализа и подсчета дублейUNION— для объединения без повторовROW_NUMBER()— для выбора одной строки по правилуDELETEчерез ранжирование — для физической очистки таблицы
Если нужно просто убрать повторы из вывода — начни с DISTINCT.
Если нужно навести порядок в реальных данных — почти всегда смотри в сторону ROW_NUMBER().