web-dev-qa-db-fra.com

Comment trouver les doublons dans 2 colonnes et non pas 1

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

stone_id peut avoir des doublons tant que pour chaque upsharge le titre est différent et inversement. Mais disons par exemple stone_id = 412 et upcharge_title = "saphir", cette combinaison ne doit apparaître 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 permettant de trouver des doublons dans les deux champs? Et si possible existe-t-il un moyen de configurer ma base de données pour ne pas permettre cela?

J'utilise MySQL version 4.1.22

91
JD Isaacks

Vous devez configurer une clé composite entre les deux champs. Cela nécessitera un identifiant unique stone_id et upcharge_title 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
170
Miyagi Coder

J'ai trouvé utile d'ajouter un index unqiue en utilisant un "ALTER IGNORE" qui supprime les doublons et applique des enregistrements uniques qui sonnent comme vous le souhaitez. Donc, la syntaxe serait la suivante:

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

Cela ajoute effectivement la contrainte unique, ce qui signifie que vous ne créerez jamais d'enregistrements en double et que IGNORE supprimera les doublons existants.

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

Mise à jour: @Inquisitive m'a informé que cela pourrait échouer dans les versions de MySql> 5.5:

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

Mise à jour - ALTER IGNORE Supprimé dans 5.7

De la docs

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

Un des devs de MySQL donne deux alternatives :

  • Grouper par les champs uniques et supprimer comme vu 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

33
SeanDowney

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
7
Jason Punyon

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.

4
Ian Nelson

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 valide T-SQL. Pas sûr de MySQL.)

3
P Daddy

ceci SO post m'a aidé, mais je voulais aussi savoir comment supprimer et conserver l'une des lignes ... voici une PHP solution pour supprimer le duplicata lignes et en garder une (dans mon cas, il n'y avait que 2 colonnes et il est dans une fonction pour effacer les associations de catégorie 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

0
groovenectar