Comment trouver des doublons dans 2 colonnes et non 1

107

J'ai une table de base de données MySQL avec deux colonnes qui m'intéressent. Individuellement, ils peuvent chacun avoir des doublons, mais ils ne devraient jamais avoir un double des DEUX d'entre eux ayant la même valeur.

stone_idpeut avoir des doublons tant que pour chaque upshargetitre est différent, et inversement. Mais disons par exemple stone_id= 412 et upcharge_title= "saphir" que la combinaison ne devrait se produire qu'une seule fois.

C'est acceptable:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "ruby"

Ce n'est PAS ok:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "sapphire"

Existe-t-il une requête qui trouvera des doublons dans les deux champs? Et si possible, y a-t-il un moyen de configurer ma base de données pour ne pas permettre cela?

J'utilise MySQL version 4.1.22

JD Isaacks
la source

Réponses:

192

Vous devez configurer une clé composite entre les deux champs. Cela nécessitera un stone_id et un upcharge_title uniques pour chaque ligne.

En ce qui concerne la recherche des doublons existants, essayez ceci:

select   stone_id,
         upcharge_title,
         count(*)
from     your_table
group by stone_id,
         upcharge_title
having   count(*) > 1
Codeur Miyagi
la source
Merci, cela les sélectionne. Pourriez-vous avoir la gentillesse de me dire comment supprimer les doublons (mais laissez-en 1 copie bien sûr) MERCI !!
JD Isaacks
2
Une façon serait de récupérer toutes les données distinctes et de recréer la table.
Miyagi Coder
1
@John Isaacks: S'il n'y a pas d'autres champs avec lesquels vous pourriez les distinguer (c'est-à-dire que tous les champs sont des doublons), alors vous devrez supprimer les deux lignes et en recréer une. Une façon serait de copier les doublons dans une copie de la table, de les supprimer de l'original et de réinsérer des lignes distinctes de la copie.
P Daddy
Cela ne fonctionne pas sur postgres 8.1, quelqu'un pourrait-il me donner un coup de main?
Lennon
grand merci, l'ordre que vous groupez par matière?
Andrew
35

J'ai trouvé utile d'ajouter un index unqiue en utilisant un "ALTER IGNORE" qui supprime les doublons et applique des enregistrements uniques qui sonne comme vous voudriez le faire. La syntaxe serait donc:

ALTER IGNORE TABLE `table` ADD UNIQUE INDEX(`id`, `another_id`, `one_more_id`);

Cela ajoute effectivement la contrainte unique, ce qui signifie que vous n'aurez jamais d'enregistrements en double et que l'IGNORE supprime les doublons existants.

Vous pouvez en savoir plus sur ALTER IGNORE ici: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

Mise à jour: j'ai été informé par @Inquisitive que cela peut échouer dans les versions de MySql> 5.5:

Il échoue sur MySQL> 5.5 et sur la table InnoDB, et dans Percona en raison de leur fonction de création d'index rapide InnoDB [ http://bugs.mysql.com/bug.php?id=40344] . Dans ce cas, exécutez d'abord set session old_alter_table=1, puis la commande ci-dessus fonctionnera correctement

Mise à jour - ALTER IGNORESupprimé dans 5.7

À partir des documents

Depuis MySQL 5.6.17, la clause IGNORE est obsolète et son utilisation génère un avertissement. IGNORE est supprimé dans MySQL 5.7.

L'un des développeurs MySQL propose deux alternatives :

  • Regrouper par les champs uniques et supprimer comme indiqué ci-dessus
  • Créez une nouvelle table, ajoutez un index unique, utilisez INSERT IGNORE, ex:
CREATE TABLE duplicate_row_table LIKE regular_row_table;
ALTER TABLE duplicate_row_table ADD UNIQUE INDEX (id, another_id);
INSERT IGNORE INTO duplicate_row_table SELECT * FROM regular_row_table;
DROP TABLE regular_row_table;
RENAME TABLE duplicate_row_table TO regular_row_table;

Mais selon la taille de votre table, cela peut ne pas être pratique

SeanDowney
la source
1
C'est vrai, mais au moins pour la prochaine fois que tu sais. J'ai eu le même problème et j'ai pensé qu'il était bon de partager avec les autres
SeanDowney
Je plaisantais seulement à propos du retard de 3 ans. Je suis vraiment content que vous ayez partagé. D'où le plus 1.
JD Isaacks
J'imagine que cela supprime arbitrairement l'un des doublons, alors assurez-vous qu'il n'y a pas de données différentes entre chaque ligne qui pourraient être utiles à connaître ou à conserver.
Joshua Pinter
+1 pour la réponse même après 2 ans de retard. J'ai accidentellement supprimé une clé composite et cela m'a sauvé la vie. Merci
ivcode
J'ai essayé quelques techniques de recherche de duplication et aucune d'entre elles n'était aussi simple et rapide. Merci de partager cette méthode.
Kristjan O.29
8

Vous pouvez trouver des doublons comme celui-ci.

Select
    stone_id, upcharge_title, count(*)
from 
    particulartable
group by 
    stone_id, upcharge_title
having 
    count(*) > 1
Jason Punyon
la source
4

Pour trouver les doublons:

select stone_id, upcharge_title from tablename group by stone_id, upcharge_title having count(*)>1

Pour éviter cela à l'avenir, créez une clé unique composite sur ces deux champs.

Ian Nelson
la source
1
Merci beaucoup, pouvez-vous me dire comment supprimer tous les doublons sauf un. Et comment configurer une clé compisite dans phpmyadmin. MERCI!!!
JD Isaacks
3

Incidemment, une contrainte unique composite sur la table empêcherait cela de se produire en premier lieu.

ALTER TABLE table
    ADD UNIQUE(stone_id, charge_title)

(Ceci est T-SQL valide. Je ne suis pas sûr de MySQL.)

Papa
la source
1
Je pense que cela fonctionne, mais cela ne me laissera pas le faire tant que je n'aurai pas supprimé les doublons en premier. Merci.
JD Isaacks
1

ce post SO m'a aidé, mais moi aussi je voulais savoir comment supprimer et conserver l'une des lignes ... voici une solution PHP pour supprimer les lignes en double et en conserver une (dans mon cas, il n'y avait que 2 colonnes et c'est dans un pour effacer les associations de catégories en double)

$dupes = $db->query('select *, count(*) as NUM_DUPES from PRODUCT_CATEGORY_PRODUCT group by fkPRODUCT_CATEGORY_ID, fkPRODUCT_ID having count(*) > 1');
if (!is_array($dupes))
    return true;
foreach ($dupes as $dupe) {
    $db->query('delete from PRODUCT_CATEGORY_PRODUCT where fkPRODUCT_ID = ' . $dupe['fkPRODUCT_ID'] . ' and fkPRODUCT_CATEGORY_ID = ' . $dupe['fkPRODUCT_CATEGORY_ID'] . ' limit ' . ($dupe['NUM_DUPES'] - 1);
}

la (limite NUM_DUPES - 1) est ce qui préserve la seule ligne ...

Merci a tous

groovenectar
la source
3
ALTER IGNORE TABLE table ADD UNIQUE INDEX index_name(stone_id, charge_title)supprimera les lignes en double ne laissant qu'une seule paire unique.
dev-null-dweller