MySQL дубликаты строк - PullRequest
       9

MySQL дубликаты строк

4 голосов
/ 20 марта 2009

У меня есть таблица с некоторой повторяющейся информацией: идентификатор, имя, фамилия, рождение, PersonalKey, личная информация, направление, источник.

Где источник сообщает мне, откуда информация.

Повторяющаяся информация имеет уникальный идентификатор, и мне нужно стереть дублированную информацию. Но , у меня есть приоритет над некоторой исходной информацией, которая должна быть той, которая остается, а другая стирается.

Другое дело, что в другой информации об Источнике есть некоторая информация, которой нет у той, которую я хочу оставить, поэтому мне нужно пополнить PersonalKey до той, которая будет оставаться, и стереть повторяющиеся.

Стол с именем Pruebas

---Id, Name, Firstname, Lastname, Birth, RFC, Source, PersonalKey---
---2,Juan,Garcia,Escobeddo,1983-08-04,GAED87393, DRV484930, 34233--
---3,Juan,Garcia,Escobedo,1987-08-04,GAED87393, FIN484930, --
---4,Juan,Garcia,Escobedo,1987-08-04,GAED87393, SA484930, --

Как видите:

  • Идентификаторы уникальны
  • Имя, имя и фамилия повторяются
  • У идентификатора 2 есть значение PersonalKey, но у 3 и 4 нет
    • Я хочу, чтобы тот, у которого источник 'FIN%' остался, а остальные были стерты, но сначала мне нужно убедиться, что оставшаяся строка получает значение PersonalKey (IOW, я не хочу терять значение PersonalKey ).

Заранее спасибо.

Ответы [ 4 ]

3 голосов
/ 21 марта 2009

Самое простое решение, которое я могу придумать, - это скопировать PersonalKey в другие повторяющиеся строки, а затем удалить все строки, которые не соответствуют 'FIN%'.

UPDATE Pruebas p1 JOIN Pruebas p2
 ON (SOUNDEX(CONCAT(p1.Name, p2.Firstname, p3.Lastname)) 
   = SOUNDEX(CONCAT(p2.Name, p2.Firstname, p2.Lastname)))
SET p1.PersonalKey = p2.PersonalKey
WHERE p2.PersonalKey IS NOT NULL;

DELETE FROM Pruebas WHERE Source NOT LIKE 'FIN%';

Я показываю примерное выражение соответствия для объединения, используя SOUNDEX().


Я вижу из других оставленных вами комментариев, что у вас много вариаций и неопределенности. В этом случае нет способа автоматизировать очистку и дедупликацию, или, по крайней мере, автоматическая очистка будет сложнее и сложнее, чем сделать это вручную.


Повторяйте ваш комментарий о том, что запрос занимает много часов: да, он действительно не должен быть эффективным. Выражение JOIN не является sargable , то есть оно не может использовать индекс. Вы можете сделать его более эффективным, добавив дополнительный столбец для физического хранения значения SOUNDEX() имени, имени, фамилии. Затем создайте индекс для этого столбца.

Но SOUNDEX() не гарантирует, что найдет все возможные ошибки в любом случае. Перед вами стоит задача очистки данных, которая не может быть полностью автоматизирована. Любое решение для очистки данных требует ручной работы.

3 голосов
/ 20 марта 2009

Я бы запустил курсор (с языком программирования MySQL SP, Java, Python, .NET) по этому запросу:

select Name, Firstname, Lastname, count(1)
  from Pruebas
 group by Name, Firstname, Lastname
having count(1) > 1

Затем на возвращаемых строках курсора просто сделайте все, что вам нужно: проверьте наличие экземпляра FIN%, проверьте наличие PersonalKey и обновите соответствующим образом.

Для каждой строки курсора вы можете открыть свой курсор с помощью:

select *
  from Pruebas
 where Name = the_Name
   and Firstname = the_Firstname
   and Lastname = the_Lastname

И теперь у вас будет внутренний курсор со всеми строками, которые вы будете изменять. Если это то, что вам нужно, сохраните его и обновите его указанным вами значением KEY. В противном случае удалите его.

В Oracle вы можете достичь желаемого за один запрос, но я не думаю, что таким образом вы получите ту же производительность, что и при таком подходе.

Надеюсь, это поможет.

2 голосов
/ 20 марта 2009

Я бы сделал что-то вроде этого:

CREATE TABLE Pruebas_new
ВЫБРАТЬ * ИЗ Pruebas
GROUP BY имя, имя, фамилия
с источником типа 'FIN%';

Его можно переписать во временную таблицу и переписать то, что находится в исходной таблице, если вам нужно, чтобы она была быстрее, но это позволяет получить необходимые данные самым простым способом.

1 голос
/ 23 марта 2009

Извините за задержку с ответом. Я был немного занят последние пару дней.

Ниже приведен мой ответ, основанный на следующих предположениях:

1) Вы будете устранять проблемы с написанием имен с помощью какого-либо другого механизма (вы упомянули, что вы будете очищать его с помощью регулярных выражений в комментарии к исходному вопросу).

2) Набор DUP может быть идентифицирован с использованием имени, фамилии и рождения (вы упомянули это в комментарии к исходному вопросу).

3) Имя, фамилия и рождение не могут быть NULL.

4) Вы не можете иметь более одной записи FIN в наборе DUP (вы упомянули это в комментарии к исходному вопросу).

Если какое-либо из вышеперечисленных предположений неверно, мой ответ необходимо будет изменить.

Ниже приведены шаги, которые необходимо предпринять:

1) Обновите все записи FIN, чтобы скопировать PersonalKey из записи не-FIN:

    UPDATE Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
       SET p1.PersonalKey = p2.PersonalKey
     WHERE p1.Source like 'FIN%'
       AND p1.PersonalKey is null
       AND p2.PersonalKey is not null;

2) Удалить все не-FIN записи, где у нас есть FIN-запись:

    DELETE p2
      FROM Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
     WHERE p1.Source like 'FIN%'
       AND p2.Source not like 'FIN%';

На этом этапе все DUP с записью FIN были очищены, так что осталась только запись FIN.

3) Если мы решим остаться с записью DRV для всех остальных случаев. Нам нужно будет скопировать PersonalKey из другой записи в запись DRV:

    UPDATE Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
       SET p1.PersonalKey = p2.PersonalKey
     WHERE p1.Source like 'DRV%'
       AND p1.PersonalKey is null
       AND p2.PersonalKey is not null;

4) Удалить все записи без DRV, где у нас есть запись DRV:

    DELETE p2
      FROM Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
     WHERE p1.Source like 'DRV%'
       AND p2.Source not like 'DRV%';

В этот момент все DUP с записью DRV были очищены, так что осталась только запись DRV.

Если единственным другим типом записи является запись SA, то больше не должно быть DUP, и мы закончили.

5) Если мы хотим выбрать запись с наибольшим количеством заполненной информации, или если мы закончили 3 и 4, и остается более одного типа записи, вызывающей DUP. Нам нужно скопировать PersonalKey из любой записи в наборе DUP, которая имеет его, в любую запись, которая не имеет его для всех не-FIN записей:

    UPDATE Pruebas p1
INNER JOIN Pruebas p2
        ON p1.Firstname = p2.Firstname
       AND p1.Lastname = p2.Lastname
       AND p1.Birth = p2.Birth
       SET p1.PersonalKey = p2.PersonalKey
     WHERE p1.Source not like 'FIN%'
       AND p1.PersonalKey is null
       AND p2.PersonalKey is not null;

6) Удалить все записи, кроме той, которая содержит наибольшее количество информации (как определено в вычисляемом столбце info_score):

    DELETE p5
      FROM Pruebas p5
INNER JOIN (SELECT p3.Firstname
                 , p3.Lastname
                 , p3.Birth
                 , MIN(p3.Id) AS min_id
              FROM Pruebas p3
        INNER JOIN (SELECT p1.Firstname
                         , p1.Lastname
                         , p1.Birth
                         , count(*) AS c
                         , MAX((p1.Name is not null) + (p1.RFC is not null) + (p1.Source is not null) + (p1.PersonalKey is not null)) AS info_score
                      FROM Pruebas p1
                  GROUP BY p1.Firstname
                         , p1.Lastname
                         , p1.Birth 
                    HAVING count(*) > 1) p2
                ON p3.Firstname = p2.Firstname
               AND p3.Lastname = p2.Lastname
               AND p3.Birth = p2.Birth
               AND ((p3.Name is not null) + (p3.RFC is not null) + (p3.Source is not null) + (p3.PersonalKey is not null)) = p2.info_score
          GROUP BY p3.Firstname
                 , p3.Lastname
                 , p3.Birth) p4
        ON p4.Firstname = p5.Firstname
       AND p4.Lastname = p5.Lastname
       AND p4.Birth = p5.Birth
       AND p4.min_id <> p5.Id;

В этот момент все DUP были свернуты с сохранением PersonalKey, если он был доступен, и записями FIN были сохранены, если они существовали, в противном случае была сохранена либо запись DRV, либо запись с наибольшим количеством информации.

Дайте мне знать, если у вас есть какие-либо вопросы по любому из перечисленных вопросов.

Надеюсь, это поможет,

-Dipin

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...