sql >> Databasteknik >  >> RDS >> Oracle

11 sätt att hitta dubbletter av rader som har en primärnyckel i Oracle

Här är elva alternativ för att returnera dubbletter av rader i Oracle Database när dessa rader har en primärnyckel eller någon annan unik identifierarkolumn och du vill ignorera den.

Exempeldata

Vi använder följande data för våra exempel:

SELECT * FROM Dogs;

Resultat:

DOGID FIRSTNAME LASTNAME
1 Skall Smith
2 Skall Smith
3 Wuff Jones
4 Ruff Robinson
5 Wag Johnson
6 Wag Johnson
7 Wag Johnson

De två första raderna är dubbletter och de tre sista raderna är dubbletter. Dubblettraderna delar exakt samma värden i alla kolumner med undantag för deras primärnyckel/unika ID-kolumn.

Primärnyckelkolumnen säkerställer att det inte finns några dubbletter av rader, vilket är god praxis i RDBMS, eftersom primärnycklar hjälper till att framtvinga dataintegritet. Men det faktum att primärnycklar innehåller unika värden gör att vi måste ignorera den kolumnen när vi söker efter dubbletter.

I vår tabell ovan är primärnyckelkolumnen ett ökande tal, och dess värde har ingen betydelse och är inte signifikant. Vi kan därför ignorera kolumnens data när vi söker efter dubbletter.

Alternativ 1

Här är vårt första alternativ för att returnera dubbletter:

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

Resultat:

FÖRNAMN LASTNAME COUNT
Wag Johnson 3
Skäll Smith 2
Ruff Robinson 1
Wuff Jones 1

Här konstruerade vi vår fråga med GROUP BY så att utdata grupperas efter relevanta kolumner. Vi använde även COUNT() funktion för att returnera antalet identiska rader. Och vi beställde den efter antal i fallande ordning så att dubletterna visas först.

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). De andra två raderna har inga dubbletter.

Alternativ 2

Vi kan lägga till HAVING klausul till vårt tidigare exempel för att utesluta icke-dubbel från utdata:

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

Resultat:

FÖRNAMN LASTNAME COUNT
Wag Johnson 3
Skall Smith 2

Alternativ 3

Vi kan också kontrollera om det finns dubbletter på sammanlänkade kolumner. I det här fallet använder vi DISTINCT nyckelord för att få distinkta värden, använd sedan COUNT() funktion för att returnera antalet:

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

Resultat:

HUNDNAMN COUNT
Wag Johnson 3
Bark Smith 2
Ruff Robinson 1
Woof Jones 1

Alternativ 4

Varje rad i Oracle har en rovid pseudokolumn som returnerar adressen till raden. rovid är en unik identifierare för rader i tabellen, och vanligtvis identifierar dess värde unikt en rad i databasen (även om det är viktigt att notera att rader i olika tabeller som lagras tillsammans i samma kluster kan ha samma radid ).

Hur som helst, vi kan konstruera en fråga som använder rovid om vi vill:

SELECT * FROM Dogs
WHERE EXISTS (
  SELECT 1 FROM Dogs d2 
  WHERE Dogs.FirstName = d2.FirstName
  AND Dogs.LastName = d2.LastName
  AND Dogs.rowid > d2.rowid
);

Resultat:

DOGID FIRSTNAME LASTNAME
2 Skall Smith
6 Wag Johnson
7 Wag Johnson

Vi skulle kunna ersätta SELECT * med DELETE för att utföra en de-duping-operation på bordet.

Observera att vi kunde ha använt DogId kolumnen (vår primärnyckel) istället för rovid om vi ville. Som sagt, rovid kan vara användbart om du inte kan använda primärnyckelkolumnen av någon anledning, eller om tabellen inte har en primärnyckel.

Alternativ 5

Här är en annan fråga som använder rovid :

SELECT * FROM Dogs
WHERE rowid > (
  SELECT MIN(rowid) FROM Dogs d2  
  WHERE Dogs.FirstName = d2.FirstName
  AND Dogs.LastName = d2.LastName
);

Resultat:

DOGID FIRSTNAME LASTNAME
2 Skall Smith
6 Wag Johnson
7 Wag Johnson

Som med föregående exempel, kunde vi ersätta SELECT * med DELETE för att ta bort dubblettraderna.

Alternativ 6

De två rovid alternativen ovan är bra om du helt måste ignorera primärnyckeln i din fråga (eller om du inte har en primärnyckelkolumn alls). Men som nämnts finns det fortfarande möjlighet att ersätta rowid med primärnyckelkolumnen – i vårt fall DogId kolumn:

SELECT * FROM Dogs
WHERE EXISTS (
  SELECT 1 FROM Dogs d2 
  WHERE Dogs.FirstName = d2.FirstName
  AND Dogs.LastName = d2.LastName
  AND Dogs.DogId > d2.DogId
);

Resultat:

DOGID FIRSTNAME LASTNAME
2 Skall Smith
6 Wag Johnson
7 Wag Johnson

Alternativ 7

Och här är den andra frågan med rovid ersatt av DogId kolumn:

SELECT * FROM Dogs
WHERE DogId > (
  SELECT MIN(DogId) FROM Dogs d2  
  WHERE Dogs.FirstName = d2.FirstName
  AND Dogs.LastName = d2.LastName
);

Resultat:

DOGID FIRSTNAME LASTNAME
2 Skall Smith
6 Wag Johnson
7 Wag Johnson

Alternativ 8

Ett annat sätt att hitta dubbletter är att använda ROW_NUMBER() fönsterfunktion:

SELECT 
    DogId,
    FirstName,
    LastName,
    ROW_NUMBER() OVER ( 
        PARTITION BY FirstName, LastName 
        ORDER BY FirstName, LastName
        ) AS row_num
FROM Dogs;

Resultat:

DOGID FIRSTNAME LASTNAME ROW_NUM
1 Skall Smith 1
2 Skall Smith 2
4 Ruff Robinson 1
7 Wag Johnson 1
5 Wag Johnson 2
6 Wag Johnson 3
3 Wuff Jones 1

Använda PARTITION klausul resulterar i att en ny kolumn läggs till, med ett radnummer som ökar varje gång det finns en dubblett, men återställs igen när det finns en unik rad.

I det här fallet grupperar vi inte resultaten, vilket innebär att vi kan se varje dubblettrad, inklusive dess unika identifierarkolumn.

Alternativ 9

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

WITH cte AS 
    (
        SELECT 
            DogId,
            FirstName,
            LastName,
            ROW_NUMBER() OVER ( 
                PARTITION BY FirstName, LastName 
                ORDER BY FirstName, LastName
                ) AS row_num
        FROM Dogs
    )
SELECT * FROM cte WHERE row_num <> 1;

Resultat:

DOGID FIRSTNAME LASTNAME ROW_NUM
2 Skall Smith 2
5 Wag Johnson 2
6 Wag Johnson 3

Den frågan utesluter icke-dubbletter från utdata, och den exkluderar en rad av varje dubblett från utdata.

Alternativ 10

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

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

Resultat:

DOGID FIRSTNAME LASTNAME
2 Skall Smith
6 Wag Johnson
7 Wag Johnson

Det här exemplet använder Oracles MINUS operator, som endast returnerar unika rader som returneras av den första frågan men inte av den andra.

MINUS operatorn liknar EXCEPT operatör i andra DBMS, som SQL Server, MariaDB, PostgreSQL och SQLite.

Alternativ 11

Här är ännu ett alternativ för att välja dubbletter från vår tabell:

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 HUNDID FIRSTNAME LASTNAME
2 Skall Smith 1 Skall Smith
7 Wag Johnson 5 Wag Johnson
7 Wag Johnson 6 Wag Johnson

  1. Oracle WHILE LOOP Exempel

  2. ScaleGrid DigitalOcean-stöd för MySQL, PostgreSQL och Redis™ nu tillgängligt

  3. GROUP BY utan aggregatfunktion

  4. Är kapslade transaktioner tillåtna i MySQL?