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

3 modi per eliminare le righe duplicate in SQL Server ignorando la chiave primaria

Gli esempi seguenti usano T-SQL per eliminare le righe duplicate in SQL Server ignorando la chiave primaria o la colonna dell'identificatore univoco.

Più specificamente, gli esempi eliminano le righe duplicate ma ne conservano una. Quindi, date due righe identiche, una viene eliminata e l'altra rimane. Questo è spesso indicato come "deduplicazione" della tabella, "deduplicazione" della tabella, ecc.

Dati di esempio

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, così come le ultime tre righe.

Opzione 1

Innanzitutto, eseguiamo il codice seguente per verificare quali righe verranno deduplicate:

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

Abbiamo usato il ROW_NUMBER() funzione con il PARTITION BY clausola per creare il nostro numero di riga che aumenta quando vengono trovati duplicati e si ripristina quando viene trovato un non duplicato. Un numero maggiore di 1 indica che si tratta di un duplicato, quindi restituiamo solo righe che hanno un numero maggiore di 1.

Possiamo vedere che tre righe verranno eliminate quando deduppiamo questa tabella.

Ora deduplica il tavolo:

WITH cte AS 
    (
        SELECT 
            *,
            ROW_NUMBER() OVER ( 
                PARTITION BY FirstName, LastName 
                ORDER BY FirstName, LastName
                ) AS Row_Number
        FROM Dogs
    )
DELETE FROM cte WHERE Row_Number <> 1;

Risultato:

(3 rows affected)

Come previsto, sono state eliminate tre righe.

Questa query è quasi identica alla precedente. Tutto quello che abbiamo fatto è stato cambiare SELECT * nell'ultima riga a DELETE .

Ora selezioniamo tutte le righe della tabella per verificare che siano state eliminate le righe corrette:

SELECT * FROM Dogs;

Risultato:

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

Possiamo vedere che ogni cane ora appare solo una volta nella tabella.

Opzione 2

Supponendo che la tabella sia stata ripristinata dopo l'esempio precedente, ecco un altro modo per verificare la presenza di duplicati:

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

In questo caso, abbiamo utilizzato EXCEPT insieme all'operatore MIN() funzione. Potremmo sostituire MIN() con MAX() a seconda di quali righe vogliamo eliminare.

Per eliminare le righe, possiamo semplicemente sostituire SELECT * con DELETE :

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

Risultato:

(3 rows affected)

E controlla cosa è rimasto:

SELECT * FROM Dogs;

Risultato:

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

Opzione 3

Un altro modo per farlo è unire il tavolo su se stesso e verificare la presenza di duplicati in questo modo.

Supponendo che la tabella sia stata ripristinata dopo l'esempio precedente, ecco la nostra terza opzione per selezionare i duplicati:

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

Questo risultato non è così chiaro come quello dell'esempio precedente, ma possiamo comunque vedere quali righe sono duplicate.

Ora possiamo modificare quella query in modo da eliminare le righe duplicate:

DELETE FROM Dogs WHERE DogId IN (
    SELECT d2.DogId 
    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:

(3 rows affected)

Ancora una volta, tre righe sono state eliminate.

Controlliamo di nuovo la tabella:

SELECT * FROM Dogs;

Risultato:

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

Potresti notare che questa volta le altre righe sono state eliminate. In altre parole, ora abbiamo DogId s 2, 3, 4 e 7 mentre negli esempi precedenti siamo rimasti con 1, 3, 4 e 5.

Possiamo facilmente modificare questo esempio per eliminare le stesse righe degli esempi precedenti. Per fare ciò, possiamo usare il MIN() funzione invece di MAX() funzione:

DELETE FROM Dogs WHERE DogId IN (
    SELECT d2.DogId 
    FROM Dogs d1, Dogs d2 
    WHERE d1.FirstName = d2.FirstName 
    AND d1.LastName = d2.LastName 
    AND d1.DogId <> d2.DogId 
    AND d1.DogId=( 
        SELECT MIN(DogId) 
        FROM Dogs d3 
        WHERE d3.FirstName = d1.FirstName 
        AND d3.LastName = d1.LastName
    )
);