Her er syv måder at returnere duplikerede rækker i PostgreSQL, når disse rækker har en primær nøgle eller en anden unik identifikatorkolonne.
Det betyder, at de duplikerede rækker deler nøjagtig de samme værdier på tværs af alle kolonner med undtagelse af deres primære nøgle/unik ID-kolonne.
Eksempel på data
Vi bruger følgende data til vores eksempler:
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 første to rækker er dubletter (undtagen DogId
kolonne, som er tabellens primære nøgle og indeholder en unik værdi på tværs af alle rækker). De sidste tre rækker er også dubletter (bortset fra DogId
kolonne).
Primærnøglekolonnen sikrer, at der ikke er duplikerede rækker, hvilket er god praksis i RDBMS'er, fordi primærnøgler hjælper med at håndhæve dataintegritet. Men fordi primærnøgler forhindrer duplikerede rækker, har de potentialet til at forstyrre vores evne til at finde dubletter.
I vores tabel ovenfor er den primære nøglekolonne et stigende tal, og dens værdi har ingen betydning og er ikke signifikant. Vi er derfor nødt til at ignorere den række, hvis vi vil finde dubletter i de andre kolonner.
Mulighed 1
Vi kan bruge SQL GROUP BY
klausul for at gruppere kolonnerne efter deres signifikante kolonner, og brug derefter COUNT()
funktion for at returnere antallet af identiske rækker:
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
Her udelukkede vi primærnøglekolonnen ved at udelade den fra vores forespørgsel.
Resultatet fortæller os, at der er tre rækker indeholdende Wag Johnson og to rækker indeholdende Bark Smith. Disse er dubletter (eller triplikater i tilfældet med Wag Johnson). De to andre rækker har ingen dubletter.
Mulighed 2
Vi kan ekskludere ikke-duplikater fra outputtet 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
Mulighed 3
Her er et eksempel på kontrol for dubletter på sammenkædede kolonner. I dette tilfælde bruger vi CONCAT()
funktion til at sammenkæde vores to kolonner, brug DISTINCT
søgeord for at få forskellige værdier, og brug derefter COUNT()
funktion for at returnere antallet:
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
Mulighed 4
Vi kan alternativt bruge ROW_NUMBER()
vinduesfunktion:
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
Brug af PARTITION
klausul resulterer i, at der tilføjes en ny kolonne med et rækkenummer, der stiger hver gang der er en dublet, men nulstilles igen, når der er en unik række.
I dette tilfælde grupperer vi ikke resultaterne, hvilket betyder, at vi kan se hver dubletrække, inklusive dens unikke identifikatorkolonne.
Mulighed 5
Vi kan også bruge det forrige eksempel som et almindeligt tabeludtryk i en større forespørgsel:
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
Dette udelukker ikke-duplikater fra outputtet, og det udelukker en række af hver dublet fra outputtet. Med andre ord viser den kun de overskydende rækker fra dubletterne. Disse rækker er primære kandidater til at blive slettet i en de-duping-operation.
Mulighed 6
Her er en mere kortfattet måde at få det samme output som det forrige eksempel:
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 forskel mellem dette eksempel og det foregående er, at dette eksempel ikke kræver at generere vores eget separate rækkenummer.
Mulighed 7
Her er endnu en mulighed for at returnere duplikerede rækker 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