sql >> Databasteknik >  >> RDS >> Sqlserver

7 sätt att hitta dubbletter av rader i SQL Server samtidigt som du ignorerar en primärnyckel

Här är sju alternativ för att hitta dubbletter av rader i SQL Server, när dessa rader har en primärnyckel eller annan unik identifierarkolumn.

Tabellen innehåller med andra ord två eller flera rader som delar exakt samma värden i alla kolumner förutom dess unika identifierarkolumn.

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 (förutom DogId kolumn, som innehåller ett unikt värde över alla rader och kan användas som tabellens primära nyckelkolumn). Vi kan också se att de tre sista raderna är dubbletter (förutom DogId kolumn).

Den unika ID-kolumnen säkerställer att det inte finns några dubbletter av rader, vilket normalt är en mycket önskvärd egenskap i RDBMS. Men i det här fallet har det potential att störa vår förmåga att hitta dubbletter. Per definition säkerställer den unika ID-kolumnen att det inte finns några dubbletter. Lyckligtvis kan vi lösa detta problem ganska enkelt, vilket följande exempel visar.

Alternativ 1

Förmodligen är det enklaste/enklaste sättet att göra det med en enkel fråga som använder GROUP BY klausul:

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

Resultat:

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

Vi kunde utesluta kolumnen primärnyckel/unika ID genom att utelämna den från vår fråga.

Resultatet berättar att det finns tre rader som innehåller Wag Johnson och två rader som innehåller Bark Smith. Dessa är dubbletter (eller triplikat i fallet med Wag Johnson).

Alternativ 2

Vi kan utesluta icke-duplikat från resultatet genom att inkludera HAVING klausul i vår fråga:

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

Resultat:

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

Alternativ 3

Vi kan också kontrollera om det finns dubbletter på sammanlänkade kolumner. Till exempel kan vi använda CONCAT() funktion för att sammanfoga våra två kolumner:

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

Resultat:

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

Alternativ 4

Vi kan använda ROW_NUMBER() funktion med PARTITION BY sats för att skapa en ny kolumn med ett radnummer som ökar varje gång det finns en dubblett, men återställs igen när det finns en unik rad:

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

Resultat:

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

En fördel med den här metoden är att vi kan se varje dubblettrad, tillsammans med dess unika identifierarkolumn, på grund av det faktum att vi inte grupperar resultaten.

Alternativ 5

Vi kan också använda föregående exempel som ett vanligt tabelluttryck i en större fråga:

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

Det här alternativet utesluter icke-dubbletter från utdata.

Det exkluderar också exakt en rad av varje dubblett från utdata. Detta öppnar dörren för oss att vrida den sista SELECT * till en DELETE att de-dupera tabellen samtidigt som du behåller en av varje dubblett.

Alternativ 6

Här är ett mer kortfattat sätt att få samma utdata som föregående exempel:

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

Det här exemplet kräver inte att vi genererar vårt eget separata radnummer.

Alternativ 7

Och slutligen, här är en lite mer invecklad teknik för att returnera dubbletter av rader:

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

Även resultatet ser mer invecklat ut, men hey, det visar oss fortfarande dubbletterna!


  1. Ta bort dubbletter med endast en MySQL-fråga?

  2. Hitta och ersätt hela mysql-databasen

  3. Köra ett SSIS-paket med dtexec

  4. Hur DATE_FORMAT() fungerar i MariaDB