Følgende eksempler bruger T-SQL til at slette duplikerede rækker i SQL Server, mens den primære nøgle eller den unikke identifikatorkolonne ignoreres.
Mere specifikt sletter eksemplerne duplikerede rækker, men beholder én. Så givet to identiske rækker, slettes den ene, og den anden forbliver. Dette omtales ofte som "de-dupering" af tabellen, "deduplikering" af tabellen osv.
Eksempel på data
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, og det samme er de sidste tre rækker.
Mulighed 1
Lad os først køre følgende kode for at kontrollere, hvilke rækker der skal de-duperes:
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 | +---------+-------------+------------+--------------+
Vi brugte ROW_NUMBER()
funktion med PARTITION BY
klausul for at oprette vores eget rækkenummer, der øges, når der findes dubletter, og nulstilles, når der findes et ikke-duplikat. Et tal større end 1 angiver, at det er en dublet, og derfor returnerer vi kun rækker, der har et tal større end 1.
Vi kan se, at tre rækker vil blive slettet, når vi de-duperer denne tabel.
Lad os nu dedupere tabellen:
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;
Resultat:
(3 rows affected)
Som forventet blev tre rækker slettet.
Denne forespørgsel er næsten identisk med den forrige. Det eneste, vi gjorde, var at ændre SELECT *
på den sidste linje til DELETE
.
Lad os nu vælge alle rækker fra tabellen for at bekræfte, at de korrekte rækker blev slettet:
SELECT * FROM Dogs;
Resultat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | +---------+-------------+------------+
Vi kan se, at hver hund nu kun optræder én gang i tabellen.
Mulighed 2
Hvis det antages, at tabellen er blevet gendannet efter det forrige eksempel, er her en anden måde at kontrollere for dubletter:
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 | +---------+-------------+------------+
I dette tilfælde brugte vi EXCEPT
operator sammen med MIN()
fungere. Vi kunne erstatte MIN()
med MAX()
afhængig af hvilke rækker vi ønsker skal slettes.
For at slette rækkerne kan vi blot erstatte SELECT *
med DELETE
:
DELETE FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
EXCEPT SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Resultat:
(3 rows affected)
Og tjek for at se, hvad der er tilbage:
SELECT * FROM Dogs;
Resultat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | +---------+-------------+------------+
Mulighed 3
En anden måde at gøre det på er at slutte sig til bordet på sig selv og tjekke for dubletter på den måde.
Hvis vi antager, at tabellen er blevet gendannet efter det forrige eksempel, er her vores tredje mulighed for at vælge dubletter:
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 | +---------+-------------+------------+---------+-------------+------------+
Dette resultat er ikke helt så klart som det i det foregående eksempel, men vi kan stadig se, hvilke rækker der er dubletter.
Nu kan vi ændre den forespørgsel, så vi sletter duplikerede rækker:
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
)
);
Resultat:
(3 rows affected)
Endnu en gang blev tre rækker slettet.
Lad os tjekke tabellen igen:
SELECT * FROM Dogs;
Resultat:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 7 | Wag | Johnson | +---------+-------------+------------+
Du vil måske bemærke, at denne gang blev de andre rækker slettet. Med andre ord har vi nu DogId
s 2, 3, 4 og 7, hvorimod vi i de foregående eksempler stod tilbage med 1, 3, 4 og 5.
Vi kan nemt ændre dette eksempel for at slette de samme rækker som de foregående eksempler. For at gøre dette kan vi bruge MIN()
funktion i stedet for MAX()
funktion:
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
)
);