Här är sju sätt att returnera dubbletter av rader i PostgreSQL när dessa rader har en primärnyckel eller annan unik identifierarkolumn.
Det betyder att dubblettraderna delar exakt samma värden i alla kolumner med undantag för deras primärnyckel/unika ID-kolumn.
Exempeldata
Vi använder följande data för våra exempel:
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
De två första raderna är dubbletter (förutom DogId
kolumn, som är tabellens primärnyckel och innehåller ett unikt värde över alla rader). De sista tre raderna är också dubbletter (förutom DogId
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 eftersom primärnycklar förhindrar dubbletter av rader har de potential att störa vår förmåga att hitta dubbletter.
I vår tabell ovan är primärnyckelkolumnen ett ökande tal, och dess värde har ingen betydelse och är inte signifikant. Vi måste därför ignorera den raden om vi vill hitta dubbletter i de andra kolumnerna.
Alternativ 1
Vi kan använda SQL GROUP BY
sats för att gruppera kolumnerna efter deras signifikanta kolumner, använd sedan COUNT()
funktion för att returnera antalet identiska rader:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;
Resultat:
firstname | lastname | count -----------+----------+------- Ruff | Robinson | 1 Wag | Johnson | 3 Woof | Jones | 1 Bark | Smith | 2
Här uteslöt vi primärnyckelkolumnen 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). De andra två raderna har inga dubbletter.
Alternativ 2
Vi kan utesluta icke-duplikat från utdata med HAVING
klausul:
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
Här är ett exempel på hur du söker efter dubbletter på sammanlänkade kolumner. I det här fallet använder vi CONCAT()
funktion för att sammanfoga våra två kolumner, använd DISTINCT
nyckelord för att få distinkta värden, använd sedan COUNT()
funktion för att returnera antalet:
SELECT
DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);
Resultat:
dogname | count ---------------+------- Wag Johnson | 3 Ruff Robinson | 1 Woof Jones | 1 Bark Smith | 2
Alternativ 4
Vi kan alternativt använda ROW_NUMBER()
fönsterfunktion:
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
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 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
Detta utesluter icke-dubbletter från utgången, och det utesluter en rad av varje dubblett från utgången. Med andra ord visar den bara de överflödiga raderna från dubbletterna. Dessa rader är främsta kandidater för att raderas i en de-dupingoperation.
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 -------+-----------+---------- 6 | Wag | Johnson 2 | Bark | Smith 7 | Wag | Johnson
En skillnad mellan det här exemplet och det föregående är att det här exemplet inte kräver att vi genererar vårt eget separata radnummer.
Alternativ 7
Här är ytterligare ett alternativ för att returnera dubbletter av rader i Postgres:
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