Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

7 modi per trovare righe duplicate in SQL Server ignorando qualsiasi chiave primaria

Ecco sette opzioni per trovare righe duplicate in SQL Server, quando tali righe hanno una chiave primaria o un'altra colonna di identificatore univoco.

In altre parole, la tabella contiene due o più righe che condividono esattamente gli stessi valori in tutte le colonne ad eccezione della colonna dell'identificatore univoco.

Dati campione

Supponiamo di avere una tabella con i seguenti dati:

SELECT * FROM Dogs;

Risultato:

+---------+-------------+------------+
| DogId   | FirstName   | LastName   |
|---------+-------------+------------|
| 1       | Bark        | Smith      |
| 2       | Bark        | Smith      |
| 3       | Woof        | Jones      |
| 4       | Ruff        | Robinson   |
| 5       | Wag         | Johnson    |
| 6       | Wag         | Johnson    |
| 7       | Wag         | Johnson    |
+---------+-------------+------------+

Possiamo vedere che le prime due righe sono duplicate (ad eccezione di DogId colonna, che contiene un valore univoco in tutte le righe e può essere utilizzata come colonna della chiave primaria della tabella). Possiamo anche vedere che le ultime tre righe sono duplicate (tranne per DogId colonna).

La colonna ID univoco assicura che non vi siano righe duplicate, che normalmente è una caratteristica altamente desiderabile negli RDBMS. Tuttavia, in questo caso ha il potenziale per interferire con la nostra capacità di trovare duplicati. Per definizione, la colonna ID univoco garantisce che non vi siano duplicati. Fortunatamente, possiamo superare questo problema abbastanza facilmente, come mostrano i seguenti esempi.

Opzione 1

Probabilmente il modo più semplice/semplice per farlo è con una semplice query che utilizza il GROUP BY clausola:

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

Risultato:

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

Siamo stati in grado di escludere la colonna chiave primaria/ID univoco omettendola dalla nostra query.

Il risultato ci dice che ci sono tre righe contenenti Wag Johnson e due righe contenenti Bark Smith. Si tratta di duplicati (o triplicati nel caso di Wag Johnson).

Opzione 2

Possiamo escludere i non duplicati dal risultato includendo HAVING clausola nella nostra query:

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

Risultato:

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

Opzione 3

Possiamo anche verificare la presenza di duplicati su colonne concatenate. Ad esempio, possiamo usare CONCAT() funzione per concatenare le nostre due colonne:

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

Risultato:

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

Opzione 4

Possiamo usare il ROW_NUMBER() funzione con il PARTITION BY clausola per creare una nuova colonna con un numero di riga che aumenta ogni volta che c'è un duplicato, ma si reimposta di nuovo quando c'è una riga univoca:

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

Risultato:

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

Uno dei vantaggi di questo metodo è che possiamo vedere ogni riga duplicata, insieme alla relativa colonna dell'identificatore univoco, poiché non stiamo raggruppando i risultati.

Opzione 5

Possiamo anche utilizzare l'esempio precedente come espressione di tabella comune in una query più ampia:

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;

Risultato:

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

Questa opzione esclude i non duplicati dall'output.

Esclude anche esattamente una riga di ogni duplicato dall'output. Questo ci apre la porta per trasformare l'ultimo SELECT * in un DELETE per deduplicare la tabella mantenendo uno di ogni duplicato.

Opzione 6

Ecco un modo più conciso per ottenere lo stesso output dell'esempio precedente:

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

Risultato:

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

Questo esempio non richiede la generazione del nostro numero di riga separato.

Opzione 7

E infine, ecco una tecnica leggermente più contorta per restituire righe duplicate:

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
);

Risultato:

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

Anche il risultato sembra più contorto, ma ehi, ci mostra ancora i duplicati!