Как вывести все дублирующиеся номера телефонов из таблицы с помощью SQL или ORM
Что проверяют
Умеете ли вы группировать и фильтровать агрегаты, а не выгружать таблицу в Python и считать там. Следом почти всегда просят вывести не сами номера, а все строки с дублями целиком, и спрашивают, что делать с номерами в разном формате.
Как отвечать
Базовый запрос: SELECT phone, COUNT(*) AS cnt FROM users WHERE phone IS NOT NULL GROUP BY phone HAVING COUNT(*) > 1;
Дальше покажите, что знаете варианты:
HAVINGпротивWHERE:WHEREфильтрует строки до группировки,HAVINGгруппы после, поэтому условие наCOUNT(*)пишется только вHAVING;- все строки с дублями: оконная функция
COUNT(*) OVER (PARTITION BY phone)в подзапросе и фильтр по ней снаружи, либоWHERE phone IN (...)с подзапросом наHAVING; - Django ORM:
User.objects.values("phone").annotate(cnt=Count("id")).filter(cnt__gt=1). Именноvalues()передannotate()даётGROUP BY phone, а фильтр по аннотации Django сам переносит вHAVING; - SQLAlchemy 2.0:
select(User.phone, func.count()).group_by(User.phone).having(func.count() > 1)черезsession.execute(). Старыйsession.query()работает, но в новом коде его уже не пишут; - нормализация:
+7 999 123-45-67и89991234567для базы разные строки. В PostgreSQL можно группировать поregexp_replace(phone, '\D', '', 'g')с заменой ведущей восьмёрки, но правильнее хранить номер сразу в едином формате, например E.164.
Закончите профилактикой: после чистки повесьте на нормализованный номер уникальный индекс, иначе дубли вернутся.
Где спотыкаются
Пишут WHERE COUNT(*) > 1. Агрегат в WHERE недопустим, запрос просто не выполнится.
Добавляют в SELECT колонку, которой нет в GROUP BY, например id. PostgreSQL на это отвечает ошибкой, а если id нужны, их собирают через array_agg(id) или берут оконной функцией.
Забывают про NULL: все пустые номера попадают в одну группу, и COUNT(*) показывает её как самый массовый «дубль». Отсюда фильтр phone IS NOT NULL в запросе выше.