sql >> Databasteknik >  >> RDS >> Sqlserver

3 sätt att ta bort dubbletter av rader i SQL Server medan du ignorerar den primära nyckeln

Följande exempel använder T-SQL för att ta bort dubbletter av rader i SQL Server samtidigt som primärnyckeln eller den unika identifierarkolumnen ignoreras.

Mer specifikt tar exemplen bort dubbletter av rader men behåller en. Så, givet två identiska rader, raderas en och den andra finns kvar. Detta hänvisas ofta till som "de-dupera" tabellen, "deduplicering" av tabellen, etc.

Exempeldata

Anta att vi har en tabell med följande 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 att de två första raderna är dubbletter, och det är de tre sista raderna också.

Alternativ 1

Låt oss först köra följande kod för att kontrollera vilka rader som kommer att deduperas:

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 använde ROW_NUMBER() funktion med PARTITION BY sats för att skapa vårt eget radnummer som ökar när några dubbletter hittas och återställs när en icke-duplicerad hittas. Ett nummer större än 1 indikerar att det är en dubblett, och därför returnerar vi bara rader som har ett nummer större än 1.

Vi kan se att tre rader kommer att raderas när vi de-duperar den här tabellen.

Låt oss nu de-dupera 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 väntat togs tre rader bort.

Den här frågan är nästan identisk med den föregående. Allt vi gjorde var att ändra SELECT * på sista raden till DELETE .

Låt oss nu välja alla rader från tabellen för att verifiera att rätt rader har tagits bort:

SELECT * FROM Dogs;

Resultat:

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

Vi kan se att varje hund nu bara visas en gång i tabellen.

Alternativ 2

Förutsatt att tabellen har återställts efter föregående exempel, här är ett annat sätt att leta efter dubbletter:

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 det här fallet använde vi EXCEPT operatorn tillsammans med MIN() fungera. Vi skulle kunna ersätta MIN() med MAX() beroende på vilka rader vi vill ska raderas.

För att radera raderna kan vi helt enkelt ersätta 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)

Och kolla för att se vad som finns kvar:

SELECT * FROM Dogs;

Resultat:

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

Alternativ 3

Ett annat sätt att göra det på är att gå med i tabellen på sig själv och leta efter dubbletter på det sättet.

Förutsatt att tabellen har återställts efter föregående exempel, här är vårt tredje alternativ för att välja dubbletter:

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

Detta resultat är inte riktigt lika tydligt som det i föregående exempel, men vi kan fortfarande se vilka rader som är dubbletter.

Nu kan vi ändra den frågan så att vi tar bort dubbletter av rader:

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)

Återigen togs tre rader bort.

Låt oss kolla tabellen igen:

SELECT * FROM Dogs;

Resultat:

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

Du kanske märker att den här gången togs de andra raderna bort. Med andra ord har vi nu DogId s 2, 3, 4 och 7 medan vi i de tidigare exemplen hade 1, 3, 4 och 5.

Vi kan enkelt ändra detta exempel för att ta bort samma rader som de tidigare exemplen. För att göra detta kan vi använda MIN() funktion istället för 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. Hur man genererar skript för att återskapa främmande nyckelbegränsningar i SQL Server Database - SQL Server / TSQL Tutorial Del 73

  2. Hur UPPER()-funktionen fungerar i MySQL

  3. Filtrerade index och forcerad parametrering (redux)

  4. Fråga för att bara hämta siffror från en sträng