Voici sept façons de renvoyer des lignes en double dans MySQL lorsque ces lignes ont une clé primaire ou une autre colonne d'identifiant unique.
Exemple de données
Nous utiliserons les données suivantes pour nos exemples :
DROP TABLE IF EXISTS Dogs;
CREATE TABLE Dogs (
DogId int PRIMARY KEY NOT NULL,
FirstName varchar(50),
LastName varchar(50)
);
INSERT INTO Dogs VALUES
(1, 'Bark', 'Smith'),
(2, 'Bark', 'Smith'),
(3, 'Woof', 'Jones'),
(4, 'Ruff', 'Robinson'),
(5, 'Wag', 'Johnson'),
(6, 'Wag', 'Johnson'),
(7, 'Wag', 'Johnson');
SELECT * FROM Dogs;
Résultat :
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 1 | Bark | Smith | | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Les lignes en double partagent exactement les mêmes valeurs dans toutes les colonnes, à l'exception de leur colonne de clé primaire/ID unique.
Les deux premières lignes sont des doublons (sauf pour le DogId
colonne, qui est la clé primaire de la table et contient une valeur unique sur toutes les lignes). Les trois dernières lignes sont également des doublons (sauf pour le DogId
colonne).
La colonne de clé primaire garantit qu'il n'y a pas de lignes en double, ce qui est normalement une bonne chose dans les SGBDR. Cependant, par définition, cela signifie qu'il n'y a pas de doublons. Dans notre cas, la colonne de la clé primaire est un nombre qui s'incrémente, et sa valeur n'a aucune signification et n'est pas significative. Nous devons donc ignorer cette ligne si nous voulons trouver des doublons dans les colonnes qui sont important.
Option 1
Notre première option est d'utiliser le GROUP BY
clause pour regrouper les colonnes par leurs colonnes significatives, puis utilisez le COUNT()
fonction pour retourner le nombre de lignes identiques :
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;
Résultat :
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Woof | Jones | 1 | | Ruff | Robinson | 1 | | Wag | Johnson | 3 | +-----------+----------+-------+
Nous avons pu ignorer la colonne de clé primaire en l'omettant de notre requête.
Le résultat nous indique qu'il y a deux lignes contenant Bark Smith et trois lignes contenant Wag Johnson. Ce sont des doublons (ou des triples dans le cas de Wag Johnson). Les deux autres lignes n'ont pas de doublons.
Option 2
Nous pouvons exclure les non-doublons de la sortie avec le HAVING
clause :
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;
Résultat :
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Wag | Johnson | 3 | +-----------+----------+-------+
Option 3
Nous pouvons également vérifier les doublons sur les colonnes concaténées. Par exemple, nous pouvons utiliser le CONCAT()
fonction pour concaténer nos deux colonnes, utilisez le DISTINCT
mot-clé pour obtenir des valeurs distinctes, puis utilisez le COUNT()
fonction pour retourner le compte :
SELECT
DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);
Résultat :
+---------------+-------+ | DogName | Count | +---------------+-------+ | Bark Smith | 2 | | Woof Jones | 1 | | Ruff Robinson | 1 | | Wag Johnson | 3 | +---------------+-------+
Option 4
Nous pouvons également utiliser le ROW_NUMBER()
fonction avec le PARTITION BY
clause :
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS rn
FROM Dogs;
Résultat :
+-------+-----------+----------+----+ | DogId | FirstName | LastName | rn | +-------+-----------+----------+----+ | 1 | Bark | Smith | 1 | | 2 | Bark | Smith | 2 | | 4 | Ruff | Robinson | 1 | | 5 | Wag | Johnson | 1 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | | 3 | Woof | Jones | 1 | +-------+-----------+----------+----+
Cela crée une nouvelle colonne avec un numéro de ligne qui s'incrémente à chaque fois qu'il y a un doublon, mais se réinitialise à nouveau lorsqu'il y a une ligne unique
Cette technique offre un avantage possible en ce sens que nous n'avons pas besoin de regrouper les résultats. Cela signifie que nous pouvons voir chaque ligne en double, y compris sa colonne d'identifiant unique.
Option 5
Nous pouvons utiliser l'exemple précédent comme expression de table commune dans une requête plus large :
WITH cte AS
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS rn
FROM Dogs
)
SELECT * FROM cte WHERE rn <> 1;
Résultat :
+-------+-----------+----------+----+ | DogId | FirstName | LastName | rn | +-------+-----------+----------+----+ | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +-------+-----------+----------+----+
Cette technique exclut les non-doublons de la sortie et exclut une ligne de chaque doublon de la sortie.
Cette requête peut être utilisée comme précurseur d'une opération de déduplication. Il peut nous montrer ce qui va être supprimé si nous décidons de supprimer les doublons. Pour dédupliquer la table, il suffit de remplacer le dernier SELECT *
avec DELETE
.
Option 6
Voici une manière plus concise d'obtenir le même résultat que dans l'exemple précédent :
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
WHERE DogId NOT IN (SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName)
);
Résultat :
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Cette technique ne nous oblige pas à générer un numéro de ligne séparé avec ROW_NUMBER()
comme dans l'exemple précédent.
On peut aussi remplacer SELECT *
avec DELETE
pour supprimer les doublons.
Option 7
Et enfin, voici une autre option pour renvoyer les doublons :
SELECT *
FROM Dogs d1, Dogs d2
WHERE d1.FirstName = d2.FirstName
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId
AND d1.DogId = (
SELECT MAX(DogId)
FROM Dogs d3
WHERE d3.FirstName = d1.FirstName
AND d3.LastName = d1.LastName
);
Résultat :
+-------+-----------+----------+-------+-----------+----------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | +-------+-----------+----------+-------+-----------+----------+ | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +-------+-----------+----------+-------+-----------+----------+