Как удалить дубликаты строк в SQL Server?


416

Как я могу удалить повторяющиеся строки там, где их нет unique row id?

Мой стол

col1  col2 col3 col4 col5 col6 col7
john  1    1    1    1    1    1 
john  1    1    1    1    1    1
sally 2    2    2    2    2    2
sally 2    2    2    2    2    2

Я хочу остаться со следующим после удаления дубликата:

john  1    1    1    1    1    1
sally 2    2    2    2    2    2

Я пробовал несколько запросов, но я думаю, что они зависят от наличия идентификатора строки, так как я не получаю желаемого результата. Например:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)

5
Это не обманка первой ссылки. В этом вопросе нет идентификатора строки, а в связанном вопросе - идентификатор строки. Очень разные.
Alien Technology

измените 'SELECT id FROM таблицы GROUP BY id HAVING', чтобы иметь агрегированную функцию, например, MAX / MIN, и она должна работать.
испорченный

Ответы:


786

Я как КТР и ROW_NUMBERкак две совокупности позволяют нам видеть , какие строки будут удалены (или обновлены), поэтому просто изменить DELETE FROM CTE...на SELECT * FROM CTE:

WITH CTE AS(
   SELECT [col1], [col2], [col3], [col4], [col5], [col6], [col7],
       RN = ROW_NUMBER()OVER(PARTITION BY col1 ORDER BY col1)
   FROM dbo.Table1
)
DELETE FROM CTE WHERE RN > 1

DEMO (результат другой; я предполагаю, что это из-за опечатки с вашей стороны)

COL1    COL2    COL3    COL4    COL5    COL6    COL7
john    1        1       1       1       1       1
sally   2        2       2       2       2       2

Этот пример определяет дубликаты по одному столбцу col1из-за PARTITION BY col1. Если вы хотите включить несколько столбцов, просто добавьте их в PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)

2
Спасибо за отличный ответ. MSFT, напротив, имеет очень сложный ответ здесь: stackoverflow.com/questions/18390574/…
Барка

2
@ omachu23: в данном случае это не имеет значения, хотя я думаю, что это более эффективно в CTE, чем снаружи ( AND COl1='John'). Обычно вы должны применять фильтр в CTE.
Тим Шмельтер

1
@ omachu23: вы можете использовать любой SQL в КТР (кроме заказа), так что если вы хотите , чтобы фильтр по Джонсу: ...FROM dbo.Table1 WHERE Col1='John'. Вот скрипка: sqlfiddle.com/#!6/fae73/744/0
Тим

1
Самое простое решение может быть таким, set rowcount 1 delete from t1 where col1=1 and col2=1как показано здесь
Зоргарат

15
Этот ответ удалит только те строки, которые имеют дубликаты в столбце col1. Добавьте столбцы в «select» в «partition by», например, используя select в ответе: RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
rlee

159

Я бы предпочел CTE для удаления повторяющихся строк из таблицы сервера SQL

настоятельно рекомендуем следовать этой статье: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

сохраняя оригинал

WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)

DELETE FROM CTE WHERE RN<>1

не сохраняя оригинал

WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
 
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)

2
Оконная функция является отличным решением.
Роберт Кейси

2
Я немного смущен. Вы удалили его из CTE, а не из исходной таблицы. Итак, как это работает?
Bigeyes

8
@Bigeyes удаление записей из CTE удалит соответствующие записи из фактической физической таблицы (потому что CTE содержит ссылку на фактические записи).
Shamseer K

Я понятия не имел, что это был случай до этого поста ... Спасибо
Zakk Diaz

1
Почему вы хотите удалить как оригинал, так и его дубликат? Я не понимаю, почему вы не хотите просто удалить дубликаты и сохранить другой.
Богатый

52

Без использования, CTEи ROW_NUMBER()вы можете просто удалить записи, просто используя группу с помощью MAXфункции здесь и пример

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)

4
Этот запрос удалит не повторяющиеся записи.
Дерек Смоллс

8
Это отлично работает, спасибо. @DerekSmalls это не удаляет мои неповторяющиеся записи.
monteirobrena

1
Или вы можете сохранить оригинальные записи, используяMIN(ID)
Savage

18
DELETE from search
where id not in (
   select min(id) from search
   group by url
   having count(*)=1

   union

   SELECT min(id) FROM search
   group by url
   having count(*) > 1
)

Не могли бы вы переписать: где id (выбрать max (id) ... с количеством (*)> 1)?
Брент

1
Я не верю, что есть необходимость использовать имея или объединение, этого будет достаточно: удалить из поиска, где идентификатор не указан (выберите min (id) из группы поиска по URL)
Кристофер Ян

9

Пожалуйста, смотрите ниже способ удаления тоже.

Declare @table table
(col1 varchar(10),col2 int,col3 int, col4 int, col5 int, col6 int, col7 int)
Insert into @table values 
('john',1,1,1,1,1,1),
('john',1,1,1,1,1,1),
('sally',2,2,2,2,2,2),
('sally',2,2,2,2,2,2)

Создал образец таблицы с именем @tableи загрузил ее с заданными данными.

введите описание изображения здесь

Delete  aliasName from (
Select  *,
        ROW_NUMBER() over (Partition by col1,col2,col3,col4,col5,col6,col7 order by col1) as rowNumber
From    @table) aliasName 
Where   rowNumber > 1

Select * from @table

введите описание изображения здесь

Примечание: если вы даете все столбцы в Partition byдетали, то order byне имеют большого значения.

Я знаю, вопрос задают три года назад, и мой ответ - еще одна версия того, что Тим опубликовал, но публикация только в том случае, если это полезно для всех.


9

Если у вас нет ссылок, таких как внешние ключи, вы можете сделать это. Я делаю это много, когда проверяю доказательства концепции, а данные теста дублируются.

SELECT DISTINCT [col1],[col2],[col3],[col4],[col5],[col6],[col7]

INTO [newTable]

Зайдите в проводник объектов и удалите старую таблицу.

Переименуйте новую таблицу с именем старой таблицы.


Это самый простой способ, который я изучил в своих вступительных материалах и который я использую.
Эриком

7

У Microsoft есть очень аккуратное руководство по удалению дубликатов. Проверять, выписываться http://support.microsoft.com/kb/139444

Вкратце, вот самый простой способ удалить дубликаты, когда у вас есть всего несколько строк для удаления:

SET rowcount 1;
DELETE FROM t1 WHERE myprimarykey=1;

myprimarykey - это идентификатор строки.

Я установил rowcount в 1, потому что у меня было только две строки, которые были продублированы. Если бы у меня было 3 дублированных строки, я бы установил rowcount в 2, чтобы он удалял первые две, которые он видит, и оставлял только одну в таблице t1.

Надеюсь, это кому-нибудь поможет


1
Как я могу узнать, сколько строк я продублировал, если у меня есть 10k строк?
Fearghal

@Fearghal try "select primaryKey, count (*) из группы myTable по primaryKey;"
oabarca

1
Но что, если количество повторяющихся строк различно? то есть строка a имеет 2 записи, а строка b имеет 5 записей, а строка c не имеет повторяющихся записей
термит

1
@ user2070775 Что, если только подмножество всех строк имеют дубликаты, а некоторые из этих дубликатов дублируются дважды, а некоторые три или четыре раза?
термит

@ user2070775 Я пропустил ту часть, где вы сказали "просто удалить несколько строк". Также на странице есть предупреждение о настройке rowcount, что в будущих версиях sql это не повлияет на операторы update или delete
термит

6

Попробуй использовать:

SELECT linkorder
    ,Row_Number() OVER (
        PARTITION BY linkorder ORDER BY linkorder DESC
        ) AS RowNum
FROM u_links

введите описание изображения здесь


4

После попытки предложенного решения выше, это работает для небольших средних таблиц. Я могу предложить это решение для очень больших таблиц. так как он работает в итерациях.

  1. Отбросьте все виды зависимостей LargeSourceTable
  2. Вы можете найти зависимости с помощью sql managment studio, щелкните правой кнопкой мыши таблицу и выберите «Просмотр зависимостей»
  3. Переименуйте таблицу:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Создайте LargeSourceTableснова, но теперь добавьте первичный ключ со всеми столбцами, которые определяют дубликаты, добавьтеWITH (IGNORE_DUP_KEY = ON)
  6. Например:

    CREATE TABLE [dbo].[LargeSourceTable] ( ID int IDENTITY(1,1), [CreateDate] DATETIME CONSTRAINT [DF_LargeSourceTable_CreateDate] DEFAULT (getdate()) NOT NULL, [Column1] CHAR (36) NOT NULL, [Column2] NVARCHAR (100) NOT NULL, [Column3] CHAR (36) NOT NULL, PRIMARY KEY (Column1, Column2) WITH (IGNORE_DUP_KEY = ON) ); GO

  7. Снова создайте представления, которые вы отбросили в первую очередь для новой созданной таблицы.

  8. Теперь, запустите следующий сценарий sql, вы увидите результаты в 1000000 строк на странице, вы можете изменить номер строки на странице, чтобы увидеть результаты чаще.

  9. Обратите внимание, что я включил IDENTITY_INSERTи выключил, потому что один столбец содержит автоматически добавочный идентификатор, который я также копирую

SET IDENTITY_INSERT LargeSourceTable ON DECLARE @PageNumber AS INT, @RowspPage AS INT DECLARE @TotalRows AS INT declare @dt varchar(19) SET @PageNumber = 0 SET @RowspPage = 1000000 select @TotalRows = count (*) from LargeSourceTable_TEMP

While ((@PageNumber - 1) * @RowspPage < @TotalRows )
Begin
    begin transaction tran_inner
        ; with cte as
        (
            SELECT * FROM LargeSourceTable_TEMP ORDER BY ID
            OFFSET ((@PageNumber) * @RowspPage) ROWS
            FETCH NEXT @RowspPage ROWS ONLY
        )

        INSERT INTO LargeSourceTable 
        (
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        )       
        select 
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        from cte

    commit transaction tran_inner

    PRINT 'Page: ' + convert(varchar(10), @PageNumber)
    PRINT 'Transfered: ' + convert(varchar(20), @PageNumber * @RowspPage)
    PRINT 'Of: ' + convert(varchar(20), @TotalRows)

    SELECT @dt = convert(varchar(19), getdate(), 121)
    RAISERROR('Inserted on: %s', 0, 1, @dt) WITH NOWAIT
    SET @PageNumber = @PageNumber + 1
End

SET IDENTITY_INSERT LargeSourceTable OFF


4

Есть два решения в mysql:

A) Удалите дублирующиеся строки, используя DELETE JOINоператор

DELETE t1 FROM contacts t1
INNER JOIN contacts t2 
WHERE 
    t1.id < t2.id AND 
    t1.email = t2.email;

Этот запрос дважды обращается к таблице контактов, поэтому использует псевдоним таблицы t1и t2.

Выход:

1 запрос в порядке, 4 строки затронуты (0,10 с)

Если вы хотите удалить повторяющиеся строки и сохранить их lowest id, вы можете использовать следующую инструкцию:

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE
    c1.id > c2.id AND 
    c1.email = c2.email;

   

Б) Удалить дубликаты строк, используя промежуточную таблицу

Ниже показаны шаги по удалению повторяющихся строк с использованием промежуточной таблицы:

    1. Создайте новую таблицу со структурой, аналогичной исходной таблице, в которой вы хотите удалить повторяющиеся строки.

    2. Вставьте отдельные строки из исходной таблицы в непосредственную таблицу.

    3. Вставьте отдельные строки из исходной таблицы в непосредственную таблицу.

 

Шаг 1. Создайте новую таблицу, структура которой совпадает с исходной таблицей:

CREATE TABLE source_copy LIKE source;

Шаг 2. Вставьте отдельные строки из исходной таблицы в новую таблицу:

INSERT INTO source_copy
SELECT * FROM source
GROUP BY col; -- column that has duplicate values

Шаг 3. удалите исходную таблицу и переименуйте непосредственную таблицу в исходную.

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

Источник: http://www.mysqltutorial.org/mysql-delete-duplicate-rows/


2
-- this query will keep only one instance of a duplicate record.
;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY col1, col2, col3-- based on what? --can be multiple columns
                                       ORDER BY ( SELECT 0)) RN
         FROM   Mytable)



delete  FROM cte
WHERE  RN > 1

2

Вам нужно сгруппировать дубликаты записей в соответствии с полями, затем сохранить одну из записей и удалить остальные. Например:

DELETE prg.Person WHERE Id IN (
SELECT dublicateRow.Id FROM
(
select MIN(Id) MinId, NationalCode
 from  prg.Person group by NationalCode  having count(NationalCode ) > 1
 ) GroupSelect
 JOIN  prg.Person dublicateRow ON dublicateRow.NationalCode = GroupSelect.NationalCode 
 WHERE dublicateRow.Id <> GroupSelect.MinId)

2

Удаление дубликатов из огромной (несколько миллионов записей) таблицы может занять много времени. Я предлагаю вам сделать массовую вставку во временную таблицу выбранных строк, а не удалять.

--REWRITING YOUR CODE(TAKE NOTE OF THE 3RD LINE) WITH CTE AS(SELECT NAME,ROW_NUMBER() 
OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB) SELECT * INTO #unique_records FROM 
CTE WHERE ID =1;

2

Это может быть сделано многими способами на сервере sql, самый простой способ сделать это: вставить отдельные строки из таблицы повторяющихся строк в новую временную таблицу. Затем удалите все данные из таблицы повторяющихся строк, затем вставьте все данные из временной таблицы, в которой нет дубликатов, как показано ниже.

select distinct * into #tmp From table
   delete from table
   insert into table
   select * from #tmp drop table #tmp

   select * from table

Удалите повторяющиеся строки, используя Common Table Expression (CTE)

With CTE_Duplicates as 
(select id,name , row_number() 
over(partition by id,name order by id,name ) rownumber  from table  ) 
delete from CTE_Duplicates where rownumber!=1

1
with myCTE
as

(
select productName,ROW_NUMBER() over(PARTITION BY productName order by slno) as Duplicate from productDetails
)
Delete from myCTE where Duplicate>1

1

Со ссылкой на https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

Идея удаления дубликатов включает в себя

  • а) Защита тех строк, которые не повторяются
  • б) Сохраните одну из множества строк, которые квалифицируются как дубликаты.

Шаг за шагом

  • 1) Сначала определите строки, которые удовлетворяют определению дубликата, и вставьте их во временную таблицу, скажем, #tableAll.
  • 2) Выберите не дублирующиеся (одиночные строки) или отдельные строки во временной таблице, скажем, #tableUnique.
  • 3) Удалить из исходной таблицы, присоединяющейся к #tableAll, чтобы удалить дубликаты.
  • 4) Вставьте в исходную таблицу все строки из #tableUnique.
  • 5) Удалите #tableAll и #tableUnique

1

Если у вас есть возможность временно добавить столбец в таблицу, это решение подойдет мне:

ALTER TABLE dbo.DUPPEDTABLE ADD RowID INT NOT NULL IDENTITY(1,1)

Затем выполните УДАЛЕНИЕ, используя комбинацию MIN и GROUP BY

DELETE b
FROM dbo.DUPPEDTABLE b
WHERE b.RowID NOT IN (
                     SELECT MIN(RowID) AS RowID
                     FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
                     GROUP BY a.ITEM_NUMBER,
                              a.CHARACTERISTIC,
                              a.INTVALUE,
                              a.FLOATVALUE,
                              a.STRINGVALUE
                 );

Убедитесь, что УДАЛЕНИЕ выполнено правильно:

SELECT a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE, COUNT(*)--MIN(RowID) AS RowID
FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
GROUP BY a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE
ORDER BY COUNT(*) DESC 

В результате не должно быть строк с числом больше 1. Наконец, удалите столбец rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

Другой способ удаления дублированных строк без потери информации за один шаг:

delete from dublicated_table t1 (nolock)
join (
    select t2.dublicated_field
    , min(len(t2.field_kept)) as min_field_kept
    from dublicated_table t2 (nolock)
    group by t2.dublicated_field having COUNT(*)>1
) t3 
on t1.dublicated_field=t3.dublicated_field 
    and len(t1.field_kept)=t3.min_field_kept

0

Ого, я чувствую себя настолько глупо, когда готов ответить на все эти вопросы, они похожи на ответы экспертов со всеми CTE и временными таблицами и т. Д.

И все, что я сделал, чтобы заставить его работать, было просто агрегировать столбец ID с помощью MAX.

DELETE FROM table WHERE col1 IN (
    SELECT MAX(id) FROM table GROUP BY id HAVING ( COUNT(col1) > 1 )
)

ПРИМЕЧАНИЕ: вам может потребоваться запустить его несколько раз, чтобы удалить дубликаты, так как это приведет к удалению только одного набора дублирующихся строк за раз.


Это не будет работать, так как удалит все дубликаты, не оставляя оригиналов. ОП просит сохранить оригинальные записи.
0xdd

2
Не верно, max даст вам максимальный идентификатор, который удовлетворяет условию. Если это не так, докажите свою позицию для отрицательного голосования.
испорчен

0
DECLARE @TB TABLE(NAME VARCHAR(100));
INSERT INTO @TB VALUES ('Red'),('Red'),('Green'),('Blue'),('White'),('White')
--**Delete by Rank**
;WITH CTE AS(SELECT NAME,DENSE_RANK() OVER (PARTITION BY NAME ORDER BY NEWID()) ID FROM @TB)
DELETE FROM CTE WHERE ID>1
SELECT NAME FROM @TB;
--**Delete by Row Number** 
;WITH CTE AS(SELECT NAME,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB)
DELETE FROM CTE WHERE ID>1;
SELECT NAME FROM @TB;

Удаление дубликатов из огромной (несколько миллионов записей) таблицы может занять много времени. Я предлагаю сделать массовую вставку во временную таблицу выбранных строк, а скорее удалить. '- ЗАПИСЬ ВАШЕГО КОДА (ПРИМИТЕ 3-Й ЛИНИИ) С CTE КАК (ВЫБЕРИТЕ ИМЯ, ROW_NUMBER () ПЕРЕВЕРНИТЕ (РАЗДЕЛЕНИЕ ПО ИМЕНЕ ПОРЯДОК ИМЯ) ID ИЗ @TB) ВЫБРАТЬ * INTO #unique_records ИЗ CTE ГДЕ ID = 1; '
Эммануэль Булл

0
DELETE FROM TBL1  WHERE ID  IN
(SELECT ID FROM TBL1  a WHERE ID!=
(select MAX(ID) from TBL1  where DUPVAL=a.DUPVAL 
group by DUPVAL
having count(DUPVAL)>1))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.