Следните примери използват T-SQL за изтриване на дублиращи се редове в SQL Server, като игнорират първичния ключ или колоната с уникален идентификатор.
По-конкретно, примерите изтриват дублиращи се редове, но запазват един. И така, като се имат предвид два еднакви реда, единият се изтрива, а другият остава. Това често се нарича „дедупиране“ на таблицата, „дедупликация“ на таблицата и т.н.
Примерни данни
Да предположим, че имаме таблица със следните данни:
SELECT * FROM Dogs;
Резултат:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +---------+-------------+------------+
Можем да видим, че първите два реда са дублирани, както и последните три реда.
Опция 1
Първо, нека изпълним следния код, за да проверим кои редове ще бъдат премахнати:
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;
Резултат:
+---------+-------------+------------+--------------+ | DogId | FirstName | LastName | Row_Number | |---------+-------------+------------+--------------| | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +---------+-------------+------------+--------------+
Използвахме ROW_NUMBER()
функция с PARTITION BY
клауза, за да създадем наш собствен номер на ред, който се увеличава, когато се намерят дубликати, и се нулира, когато се намери недубликат. Число, по-голямо от 1, показва, че е дубликат и затова връщаме само редове, които имат число, по-голямо от 1.
Можем да видим, че три реда ще бъдат изтрити, когато премахнем дублирането на тази таблица.
Сега нека премахнем измамата на таблицата:
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;
Резултат:
(3 rows affected)
Както се очакваше, три реда бяха изтрити.
Тази заявка е почти идентична с предишната. Всичко, което направихме, беше да променим SELECT *
на последния ред до DELETE
.
Сега нека изберем всички редове от таблицата, за да проверим дали правилните редове са изтрити:
SELECT * FROM Dogs;
Резултат:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | +---------+-------------+------------+
Можем да видим, че всяко куче сега се появява само веднъж в таблицата.
Опция 2
Ако приемем, че таблицата е била възстановена след предишния пример, ето друг начин за проверка за дубликати:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
EXCEPT SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Резултат:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +---------+-------------+------------+
В този случай използвахме EXCEPT
оператор заедно с MIN()
функция. Бихме могли да заменим MIN()
с MAX()
в зависимост от това кои редове искаме да бъдат изтрити.
За да изтрием редовете, можем просто да заменим SELECT *
с DELETE
:
DELETE FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
EXCEPT SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Резултат:
(3 rows affected)
И проверете какво остава:
SELECT * FROM Dogs;
Резултат:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | +---------+-------------+------------+
Опция 3
Друг начин да го направите е да присъедините таблицата към нея и да проверите за дубликати по този начин.
Ако приемем, че таблицата е била възстановена след предишния пример, ето нашата трета опция за избор на дубликати:
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
);
Резултат:
+---------+-------------+------------+---------+-------------+------------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | |---------+-------------+------------+---------+-------------+------------| | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +---------+-------------+------------+---------+-------------+------------+
Този резултат не е толкова ясен като този в предишния пример, но все пак можем да видим кои редове са дублирани.
Сега можем да модифицираме тази заявка, така че да изтрием дублиращи се редове:
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
)
);
Резултат:
(3 rows affected)
Отново три реда бяха изтрити.
Нека отново проверим таблицата:
SELECT * FROM Dogs;
Резултат:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 7 | Wag | Johnson | +---------+-------------+------------+
Може да забележите, че този път другите редове бяха изтрити. С други думи, сега имаме DogId
s 2, 3, 4 и 7, докато в предишните примери бяхме оставени с 1, 3, 4 и 5.
Можем лесно да променим този пример, за да изтрием същите редове като предишните примери. За да направим това, можем да използваме MIN()
функция вместо MAX()
функция:
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
)
);