sql >> Database teknologi >  >> RDS >> Sqlserver

3 måder at slette duplikerede rækker i SQL Server, mens du ignorerer den primære nøgle

Følgende eksempler bruger T-SQL til at slette duplikerede rækker i SQL Server, mens den primære nøgle eller den unikke identifikatorkolonne ignoreres.

Mere specifikt sletter eksemplerne duplikerede rækker, men beholder én. Så givet to identiske rækker, slettes den ene, og den anden forbliver. Dette omtales ofte som "de-dupering" af tabellen, "deduplikering" af tabellen osv.

Eksempel på data

Antag, at vi har en tabel med følgende data:

SELECT * FROM Dogs;

Resultat:

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

Vi kan se, at de første to rækker er dubletter, og det samme er de sidste tre rækker.

Mulighed 1

Lad os først køre følgende kode for at kontrollere, hvilke rækker der skal de-duperes:

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;

Resultat:

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

Vi brugte ROW_NUMBER() funktion med PARTITION BY klausul for at oprette vores eget rækkenummer, der øges, når der findes dubletter, og nulstilles, når der findes et ikke-duplikat. Et tal større end 1 angiver, at det er en dublet, og derfor returnerer vi kun rækker, der har et tal større end 1.

Vi kan se, at tre rækker vil blive slettet, når vi de-duperer denne tabel.

Lad os nu dedupere tabellen:

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;

Resultat:

(3 rows affected)

Som forventet blev tre rækker slettet.

Denne forespørgsel er næsten identisk med den forrige. Det eneste, vi gjorde, var at ændre SELECT * på den sidste linje til DELETE .

Lad os nu vælge alle rækker fra tabellen for at bekræfte, at de korrekte rækker blev slettet:

SELECT * FROM Dogs;

Resultat:

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

Vi kan se, at hver hund nu kun optræder én gang i tabellen.

Mulighed 2

Hvis det antages, at tabellen er blevet gendannet efter det forrige eksempel, er her en anden måde at kontrollere for dubletter:

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

Resultat:

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

I dette tilfælde brugte vi EXCEPT operator sammen med MIN() fungere. Vi kunne erstatte MIN() med MAX() afhængig af hvilke rækker vi ønsker skal slettes.

For at slette rækkerne kan vi blot erstatte SELECT * med DELETE :

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

Resultat:

(3 rows affected)

Og tjek for at se, hvad der er tilbage:

SELECT * FROM Dogs;

Resultat:

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

Mulighed 3

En anden måde at gøre det på er at slutte sig til bordet på sig selv og tjekke for dubletter på den måde.

Hvis vi antager, at tabellen er blevet gendannet efter det forrige eksempel, er her vores tredje mulighed for at vælge dubletter:

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

Resultat:

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

Dette resultat er ikke helt så klart som det i det foregående eksempel, men vi kan stadig se, hvilke rækker der er dubletter.

Nu kan vi ændre den forespørgsel, så vi sletter duplikerede rækker:

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

Resultat:

(3 rows affected)

Endnu en gang blev tre rækker slettet.

Lad os tjekke tabellen igen:

SELECT * FROM Dogs;

Resultat:

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

Du vil måske bemærke, at denne gang blev de andre rækker slettet. Med andre ord har vi nu DogId s 2, 3, 4 og 7, hvorimod vi i de foregående eksempler stod tilbage med 1, 3, 4 og 5.

Vi kan nemt ændre dette eksempel for at slette de samme rækker som de foregående eksempler. For at gøre dette kan vi bruge MIN() funktion i stedet for MAX() funktion:

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

  1. RUC eller RUR?

  2. Hierarkiske forespørgsler i MySQL

  3. percentiler fra histogramdata

  4. IKKE I valg med NULL-værdier