In de volgende voorbeelden wordt T-SQL gebruikt om dubbele rijen in SQL Server te verwijderen terwijl de primaire sleutel of de unieke id-kolom wordt genegeerd.
Meer specifiek verwijderen de voorbeelden dubbele rijen, maar behouden ze er een. Dus, gegeven twee identieke rijen, wordt de ene verwijderd en de andere blijft. Dit wordt vaak "deduplicatie" van de tabel genoemd, "deduplicatie" van de tabel, enz.
Voorbeeldgegevens
Stel dat we een tabel hebben met de volgende gegevens:
SELECT * FROM Dogs;
Resultaat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +---------+-------------+------------+
We kunnen zien dat de eerste twee rijen duplicaten zijn, en dat geldt ook voor de laatste drie rijen.
Optie 1
Laten we eerst de volgende code uitvoeren om te controleren welke rijen worden ontdubbeld:
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;
Resultaat:
+---------+-------------+------------+--------------+ | DogId | FirstName | LastName | Row_Number | |---------+-------------+------------+--------------| | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +---------+-------------+------------+--------------+
We gebruikten de ROW_NUMBER()
functie met de PARTITION BY
clausule om ons eigen rijnummer te maken dat wordt verhoogd wanneer er duplicaten worden gevonden en opnieuw wordt ingesteld wanneer een niet-duplicaat wordt gevonden. Een getal groter dan 1 geeft aan dat het een duplicaat is, en daarom retourneren we alleen rijen met een getal groter dan 1.
We kunnen zien dat drie rijen worden verwijderd wanneer we deze tabel ontdubbelen.
Laten we nu de tabel ontdubbelen:
WITH cte AS
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS Row_Number
FROM Dogs
)
DELETE FROM cte WHERE Row_Number <> 1;
Resultaat:
(3 rows affected)
Zoals verwacht zijn er drie rijen verwijderd.
Deze vraag is bijna identiek aan de vorige. Het enige wat we deden was SELECT *
veranderen op de laatste regel naar DELETE
.
Laten we nu alle rijen uit de tabel selecteren om te controleren of de juiste rijen zijn verwijderd:
SELECT * FROM Dogs;
Resultaat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | +---------+-------------+------------+
We kunnen zien dat elke hond nu maar één keer in de tabel voorkomt.
Optie 2
Ervan uitgaande dat de tabel is hersteld na het vorige voorbeeld, is hier een andere manier om te controleren op duplicaten:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
EXCEPT SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Resultaat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +---------+-------------+------------+
In dit geval hebben we gebruik gemaakt van de EXCEPT
operator samen met de MIN()
functie. We zouden MIN()
. kunnen vervangen met MAX()
afhankelijk van welke rijen we willen verwijderen.
Om de rijen te verwijderen, kunnen we eenvoudig SELECT *
. vervangen met DELETE
:
DELETE FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
EXCEPT SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Resultaat:
(3 rows affected)
En kijk wat er nog over is:
SELECT * FROM Dogs;
Resultaat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | +---------+-------------+------------+
Optie 3
Een andere manier om dit te doen is om de tafel op zichzelf te voegen en op die manier te controleren op duplicaten.
Ervan uitgaande dat de tabel is hersteld na het vorige voorbeeld, is hier onze derde optie voor het selecteren van duplicaten:
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
);
Resultaat:
+---------+-------------+------------+---------+-------------+------------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | |---------+-------------+------------+---------+-------------+------------| | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +---------+-------------+------------+---------+-------------+------------+
Dit resultaat is niet zo duidelijk als in het vorige voorbeeld, maar we kunnen nog steeds zien welke rijen duplicaten zijn.
Nu kunnen we die zoekopdracht aanpassen zodat we dubbele rijen verwijderen:
DELETE FROM Dogs WHERE DogId IN (
SELECT d2.DogId
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
)
);
Resultaat:
(3 rows affected)
Opnieuw werden drie rijen verwijderd.
Laten we de tabel nog eens bekijken:
SELECT * FROM Dogs;
Resultaat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 7 | Wag | Johnson | +---------+-------------+------------+
Het is u misschien opgevallen dat deze keer de andere rijen zijn verwijderd. Met andere woorden, we hebben nu DogId
s 2, 3, 4 en 7 terwijl we in de vorige voorbeelden 1, 3, 4 en 5 overhielden.
We kunnen dit voorbeeld eenvoudig wijzigen om dezelfde rijen als de vorige voorbeelden te verwijderen. Om dit te doen, kunnen we de MIN()
. gebruiken functie in plaats van de MAX()
functie:
DELETE FROM Dogs WHERE DogId IN (
SELECT d2.DogId
FROM Dogs d1, Dogs d2
WHERE d1.FirstName = d2.FirstName
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId
AND d1.DogId=(
SELECT MIN(DogId)
FROM Dogs d3
WHERE d3.FirstName = d1.FirstName
AND d3.LastName = d1.LastName
)
);