sql >> Base de Datos >  >> RDS >> Mysql

7 formas de encontrar filas duplicadas ignorando la clave principal en MySQL

Aquí hay siete formas de devolver filas duplicadas en MySQL cuando esas filas tienen una clave principal u otra columna de identificador único.

Datos de muestra

Usaremos los siguientes datos para nuestros ejemplos:

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;

Resultado:

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

Las filas duplicadas comparten exactamente los mismos valores en todas las columnas excepto en la columna de clave principal/ID única.

Las dos primeras filas son duplicados (excepto el DogId columna, que es la clave principal de la tabla y contiene un valor único en todas las filas). Las últimas tres filas también son duplicados (excepto el DogId columna).

La columna de clave principal garantiza que no haya filas duplicadas, lo que normalmente es bueno en los RDBMS. Sin embargo, por definición esto significa que no hay duplicados. En nuestro caso, la columna de clave principal es un número creciente y su valor no tiene ningún significado y no es significativo. Por lo tanto, debemos ignorar esa fila si queremos encontrar duplicados en las columnas que son significativo.

Opción 1

Nuestra primera opción es usar el GROUP BY cláusula para agrupar las columnas por sus columnas significativas, luego use el COUNT() función para devolver el número de filas idénticas:

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

Resultado:

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

Pudimos ignorar la columna de la clave principal al omitirla de nuestra consulta.

El resultado nos dice que hay dos filas que contienen a Bark Smith y tres filas que contienen a Wag Johnson. Estos son duplicados (o triplicados en el caso de Wag Johnson). Las otras dos filas no tienen duplicados.

Opción 2

Podemos excluir los no duplicados de la salida con HAVING cláusula:

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

Resultado:

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

Opción 3

También podemos buscar duplicados en columnas concatenadas. Por ejemplo, podemos usar CONCAT() función para concatenar nuestras dos columnas, use el DISTINCT palabra clave para obtener valores distintos, luego use COUNT() función para devolver el conteo:

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

Resultado:

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

Opción 4

Alternativamente, podemos usar el ROW_NUMBER() función con la PARTITION BY cláusula:

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

Resultado:

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

Esto crea una nueva columna con un número de fila que aumenta cada vez que hay un duplicado, pero se reinicia cuando hay una fila única

Esta técnica proporciona un posible beneficio en el sentido de que no necesitamos agrupar los resultados. Esto significa que podemos ver cada fila duplicada, incluida su columna de identificador único.

Opción 5

Podemos usar el ejemplo anterior como una expresión de tabla común en una consulta más grande:

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;

Resultado:

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

Esta técnica excluye los no duplicados de la salida y excluye una fila de cada duplicado de la salida.

Esta consulta se puede utilizar como precursor de una operación de eliminación de duplicados. Puede mostrarnos qué se va a eliminar si decidimos eliminar los duplicados. Para deduplicar la tabla, todo lo que tenemos que hacer es reemplazar el último SELECT * con DELETE .

Opción 6

Aquí hay una forma más concisa de obtener el mismo resultado que el ejemplo anterior:

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

Resultado:

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

Esta técnica no requiere que generemos un número de fila separado con ROW_NUMBER() como en el ejemplo anterior.

También podemos reemplazar SELECT * con DELETE para eliminar los duplicados.

Opción 7

Y finalmente, aquí hay una opción más para devolver duplicados:

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

Resultado:

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