Her er syv muligheder for at finde duplikerede rækker i SQL Server, når disse rækker har en primær nøgle eller en anden unik identifikatorkolonne.
Tabellen indeholder med andre ord to eller flere rækker, der deler nøjagtig de samme værdier på tværs af alle kolonner undtagen dens unikke identifikatorkolonne.
Eksempeldata
Antag, at vi har en tabel med følgende 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, at de første to rækker er dubletter (undtagen DogId
kolonne, som indeholder en unik værdi på tværs af alle rækker og kan bruges som tabellens primære nøglekolonne). Vi kan også se, at de sidste tre rækker er dubletter (undtagen DogId
kolonne).
Den unikke ID-kolonne sikrer, at der ikke er duplikerede rækker, hvilket normalt er et meget ønskeligt træk i RDBMS'er. Men i dette tilfælde har det potentiale til at forstyrre vores evne til at finde dubletter. Per definition sikrer den unikke ID-kolonne, at der ikke er dubletter. Heldigvis kan vi løse dette problem ganske let, som de følgende eksempler viser.
Valgmulighed 1
Sandsynligvis den nemmeste/simpelste måde at gøre det på er med en simpel forespørgsel, der bruger 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 var i stand til at ekskludere den primære nøgle/det unikke ID-kolonne 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ælde af Wag Johnson).
Valgmulighed 2
Vi kan ekskludere ikke-duplikater fra resultatet ved at inkludere HAVING
klausul i vores forespørgsel:
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 | +-------------+------------+---------+
Valgmulighed 3
Vi kan også kontrollere for dubletter på sammenkædede kolonner. For eksempel kan vi bruge CONCAT()
funktion til at sammenkæde vores to kolonner:
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 | +---------------+---------+
Valgmulighed 4
Vi kan bruge ROW_NUMBER()
funktion med PARTITION BY
klausul for at oprette 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:
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 fordel ved denne metode er, at vi kan se hver eneste dubletrække sammen med dens unikke identifikatorkolonne på grund af det faktum, at vi ikke grupperer resultaterne.
Valgmulighed 5
Vi kan også bruge det foregående 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 | +---------+-------------+------------+--------------+
Denne indstilling udelukker ikke-duplikater fra outputtet.
Det udelukker også nøjagtig én række af hver dublet fra outputtet. Dette åbner døren for os til at dreje den sidste SELECT *
ind i en DELETE
at de-dupere tabellen, mens du beholder en af hver duplikat.
Valgmulighed 6
Her er en mere kortfattet måde at få det samme output som det foregående 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 | +-------+-----------+----------+ | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Dette eksempel kræver ikke generering af vores eget separate rækkenummer.
Valgmulighed 7
Og endelig, her er en lidt mere indviklet teknik til at returnere duplikerede rækker:
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 | +---------+-------------+------------+---------+-------------+------------+
Selv resultatet ser mere indviklet ud, men hey, det viser os stadig dubletterne!