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

3 formas de eliminar filas duplicadas en SQL Server ignorando la clave principal

Los siguientes ejemplos usan T-SQL para eliminar filas duplicadas en SQL Server mientras se ignora la clave principal o la columna de identificador único.

Más específicamente, los ejemplos eliminan filas duplicadas pero conservan una. Entonces, dadas dos filas idénticas, una se elimina y la otra permanece. Esto a menudo se denomina "desduplicación" de la tabla, "desduplicación" de la tabla, etc.

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, al igual que las últimas tres filas.

Opción 1

Primero, ejecutemos el siguiente código para verificar qué filas se van a desduplicar:

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

Usamos el ROW_NUMBER() función con la PARTITION BY cláusula para crear nuestro propio número de fila que se incrementa cuando se encuentran duplicados y se restablece cuando se encuentra uno que no está duplicado. Un número mayor que 1 indica que es un duplicado, por lo que solo devolvemos las filas que tienen un número mayor que 1.

Podemos ver que se eliminarán tres filas cuando eliminemos los duplicados de esta tabla.

Ahora vamos a eliminar los duplicados de la tabla:

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;

Resultado:

(3 rows affected)

Como era de esperar, se eliminaron tres filas.

Esta consulta es casi idéntica a la anterior. Todo lo que hicimos fue cambiar SELECT * en la última línea para DELETE .

Ahora seleccionemos todas las filas de la tabla para verificar que se eliminaron las filas correctas:

SELECT * FROM Dogs;

Resultado:

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

Podemos ver que cada perro ahora aparece solo una vez en la tabla.

Opción 2

Suponiendo que la tabla se haya restaurado después del ejemplo anterior, esta es otra forma de comprobar si hay duplicados:

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

En este caso, hicimos uso de EXCEPT operador junto con el MIN() función. Podríamos reemplazar MIN() con MAX() dependiendo de qué filas queramos eliminar.

Para eliminar las filas, simplemente podemos reemplazar SELECT * con DELETE :

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

Resultado:

(3 rows affected)

Y revisa para ver lo que queda:

SELECT * FROM Dogs;

Resultado:

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

Opción 3

Otra forma de hacerlo es unir la tabla sobre sí misma y buscar duplicados de esa manera.

Suponiendo que la tabla se haya restaurado después del ejemplo anterior, esta es nuestra tercera opción para seleccionar 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    |
+---------+-------------+------------+---------+-------------+------------+

Este resultado no es tan claro como el del ejemplo anterior, pero aún podemos ver qué filas están duplicadas.

Ahora podemos modificar esa consulta para eliminar las filas duplicadas:

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

Resultado:

(3 rows affected)

Una vez más, se eliminaron tres filas.

Revisemos la tabla nuevamente:

SELECT * FROM Dogs;

Resultado:

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

Puede notar que esta vez se eliminaron las otras filas. En otras palabras, ahora tenemos DogId s 2, 3, 4 y 7 mientras que en los ejemplos anteriores nos quedamos con 1, 3, 4 y 5.

Podemos cambiar fácilmente este ejemplo para eliminar las mismas filas que los ejemplos anteriores. Para hacer esto, podemos usar MIN() función en lugar de MAX() función:

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