Mysql
 sql >> Database >  >> RDS >> Mysql

7 modi per trovare righe duplicate ignorando la chiave primaria in MySQL

Ecco sette modi per restituire righe duplicate in MySQL quando tali righe hanno una chiave primaria o un'altra colonna di identificatore univoco.

Dati campione

Utilizzeremo i seguenti dati per i nostri esempi:

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;

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

Le righe duplicate condividono esattamente gli stessi valori in tutte le colonne ad eccezione della relativa chiave primaria/colonna ID univoco.

Le prime due righe sono duplicate (tranne per DogId colonna, che è la chiave primaria della tabella e contiene un valore univoco in tutte le righe). Anche le ultime tre righe sono duplicate (ad eccezione di DogId colonna).

La colonna della chiave primaria assicura che non vi siano righe duplicate, il che normalmente è positivo negli RDBMS. Tuttavia, per definizione questo significa che non ci sono duplicati. Nel nostro caso, la colonna della chiave primaria è un numero incrementale e il suo valore non ha significato e non è significativo. Dobbiamo quindi ignorare quella riga se vogliamo trovare duplicati nelle colonne che sono significativo.

Opzione 1

La nostra prima opzione è usare il GROUP BY clausola per raggruppare le colonne in base alle colonne significative, quindi utilizzare COUNT() funzione per restituire il numero di righe identiche:

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

Risultato:

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

Siamo stati in grado di ignorare la colonna della chiave primaria omettendola dalla nostra query.

Il risultato ci dice che ci sono due righe contenenti Bark Smith e tre righe contenenti Wag Johnson. Questi sono duplicati (o triplicati nel caso di Wag Johnson). Le altre due righe non hanno duplicati.

Opzione 2

Possiamo escludere i non duplicati dall'output con HAVING clausola:

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

Risultato:

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

Opzione 3

Possiamo anche verificare la presenza di duplicati su colonne concatenate. Ad esempio, possiamo usare CONCAT() per concatenare le nostre due colonne, usa il DISTINCT parola chiave per ottenere valori distinti, quindi utilizzare COUNT() funzione per restituire il conteggio:

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

Risultato:

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

Opzione 4

In alternativa possiamo utilizzare il ROW_NUMBER() funzione con il PARTITION BY clausola:

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

Risultato:

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

Questo crea 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

Questa tecnica offre un possibile vantaggio in quanto non è necessario raggruppare i risultati. Ciò significa che possiamo vedere ogni riga duplicata, inclusa la relativa colonna dell'identificatore univoco.

Opzione 5

Possiamo usare l'esempio precedente come un'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 rn
        FROM Dogs
    )
SELECT * FROM cte WHERE rn <> 1;

Risultato:

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

Questa tecnica esclude i non duplicati dall'output ed esclude una riga di ogni duplicato dall'output.

Questa query può essere utilizzata come precursore di un'operazione di deduplicazione. Può mostrarci cosa verrà eliminato se decidiamo di eliminare i duplicati. Per deduplicare la tabella, tutto ciò che dobbiamo fare è sostituire l'ultimo SELECT * con DELETE .

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 
    WHERE DogId NOT IN (SELECT MIN(DogId) FROM Dogs
    GROUP BY FirstName, LastName)
    );

Risultato:

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

Questa tecnica non richiede la generazione di un numero di riga separato con ROW_NUMBER() come nell'esempio precedente.

Possiamo anche sostituire SELECT * con DELETE per eliminare i duplicati.

Opzione 7

E infine, ecco un'altra opzione per restituire 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  |
+-------+-----------+----------+-------+-----------+----------+