@sergey_privacy
Админ со стажем, начинающий DevOps

Как удалить дубликаты в mysql с учетом значения по полю?

Добрый день!
Есть таблица по сотрудникам в базе данных:
Id, FIO, Phone
Типа такой:
1, Иванов Иван Иванович, 9999999999
2, Иванов Иван Иванович,
3, Петров Петр Петрович,
4, Петров Петр Петрович,
5, Сидоров Сидор Сидорович, 7777777777
6, Сидоров Сидор Сидорович, 7777777777
Внезапно оказалось, что многие ФИО задублированы. При этом у одних записей есть контактный телефон, у других нет.
Мне нужно найти и удалить все дубли (знаю как). Но при этом должна учитываться логика:
1. если у одной записи есть номер телефона, то удаляем тот дубль, у которого нет телефона.
2. Если телефонов нет ни у одной записи, то удаляем любую.
3. Если телефоны есть у обоих записей, то удаляем любую.
  • Вопрос задан
  • 723 просмотра
Пригласить эксперта
Ответы на вопрос 5
firedragon
@firedragon
Не джун-мидл-сеньор, а трус-балбес-бывалый.
DELETE FROM phones
 WHERE Id NOT IN
(
 SELECT MIN(Id)
FROM phones
	where Phone <> ''
    GROUP BY FIO 
 );
Ответ написан
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
Изначально, структура БД - некорректна.
Нужно, чтобы к одному ФИО можно было присвоить несколько контактов, различимых по типу.
Один из них - номер телефона.
И проблемы бы не было.
Переделайте структуру, если ещё не поздно.
Ответ написан
@mletov
-- Пишем телефоны в пустые значения
UPDATE table AS t1
SET t1.Phone=t2.Phone
FROM table AS t1
INNER JOIN table AS t2
ON t1.FIO=t2.FIO
AND t1.phone IS NULL
AND t2.phone IS NOT NULL 

-- Удаляем лишнее
DELETE 
FROM table
WHERE Id NOT IN
(
	SELECT Id
	FROM
	(
		SELECT MAX(Id) AS id, FIO, Phone
		FROM table
		GROUP BY FIO,
				   Phone
	) AS t1
)


PS Отталкиваемся от допущения, что не может быть людей с одинаковыми ФИО
PPS Можно еще упростить, убрав одну обертку
Ответ написан
Комментировать
@d-stream
Готовые решения - не подаю, но...
На самом деле вот так вот "в лоб" удалять дублей, ибо не исключено что по ним уже были в системе какие-либо дейсвия. Соответственно удаления должно предшествовать "слияние" в виде замены в отражающих какие-либо операции id удаляемого контрагента на id с кем он сливается.
Ну и само выявление дублей - я бы не стал вот так однозначно отдавать это скрипту. Потому что действительно Васи́лий Алибаба́евич Али-Баба́ в двух записях - скорее всего дубль, а вот Кузнецов Иван Сергеевич - ну совсем не факт.

Но если базу не жалко... то можно просто вначале отобрать фио-клоны (group by fio having count(*) > 1) и потом их использовать для отбора данных для удаления задав обратный ордеринг в соответствии с критериями "информативности" (например наличие и длина телефона, дата создания и т.п.) и отобрав оттуда top 1 (limit 1)
[правда в таком раскладе из троиц будут удалены только по одному... но можно вывернуть запрос мехом внутрь и использовать его как инверсию - не удалять top 1]
Ответ написан
Комментировать
solotony
@solotony
покоряю пик Балмера
Собственно задача раскладывется на 2:
1) выбрать "основные уникальные" записи
2) удалить записи, не попадающие в множество "основных уникальных"

2-я задача тривиальна

1-я задача довольно просто решается в МySQL 8 при помощи "оконных функций"
https://dev.mysql.com/doc/refman/8.0/en/window-fun...

в mySQL более ранних версий придется поизвращаться:

1) сделать уникальную выборку имен, телефонов имеющих телефон
2) сделать уникальную выборку имен не имеющих телефона и не попадающих в первую выборку
3) объединить их

вот пример такого запроса

SELECT DISTINCT surname, name_ FROM `sdtm_users_items` WHERE (patro='' OR patro IS NULL) AND (surname, name_) NOT IN (SELECT DISTINCT surname, name_ FROM `sdtm_users_items` WHERE patro<>'' AND patro IS NOT NULL) UNION SELECT DISTINCT surname, name_ FROM `sdtm_users_items` WHERE patro<>'' AND patro IS NOT NULL ORDER BY surname, name_
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы