sql >> Base de Datos >  >> RDS >> Sqlserver

7 formas de encontrar filas duplicadas en SQL Server ignorando cualquier clave principal

Aquí hay siete opciones para encontrar filas duplicadas en SQL Server, cuando esas filas tienen una clave principal u otra columna de identificador único.

En otras palabras, la tabla contiene dos o más filas que comparten exactamente los mismos valores en todas las columnas excepto en su columna de identificador único.

Datos de muestra

Supongamos que tenemos una tabla con los siguientes datos:

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

Podemos ver que las dos primeras filas están duplicadas (excepto el DogId columna, que contiene un valor único en todas las filas y podría usarse como la columna de clave principal de la tabla). También podemos ver que las últimas tres filas están duplicadas (excepto el DogId columna).

La columna de ID única garantiza que no haya filas duplicadas, lo que normalmente es una característica muy deseable en los RDBMS. Sin embargo, en este caso tiene el potencial de interferir con nuestra capacidad de encontrar duplicados. Por definición, la columna de ID única garantiza que no haya duplicados. Afortunadamente, podemos solucionar este problema con bastante facilidad, como muestran los siguientes ejemplos.

Opción 1

Probablemente la forma más fácil/sencilla de hacerlo es con una consulta simple que usa GROUP BY cláusula:

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

Resultado:

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

Pudimos excluir la columna de clave principal/ID única al omitirla de nuestra consulta.

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

Opción 2

Podemos excluir los no duplicados del resultado incluyendo el HAVING cláusula en nuestra consulta:

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

Resultado:

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

Opción 3

También podemos buscar duplicados en columnas concatenadas. Por ejemplo, podemos usar CONCAT() función para concatenar nuestras dos columnas:

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

Resultado:

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

Opción 4

Podemos usar el ROW_NUMBER() función con la PARTITION BY cláusula para crear una nueva columna con un número de fila que se incrementa cada vez que hay un duplicado, pero se reinicia cuando hay una fila única:

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

Resultado:

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

Una ventaja de este método es que podemos ver todas y cada una de las filas duplicadas, junto con su columna de identificador único, debido al hecho de que no estamos agrupando los resultados.

Opción 5

También 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 Row_Number
        FROM Dogs
    )
SELECT * FROM cte WHERE Row_Number <> 1;

Resultado:

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

Esta opción excluye los no duplicados de la salida.

También excluye exactamente una fila de cada duplicado de la salida. Esto nos abre la puerta para convertir el último SELECT * en un DELETE para deduplicar la tabla manteniendo uno de cada duplicado.

Opción 6

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

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

Resultado:

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

Este ejemplo no requiere generar nuestro propio número de fila por separado.

Opción 7

Y finalmente, aquí hay una técnica un poco más complicada para devolver filas duplicadas:

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

Incluso el resultado parece más intrincado, pero oye, ¡todavía nos muestra los duplicados!