Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

7 façons de trouver des lignes en double dans SQL Server tout en ignorant toute clé primaire

Voici sept options pour rechercher des lignes en double dans SQL Server, lorsque ces lignes ont une clé primaire ou une autre colonne d'identifiant unique.

En d'autres termes, le tableau contient deux ou plusieurs lignes qui partagent exactement les mêmes valeurs dans toutes les colonnes, à l'exception de sa colonne d'identifiant unique.

Exemple de données

Supposons que nous ayons un tableau avec les données suivantes :

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    |
+---------+-------------+------------+

Nous pouvons voir que les deux premières lignes sont des doublons (sauf pour le DogId colonne, qui contient une valeur unique sur toutes les lignes et peut être utilisée comme colonne de clé primaire de la table). Nous pouvons également voir que les trois dernières lignes sont des doublons (sauf pour le DogId colonne).

La colonne d'ID unique garantit qu'il n'y a pas de lignes en double, ce qui est normalement un trait hautement souhaitable dans les SGBDR. Cependant, dans ce cas, il a le potentiel d'interférer avec notre capacité à trouver des doublons. Par définition, la colonne d'ID unique garantit qu'il n'y a pas de doublons. Heureusement, nous pouvons résoudre ce problème assez facilement, comme le montrent les exemples suivants.

Option 1

La façon la plus simple/la plus simple de le faire est probablement avec une requête simple qui utilise le GROUP BY clause :

SELECT 
    FirstName, 
    LastName, 
    COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;

Résultat :

+-------------+------------+---------+
| FirstName   | LastName   | Count   |
|-------------+------------+---------|
| Wag         | Johnson    | 3       |
| Woof        | Jones      | 1       |
| Ruff        | Robinson   | 1       |
| Bark        | Smith      | 2       |
+-------------+------------+---------+

Nous avons pu exclure la colonne clé primaire/identifiant unique en l'omettant de notre requête.

Le résultat nous indique qu'il y a trois lignes contenant Wag Johnson et deux lignes contenant Bark Smith. Ce sont des doublons (ou des triples dans le cas de Wag Johnson).

Option 2

Nous pouvons exclure les non-doublons du résultat en incluant le HAVING clause dans notre requête :

SELECT 
    FirstName, 
    LastName, 
    COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;

Résultat :

+-------------+------------+---------+
| FirstName   | LastName   | Count   |
|-------------+------------+---------|
| Wag         | Johnson    | 3       |
| Bark        | Smith      | 2       |
+-------------+------------+---------+

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 :

SELECT
    DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
    COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);

Résultat :

+---------------+---------+
| DogName       | Count   |
|---------------+---------|
| Bark Smith    | 2       |
| Ruff Robinson | 1       |
| Wag Johnson   | 3       |
| Woof Jones    | 1       |
+---------------+---------+

Option 4

Nous pouvons utiliser le ROW_NUMBER() fonction avec le PARTITION BY clause pour créer 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 :

SELECT 
    *,
    ROW_NUMBER() OVER ( 
        PARTITION BY FirstName, LastName 
        ORDER BY FirstName, LastName
        ) AS Row_Number
FROM Dogs;

Résultat :

+---------+-------------+------------+--------------+
| DogId   | FirstName   | LastName   | Row_Number   |
|---------+-------------+------------+--------------|
| 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            |
+---------+-------------+------------+--------------+

L'un des avantages de cette méthode est que nous pouvons voir chaque ligne en double, ainsi que sa colonne d'identifiant unique, car nous ne regroupons pas les résultats.

Option 5

Nous pouvons également 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 Row_Number
        FROM Dogs
    )
SELECT * FROM cte WHERE Row_Number <> 1;

Résultat :

+---------+-------------+------------+--------------+
| DogId   | FirstName   | LastName   | Row_Number   |
|---------+-------------+------------+--------------|
| 2       | Bark        | Smith      | 2            |
| 6       | Wag         | Johnson    | 2            |
| 7       | Wag         | Johnson    | 3            |
+---------+-------------+------------+--------------+

Cette option exclut les non-doublons de la sortie.

Il exclut également exactement une ligne de chaque doublon de la sortie. Cela nous ouvre la porte pour activer le dernier SELECT * dans un DELETE pour dédupliquer la table tout en gardant un exemplaire de chaque doublon.

Option 6

Voici une manière plus succincte d'obtenir le même résultat que dans l'exemple précédent :

SELECT * FROM Dogs 
WHERE DogId IN (
    SELECT DogId FROM Dogs 
    EXCEPT SELECT MIN(DogId) FROM Dogs 
    GROUP BY FirstName, LastName
    );

Résultat :

+-------+-----------+----------+
| DogId | FirstName | LastName |
+-------+-----------+----------+
|     2 | Bark      | Smith    |
|     6 | Wag       | Johnson  |
|     7 | Wag       | Johnson  |
+-------+-----------+----------+

Cet exemple ne nécessite pas de générer notre propre numéro de ligne séparé.

Option 7

Et enfin, voici une technique un peu plus compliquée pour renvoyer des lignes en double :

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    |
+---------+-------------+------------+---------+-------------+------------+

Même le résultat semble plus alambiqué, mais bon, il nous montre toujours les doublons !