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!