Самый простой способ - использовать CTE (общее табличное выражение). Я использую этот метод, когда у меня есть необработанные данные для импорта; первое, что я делаю, чтобы очистить его, чтобы убедиться, что нет дубликатов - у меня есть какой-то уникальный дескриптор для каждой строки.
Резюме:
WITH numbered AS (
SELECT ROW_NUMBER() OVER(PARTITION BY [dupe-column-list] ORDER BY [dupe-column-list]) AS _dupe_num FROM [table-name] WHERE 1=1
)
DELETE FROM numbered WHERE _dupe_num > 1;
В части "dupe-column-list" вы перечисляете все задействованные столбцы, где вы хотите, чтобы значения были уникальными. ORDER BY - это место, где вы решаете, в пределах набора дубликатов, какая строка «выигрывает», а какая удаляется. («ГДЕ 1 = 1» - это просто личная привычка.)
Причина, по которой это работает, заключается в том, что Sql Server хранит внутреннюю уникальную ссылку на каждую строку источника, выбранную в CTE. Таким образом, когда DELETE выполняется, он знает точную строку, которую нужно удалить, независимо от того, что вы поместили в список выбора вашего CTE. (Если вы нервничаете, вы можете изменить «УДАЛИТЬ» на «ВЫБОР *», но поскольку у вас есть дублирующиеся строки, это не поможет; если бы вы могли однозначно идентифицировать каждую строку, вы бы не читали это .)
Пример:
CREATE TABLE ##_dupes (col1 int, col2 int, col3 varchar(50));
INSERT INTO ##_dupes
VALUES (1, 1, 'one,one')
, (2, 2, 'two,two')
, (3, 3, 'three,three')
, (1, 1, 'one,one')
, (1, 2, 'one,two')
, (3, 3, 'three,three')
, (1, 1, 'one,one')
, (1, 2, '1,2');
Из 8 рядов у вас есть 5, связанных с дублирующимися проблемами; 3 ряда должны быть удалены. Вы можете увидеть проблемы с этим:
SELECT col1
, col2
, col3
, COUNT(1) AS _total
FROM ##_dupes
WHERE 1=1
GROUP BY col1, col2, col3
HAVING COUNT(1) > 1
ORDER BY _total DESC;
Теперь выполните следующий запрос, чтобы удалить дубликаты, оставив по 1 строке из каждого набора дубликатов.
WITH numbered AS (
SELECT ROW_NUMBER() OVER(PARTITION BY col1, col2, col3 ORDER BY col1, col2, col3) AS _dupe_num FROM ##_dupes WHERE 1=1
)
DELETE FROM numbered WHERE _dupe_num > 1;
Теперь у вас осталось 5 строк, ни один из которых не продублирован.