Как написать SQL-запрос для проверки уникальности id в таблице
Что проверяют
Умеете ли вы превратить требование «id уникален» в проверку, которую можно запустить и получить однозначный результат. Следом часто спрашивают, откуда вообще берутся дубли, если на колонке стоит первичный ключ, и как проверить уникальность по нескольким полям.
Как отвечать
Базовый запрос ищет дубли, а не доказывает уникальность:
SELECT id, COUNT(*) AS cnt
FROM orders
GROUP BY id
HAVING COUNT(*) > 1;Пустой результат означает, что дублей нет. Такой запрос удобно положить в автотест с проверкой «ноль строк». Быстрая сверка одной строкой: SELECT COUNT(*), COUNT(DISTINCT id) FROM orders, числа должны совпасть.
Что ещё показывает уровень тестировщика:
- сначала ограничение: если на
idестьPRIMARY KEYилиUNIQUE, база сама не пустит дубль. Проверьте это в схеме (\d ordersв psql илиinformation_schema.table_constraints), а запрос по данным нужен там, где ограничения нет: staging-таблицы, выгрузки, данные после миграции или ETL; - строки-дубли целиком:
ROW_NUMBER() OVER (PARTITION BY id ORDER BY created_at)покажет все копии, а не только значение ключа; - NULL:
COUNT(DISTINCT id)пропускаетNULL, аGROUP BYсобирает их в одну группу. ОбычныйUNIQUEв PostgreSQL допускает многоNULL; - составная уникальность:
GROUP BY user_id, product_id; - строковые ключи:
ABCиabcдля базы разные значения, при сомнениях сравнивайтеlower(trim(code)).
Отдельно стоит сказать про негативный тест: попытаться вставить дубль и убедиться, что приложение получает ошибку нарушения ограничения и показывает пользователю понятное сообщение, а не 500.
Где спотыкаются
Пишут SELECT DISTINCT id и смотрят глазами. На тысячах строк так дубль не найти, нужен запрос с однозначным ответом.
Забывают про NULL и получают расхождение между COUNT(*) и COUNT(DISTINCT id), которое принимают за дубли.
Гоняют тяжёлую группировку по большой таблице на рабочей базе в разгар дня. Для проверки подойдут реплика, тестовый стенд или фильтр по свежим данным.