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 |