Дубликаты в SQL чаще всего ищут через GROUP BY и HAVING COUNT(*) > 1. Но перед запросом нужно решить, что именно считать дублем. Для одного отчета дубль — это одинаковый email. Для другого — одинаковая пара «телефон и дата». Для третьего — полностью совпадающая строка во всех значимых столбцах
Если сразу искать «одинаковые строки» без определения правила, результат будет случайным. В базе почти всегда есть технический id, дата создания или служебное поле, из-за которых строки формально разные, хотя для бизнеса они выглядят как дубли
Найти дубли по одному полю
Пример: найти повторяющиеся email в таблице клиентов
SELECT
email,
COUNT(*) AS duplicate_count
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;
Результат:
Такой запрос показывает значения, которые повторяются, но не показывает все строки целиком. Для первичной проверки этого достаточно
Показать строки-дубли полностью
Чтобы вывести сами строки, можно использовать подзапрос:
SELECT *
FROM customers
WHERE email IN (
SELECT email
FROM customers
GROUP BY email
HAVING COUNT(*) > 1
)
ORDER BY email, id;
Теперь вы увидите все записи с повторяющимися email и сможете понять, какие из них настоящие дубли, а какие просто разные клиенты с общим адресом
Найти дубли по нескольким столбцам
Часто одного поля мало. Например, заказ может считаться дублем, если совпадают клиент, дата и сумма:
SELECT
customer_id,
order_date,
total_sum,
COUNT(*) AS duplicate_count
FROM orders
GROUP BY customer_id, order_date, total_sum
HAVING COUNT(*) > 1;
Чем точнее правило, тем меньше ложных совпадений. Но слишком жесткое правило может пропустить реальные дубли, если одно поле чуть отличается
Найти дубли через оконную функцию
Если нужно пометить повторные строки и оставить первую, удобно использовать ROW_NUMBER
WITH marked AS (
SELECT
id,
email,
created_at,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at
) AS rn
FROM customers
)
SELECT *
FROM marked
WHERE rn > 1;
Такой запрос показывает записи, которые считаются повторными внутри группы email. Первой остается самая ранняя запись по created_at
Как безопасно подходить к удалению дублей
Не удаляйте дубли сразу. Сначала:
- Найдите повторяющиеся значения
- Выведите полные строки
- Сохраните результат проверки
- Сделайте бэкап
- Уточните правило, какая строка остается
- Только потом пишите
DELETE
Для удаления часто используют CTE с ROW_NUMBER, но это уже отдельная операция. В учебном материале лучше сначала научиться видеть дубли, потому что ошибочный DELETE исправлять намного сложнее
Мини-практика
Создайте тестовую таблицу:
CREATE TABLE customers (
id int IDENTITY(1,1) PRIMARY KEY,
name nvarchar(100) NOT NULL,
email nvarchar(100) NOT NULL,
created_at date NOT NULL
);
INSERT INTO customers (name, email, created_at)
VALUES
(N'Анна', 'anna@example.com', '2026-01-10'),
(N'Анна копия', 'anna@example.com', '2026-01-12'),
(N'Илья', 'ilya@example.com', '2026-01-11');
Найдите повторяющиеся email:
SELECT email, COUNT(*) AS duplicate_count
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;
Потом выведите полные строки по этому email и решите, какую запись вы бы оставили
Частые ошибки
- Считают дублем всю строку вместе с
id, поэтому ничего не находят - Ищут только по одному полю, хотя для задачи нужна комбинация столбцов
- Удаляют дубли без бэкапа
- Не проверяют
NULL, пробелы и разный регистр букв - Не решают заранее, какая запись должна остаться
- Путают поиск дублей с проверкой уникального индекса
Что почитать дальше по SQL
Если нужен общий маршрут по теме, откройте рубрику SQL. Для соседних задач пригодятся эти разборы:
- GROUP BY и HAVING: группировка без каши
- SQL-запросы для начинающих: как устроены SELECT, WHERE, JOIN и GROUP BY
- Как выбрать 10 строк в SQL: TOP, LIMIT и FETCH
- Как запустить SQL Server: служба, SSMS и проверка подключения



