web-dev-qa-db-fra.com

MySQL supprime les doublons mais conserve les dernières

J'ai des champs uniques id et email. Les e-mails sont dupliqués. Je veux seulement conserver une adresse électronique de tous les doublons, mais avec la dernière id (le dernier enregistrement inséré).

Comment puis-je atteindre cet objectif?

22
Khuram

Imaginez que votre table test contienne les données suivantes:

  select id, email
    from test;

ID                     EMAIL                
---------------------- -------------------- 
1                      aaa                  
2                      bbb                  
3                      ccc                  
4                      bbb                  
5                      ddd                  
6                      eee                  
7                      aaa                  
8                      aaa                  
9                      eee 

Nous devons donc rechercher tous les courriels répétés et les supprimer tous, mais le dernier identifiant.
Dans ce cas, aaa, bbb et eee sont répétés, nous voulons donc supprimer les identifiants 1, 7, 2 et 6.

Pour ce faire, nous devons d’abord trouver tous les courriels répétés:

      select email 
        from test
       group by email
      having count(*) > 1;

EMAIL                
-------------------- 
aaa                  
bbb                  
eee  

Ensuite, à partir de cet ensemble de données, nous devons trouver le dernier identifiant pour chacun de ces courriels répétés:

  select max(id) as lastId, email
    from test
   where email in (
              select email 
                from test
               group by email
              having count(*) > 1
       )
   group by email;

LASTID                 EMAIL                
---------------------- -------------------- 
8                      aaa                  
4                      bbb                  
9                      eee                                 

Enfin, nous pouvons maintenant supprimer tous ces courriels avec un identifiant plus petit que LASTID. La solution est donc:

delete test
  from test
 inner join (
  select max(id) as lastId, email
    from test
   where email in (
              select email 
                from test
               group by email
              having count(*) > 1
       )
   group by email
) duplic on duplic.email = test.email
 where test.id < duplic.lastId;

MySql n'est pas installé sur cette machine pour le moment, mais devrait fonctionner

Mettre à jour

La suppression ci-dessus fonctionne, mais j'ai trouvé une version plus optimisée:

 delete test
   from test
  inner join (
     select max(id) as lastId, email
       from test
      group by email
     having count(*) > 1) duplic on duplic.email = test.email
  where test.id < duplic.lastId;

Vous pouvez voir que cela supprime les doublons les plus anciens, à savoir 1, 7, 2, 6:

select * from test;
+----+-------+
| id | email |
+----+-------+
|  3 | ccc   |
|  4 | bbb   |
|  5 | ddd   |
|  8 | aaa   |
|  9 | eee   |
+----+-------+

Une autre version, la suppression proposée par Rene Limon

delete from test
 where id not in (
    select max(id)
      from test
     group by email)
71
Jose Rui Santos

Manière correcte est 

DELETE FROM `tablename` 
  WHERE id NOT IN (
    SELECT * FROM (
      SELECT MAX(id) FROM tablename 
        GROUP BY name
    ) 
  )
8
Gaurav Kandpal

Essayez cette méthode

DELETE t1 FROM test t1, test t2 
WHERE t1.id > t2.id AND t1.email = t2.email
4
Pulkit Malhotra

Je dois dire que la version optimisée est un code doux et élégant qui fonctionne à merveille même lorsque la comparaison est effectuée sur une colonne DATETIME. C'est ce que j'ai utilisé dans mon script, dans lequel je cherchais la dernière date de fin de contrat pour chaque identifiant d'employé:

DELETE CurrentContractData
  FROM CurrentContractData
  INNER JOIN (
    SELECT
      EmployeeID,
      PeriodofPerformanceStartDate,
      max(PeriodofPerformanceEndDate) as lastDate,
      ContractID
    FROM CurrentContractData
    GROUP BY EmployeeID
    HAVING COUNT(*) > 1) Duplicate on Duplicate.EmployeeID = CurrentContractData.EmployeeID
    WHERE CurrentContractData.PeriodofPerformanceEndDate < Duplicate.lastDate;

Merci beaucoup!

0
Michael Sheaver