Как удалить повторяющиеся строки?


1,113

Каков наилучший способ удаления повторяющихся строк из довольно большой таблицы SQL Server (т. Е. 300 000 строк)?

Строки, конечно, не будут идеальными дубликатами из-за существования поля идентификации RowID.

/* MyTable */ 

RowID int not null identity(1,1) primary key, 
Col1 varchar(20) not null, 
Col2 varchar(2048) not null, 
Col3 tinyint not null 
+12

Быстрый отзыв для пользователей PostgreSQL, читающих это (много, по частому привязке): Pg не предоставляет термины CTE в качестве обновляемых представлений, поэтому вы не можете напрямую «УДАЛИТЬ ОТ» термин CTE. См. Http://stackoverflow.com/q/18439054/398670 26 авг. 132013-08-26 07:59:12

  0

@CraigRinger то же самое верно для ** Sybase ** - я собрал оставшиеся решения здесь (должен быть действителен и для PG и других: http://stackoverflow.com/q/19544489/1855801 (просто замените ' ROWID() 'по столбцу RowID, если таковой имеется) 29 окт. 132013-10-29 06:41:33

+11

Просто добавьте оговорку здесь. При запуске любого де-дубликата ионный процесс, всегда проверяйте, что вы удаляете первым! Это одна из тех областей, где очень часто случайно удалять хорошие данные. 04 дек. 132013-12-04 01:45:38

1,028

Предполагая, что не аннулирует, вы GROUP BY уникальные колонны, и SELECTMIN (or MAX) RowId как ряд, чтобы сохранить. Затем, просто удалите все, что не есть строка ID:

DELETE FROM MyTable 
LEFT OUTER JOIN (
    SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
    FROM MyTable 
    GROUP BY Col1, Col2, Col3 
) as KeepRows ON 
    MyTable.RowId = KeepRows.RowId 
WHERE 
    KeepRows.RowId IS NULL 

В случае, если у вас есть GUID вместо целого, вы можете заменить

MIN(RowId) 

с

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn))) 
+284

Будет ли это работать? 'DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN (RowId) FROM MyTable GROUP BY Col1, Col2, Col3);' 23 сен. 102010-09-23 11:13:47

+1

Решение Aswsome! Кажется, для PostgreSQL вам нужен один подзапрос, который больше похож на https://gist.github.com/754805 25 дек. 102010-12-25 10:06:38

  0

@Georg: Я думаю, что так будет. Ваше решение короче и яснее. Не так уверен в производительности, может быть, это эквивалентно Mark's, но с действительно большими таблицами я бы, вероятно, придерживался LEFT JOIN. 12 янв. 112011-01-12 13:34:22

+1

@Andriy: Разве SQL не должен выбирать самый быстрый алгоритм * независимо от того, как структурирован SQL-запрос *? 12 янв. 112011-01-12 19:26:27

+1

@Georg: Если вы так говорите, сэр. :) Честно говоря, только здесь, на SO, я начал обращать внимание на такие проблемы, например, на разные структурированные запросы, приводящие к одному и тому же фактическому алгоритму или совсем другим способом, когда запросы кажутся очень незначительными. Из того, что я узнал до сих пор, я бы предпочел согласиться с вами. Это просто, что версия LEFT JOIN * кажется * (не более того) для меня более оптимизированной. 13 янв. 112011-01-13 05:52:53

+9

@Andriy - В SQL Server 'LEFT JOIN' менее эффективен, чем' НЕ EXISTS' http://sqlinthewild.co.za/index.php/2010/03/23/left-outer-join-vs-not-exists/Тот же сайт также сравнивает 'NOT IN' с' NOT EXISTS'. http://sqlinthewild.co.za/index.php/2010/02/18/not-exist-vs-not-in/Из 3 я думаю, что 'NOT EXISTS' лучше всего работает. Все три будут генерировать план с самосоединением, хотя этого можно избежать. 14 янв. 112011-01-14 09:17:50

  0

@Martin: Очень интересно, спасибо. И я собираюсь сделать некоторые тесты, подобные описанным, только с самопроизвольными таблицами, как более применимыми к этому вопросу. 14 янв. 112011-01-14 09:46:46

+8

@Martin, @Georg: Итак, я сделал небольшой тест. Большая таблица была создана и заполнена, как описано здесь: http://sqlinthewild.co.za/index.php/2010/03/23/left-outer-join-vs-not-exists/ Затем были созданы два SELECT, один используя метод LEFT JOIN + WHERE IS NULL, другой - с помощью NOT IN. Затем я продолжил выполнение планов и угадал, что? Стоимость запроса составила 18% для LEFT JOIN против 82% для NOT IN, * большой * сюрприз для меня. Я мог бы сделать что-то, чего не хотел бы, или наоборот, что, если это правда, мне бы очень хотелось узнать. 14 янв. 112011-01-14 10:23:10

+1

Приходит очень поздно, я знаю, но http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/. Если столбцы были обнуляемы, NOT IN ведет себя по-другому и работает ужасно. Поэтому я рекомендую НЕ СУЩЕСТВУЮТ. 06 июл. 112011-07-06 07:54:40

+1

Я искал такое простое решение уже более получаса. Я встретил решения с помощью 'DELETE TOP (n)' с помощью курсоров, и никакие решения не близки к вашим. Ваше решение является быстрым и быстрым, и делает именно то, что от него ожидается. Спасибо, что поделились такими замечательными знаниями! =) 26 ноя. 112011-11-26 22:09:52

  0

Используйте CONVERT (uniqueidentifier, MAX (CONVERT (char (36), MyGuidColumn))), если у вас есть идентификатор GUID вместо целое число. 10 авг. 122012-08-10 11:56:17

+2

Удивительно, насколько сложно это объяснить, насколько распространена эта проблема, - работали над несколькими проектами, где это необходимо. Core SQL действительно взывает к более простому способу сделать это, особенно учитывая рейтинги и количество комментариев этого вопроса и других подобных. 17 апр. 132013-04-17 19:23:10

+15

@ GeorgSchölly предоставил элегантный ответ. Я использовал его на столе, где моя ошибка PHP создала повторяющиеся строки. 28 май. 132013-05-28 17:02:13

  0

Насколько я знаю, 'RowId' не существует для Sql Server, это функция Oracle, и вопрос помечен как' sql-server'. Я прав? 12 сен. 132013-09-12 11:17:14

  0

@SysDragon - В этом случае 'RowId' - это просто имя столбца. Это не имеет особого значения. В Oracle RowId в SQL Server нет [нет прямого эквивалента] (http://stackoverflow.com/questions/909155/equivalent-of-oracles-rowid-in-sql-server). 06 окт. 132013-10-06 14:12:31

+12

Извините, но почему '' DELETE MyTable FROM MyTable' правильный синтаксис? Я не вижу поместить имя таблицы сразу после 'DELETE' в качестве опции в документации [здесь] (http://technet.microsoft.com/en-us/ библиотека/ms189835 (v = sql.110) .aspx). Извините, если это очевидно для других, я новичок в SQL, просто пытаюсь учиться. Более важно, чем это работает: в чем разница между включением имени из таблицы есть или нет? 06 ноя. 132013-11-06 20:28:03

+1

@ GeorgSchölly - ваше предложение, похоже, не работает в MySQL, к сожалению. Я думаю, что он жалуется, что это циклический запрос. 19 фев. 142014-02-19 10:34:44

+3

@ GeorgSchölly: это утверждение также работает в SQLite. Спасибо! 28 мар. 142014-03-28 19:59:46

+1

@MarkBrackett: Tnx, yr query тоже помог мне, но он просто не работает для одной из моих больших таблиц (около 2 000 000 строк), поэтому я выбрал индекс, но все равно это занимает много времени, и в конце ничего не происходит! Я не знаю, является ли проблема для моего присоединения или выбора индекса по ошибке или что-то еще? 24 июн. 142014-06-24 14:21:36

+1

@ Ошибки решения Georg: вы не можете указывать целевые таблицы «продукты» для обновления в разделе FROM. 20 окт. 142014-10-20 17:32:30

+1

@ GeorgSchölly, ваш запрос возвращает эту ошибку "# 1093 - Вы не можете указать целевую таблицу' MyTable' для обновления в предложении FROM " 13 июн. 152015-06-13 19:06:24

+2

Следует иметь в виду, что если ваша таблица активна (то есть, вставляя новые записи, все время), тогда лучше запустить этот запрос с ограниченным периодом времени, который заканчивается непосредственно перед текущим временем. Поскольку самосоединение может привести к несоответствию, если новые строки будут прочитаны внешним запросом, но не подзапросом. В этом случае не дублированные строки могут быть удалены. 26 июн. 152015-06-26 14:24:20

+2

@Georg: для таблицы с очень большим количеством строк, где только очень мало дубликатов, которые должны быть удалены, инвертирование запроса для уменьшения количества параметров IN может сделать запрос намного быстрее: 'УДАЛИТЬ ИЗ myTable WHERE id IN ( SELECT, идентификатор из MyTable КРОМЕ (SELECT MIN (ID) идентификатор FROM MyTable GROUP BY col1, col2, cOL3)); ' 12 авг. 152015-08-12 15:08:13

+3

@levininja - видно из table_source (расширение T-SQL, который позволяет ОТ и присоединиться DELETE) и FROM table_alias (необязательный параметр FROM); первый MyTable - table_alias, второй - table_source. 20 сен. 152015-09-20 10:58:29

+1

Вот ответ @ MarkBrackett для Postgres: 'delete from MyTable, где не существует (выберите 1 из (выберите min (RowId) как RowId из группы MyTable по Col1, Col2, Col3) в качестве KeepRows, где MyTable.RowId = KeepRows.RowId); 'Это слишком медленно для строк 7M. 07 ноя. 152015-11-07 05:08:00

+1

@MarkBrackett Является ли ваш комментарий 9/20/15, что ваш ответ работает только в T-SQL? Это было бы полезно для скиммеров. 22 июл. 162016-07-22 22:13:46

  0

@StefanSteiger Что такое uniqueidentifier в вашем комментарии? второй CONVERT понятен, но первый я не понимаю. могли бы вы объяснить? CONVERT (уникальный идентификатор, MIN (CONVERT (char (36), MyGuidColumn))) 11 фев. 182018-02-11 04:50:08

  0

@MarkBrackett, что является единственным уникальным идентификатором в CONVERT (uniqueidentifier, MIN (CONVERT (char (36), MyGuidColumn)))? это имя столбца или тип данных или что-то еще? 11 фев. 182018-02-11 20:35:58

  0

@ericyoung: uniqueidentifier - v4-uuid. Это тип данных. Первый конверт состоит в том, что min не может применяться к уникальному идентификатору типа данных. 12 фев. 182018-02-12 01:40:27

  0

Используя предложение от @ GeorgSchölly, я получил сообщение об указании таблицы в предложении FROM, которое я пытаюсь обновить. Чтобы исправить это, мне пришлось изменить запрос с помощью подзаголовка в подзапросе: «УДАЛИТЬ ИЗ МЫТЬЮ ГДЕ ЕСТЬ RowId NOT IN (SELECT MIN (RowId) FROM (SELECT * FROM MyTable) AS MyTableSubselect GROUP BY Col1, Col2, Col3);' 27 фев. 182018-02-27 13:44:05


124

На сайте поддержки Microsoft есть хорошая статья о removing duplicates. Это довольно консервативно - они делают все по-отдельности, но должны хорошо работать против больших таблиц.

Я использовал Самосоединения сделать это в прошлом, хотя это, вероятно, можно было бы prettied вверх с HAVING пункта:

DELETE dupes 
FROM MyTable dupes, MyTable fullTable 
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField 

12

Вот еще одна хорошая статья на removing duplicates.

Он обсуждает вопрос, почему его трудно: «SQL основываются на реляционной алгебре, и дубликаты не могут происходить в реляционной алгебре, потому что дубликаты не допускаются в наборе»

ТЕмп решение таблицы и два MySQL Примеры.

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

+1

SQL основан на нескольких наборах. Но даже если он был основан на наборах, эти два набора (1, a) & (2, a) различны. 17 окт. 112011-10-17 23:50:53


12

Да, конечно. Используйте временную таблицу. Если вы хотите один, не-очень-производительную о том, что «работает» вы можете пойти с:

DELETE FROM MyTable WHERE NOT RowID IN 
    (SELECT 
     (SELECT TOP 1 RowID FROM MyTable mt2 
     WHERE mt2.Col1 = mt.Col1 
     AND mt2.Col2 = mt.Col2 
     AND mt2.Col3 = mt.Col3) 
    FROM MyTable mt) 

В принципе, для каждой строки в таблице, к югу выберите находит верхнюю RowId всех строк, которые точно так же, как рассматриваемая строка. Таким образом, вы получаете список RowID, которые представляют собой «оригинальные» не дублированные строки.


7

From the application level (unfortunately). I agree that the proper way to prevent duplication is at the database level through the use of a unique index, but in SQL Server 2005, an index is allowed to be only 900 bytes, and my varchar(2048) field blows that away.

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

-- given a table stories(story_id int not null primary key, story varchar(max) not null) 
CREATE TRIGGER prevent_plagiarism 
ON stories 
after INSERT, UPDATE 
AS 
    DECLARE @cnt AS INT 

    SELECT @cnt = Count(*) 
    FROM stories 
      INNER JOIN inserted 
        ON (stories.story = inserted.story 
         AND stories.story_id != inserted.story_id) 

    IF @cnt > 0 
     BEGIN 
      RAISERROR('plagiarism detected',16,1) 

      ROLLBACK TRANSACTION 
     END 

Кроме того, VARCHAR (2048) звучит тусклый мне (некоторые вещи в жизни 2048 байт, но это довольно редко); если это действительно не варчар (макс)?


9
  1. Создать новую пустую таблицу с той же структурой

  2. Выполнить запрос как этот

    INSERT INTO tc_category1 
    SELECT * 
    FROM tc_category 
    GROUP BY category_id, application_id 
    HAVING count(*) > 1 
    
  3. Затем выполнить этот запрос

    INSERT INTO tc_category1 
    SELECT * 
    FROM tc_category 
    GROUP BY category_id, application_id 
    HAVING count(*) = 1 
    

11

Я был стол где мне нужно было сохранить не дублированные строки. Я не уверен в скорости или эффективности.

DELETE FROM myTable WHERE RowID IN (
    SELECT MIN(RowID) AS IDNo FROM myTable 
    GROUP BY Col1, Col2, Col3 
    HAVING COUNT(*) = 2) 
+7

Предполагается, что не более 1 дубликата. 02 окт. 102010-10-02 20:11:07

  0

Почему бы не «СЧИТАТЬ (*)> 1'? 07 окт. 142014-10-07 09:26:36


679

Другой возможный способ сделать это

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above 
WITH cte 
    AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
             ORDER BY (SELECT 0)) RN 
     FROM #MyTable) 
DELETE FROM cte 
WHERE RN > 1; 

Я использую ORDER BY (SELECT 0) выше, как это произвольная строка, которая для сохранения в случае равенства.

Чтобы сохранить последний один в RowID порядке, например, вы могли бы использовать ORDER BY RowID DESC

Исполнение Планы

План выполнения этого часто проще и эффективнее, чем в принятом ответе, как это делает не требуется самоподключение.

Execution Plans

Это не всегда так. Одним из мест, где может быть предпочтительным решение GROUP BY, является ситуация, когда hash aggregate будет выбираться предпочтительнее агрегата потока.

Решение ROW_NUMBER всегда будет давать практически такой же план, тогда как стратегия GROUP BY более гибкая.

Execution Plans

Факторы, которые могут благоприятствуют хэш совокупный подход будет

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

В экстремальных версиях этого второго случая (если в каждом из них очень мало групп с большим количеством дубликатов) ld также рассмотрим просто вставку строк для сохранения в новую таблицу, а затем TRUNCATE - оригинал и копирование их назад, чтобы минимизировать ведение журнала по сравнению с удалением очень большой доли строк.

+21

Если я могу добавить: принятый ответ не работает с таблицами, которые используют 'uniqueidentifier'. Это намного проще и отлично работает на любой таблице. Спасибо Мартину. 16 ноя. 102010-11-16 15:50:44

+1

Это единственное решение, которое работает на моей большой таблице (30M строк). Хотел бы я дать больше, чем +1 12 июл. 112011-07-12 11:11:09

+13

Это такой замечательный ответ! Это сработало, когда я удалил старый ПК, прежде чем понял, где дублируются. +100 19 июл. 112011-07-19 19:55:29

+12

Предлагаю попросить, а затем ответить на этот вопрос (с этим ответом) на DBA.SE. Затем мы можем добавить его в [наш список канонических ответов] (http://meta.dba.stackexchange.com/q/708/2660). 05 июн. 122012-06-05 23:49:04

  0

Кто-нибудь знает, как я могу вернуть количество дубликатов записей в этом же запросе, а также удалить их? Я считаю, что при использовании оператора СЬ можно только почитать временный код один раз, правильно? 03 сен. 122012-09-03 13:06:48

  0

@DDiVita. Если вы просто хотите узнать, сколько строк было удалено, посмотрите на вкладке сообщений в SSMS для сообщения, затронутого строк, для более сложных потребностей, посмотрите на предложение 'OUTPUT'. 03 сен. 122012-09-03 13:12:05

+14

В отличие от принятого ответа, это также работало над таблицей, в которой не было ключа ('RowId') для сравнения. 04 мар. 132013-03-04 17:39:32

  0

Синтаксис синтаксиса: delete t from (выберите ROW_NUMBER() OVER (PARTITION BY name ORDER BY (SELECT 0)) как rn from @table) t где rn> 1 10 июл. 132013-07-10 03:57:55

  0

Отличное решение, которое также можно использовать для таблиц с составным первичным ключом. 27 авг. 132013-08-27 12:12:55

  0

Просто FYI эта статья о codeproject работает как wellhttp: //www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server 02 мар. 142014-03-02 02:49:49

  0

@Joe Уже упоминалось [в этом ответе] (http://stackoverflow.com/a/18086447/73226). Если вы не застряли на SQL Server 2000, который выглядит излишне громоздким и неэффективным по сравнению с «ROW_NUMBER». 02 мар. 142014-03-02 11:53:32

+7

. Это не работает на всех версиях SQL-сервера, с другой стороны. 24 мар. 152015-03-24 15:31:48

+5

@David - работает с 2005+. Сейчас 2015 год. 24 мар. 152015-03-24 15:32:51

  0

можно объяснить, как выражение стандартной инструкции (CTE) удаляет результаты во временной таблице #MyTable? 14 июл. 152015-07-14 10:42:01

  0

@adam - То же самое, что удаляет строки из представления.CTE должен соответствовать критериям обновляемых представлений, чтобы [Database Engine должен был иметь возможность однозначно отслеживать изменения из определения представления в одну базовую таблицу.] (Https://msdn.microsoft.com/en-GB/library/ ms187956.aspx) 14 июл. 152015-07-14 12:45:24

+1

Вот ответ @ MartinSmith для Postgres: 'cte as (select id, row_number() over (раздел по порядку Col1 по id) как rn из MyTable) удалить из MyTable, где id (select id from cte, где rn> 1) ; 'Это оставит самый младший первичный ключ и удалит остальные (' order by id'). 07 ноя. 152015-11-07 23:56:35


51
delete t1 
from table t1, table t2 
where t1.columnA = t2.columnA 
and t1.rowid>t2.rowid 

Postgres:

delete 
from table t1 
using table t2 
where t1.columnA = t2.columnA 
and t1.rowid > t2.rowid 
  0

Зачем размещать решение Postgres на SQL Вопрос сервера? 08 мар. 162016-03-08 14:34:07

  0

@Lankymart Так как пользователи postgres тоже приходят сюда. Посмотрите на оценку этого ответа. 15 янв. 182018-01-15 15:38:19

  0

@Gabriel, в чем ваш смысл? Он популярен, поэтому давайте позаботимся о тех, кто приходит к неправильному вопросу ... sheesh. 15 янв. 182018-01-15 18:07:32

  0

Я видел это в некоторых популярных вопросах SQL, как в [здесь] (https://stackoverflow.com/questions/3800551/select-first-row-in-each-group-by-group), [здесь] (https://stackoverflow.com/questions/1293330/how-can-i-do-an-update-statement-with-join-in-sql) и [здесь] (https://stackoverflow.com/questions/ 194852/конкатенации-много-строки-в-а-одной текстовой строки). ОП получил свой ответ, и все остальные тоже получили помощь. Нет проблем ИМХО. 15 янв. 182018-01-15 19:39:13


84

Следующий запрос полезен для удаления повторяющихся строк.Таблица в этом примере содержит ID в качестве столбца идентификации, а столбцы с двойными данными - Column1, Column2 и Column3.

DELETE FROM TableName 
WHERE ID NOT IN (SELECT MAX(ID) 
        FROM TableName 
        GROUP BY Column1, 
          Column2, 
          Column3 
        /*Even if ID is not null-able SQL Server treats MAX(ID) as potentially 
        nullable. Because of semantics of NOT IN (NULL) including the clause 
        below can simplify the plan*/ 
        HAVING MAX(ID) IS NOT NULL) 

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

SELECT YourColumnName, 
     COUNT(*) TotalCount 
FROM YourTableName 
GROUP BY YourColumnName 
HAVING COUNT(*) > 1 
ORDER BY COUNT(*) DESC 
+1

Ошибка MySQL с первым скриптом «Вы не можете указать целевую таблицу« TableName »для обновления в разделе FROM» 13 июн. 122012-06-13 10:54:29

  0

Помимо ошибки, о которой сообщал Д.Росадо, ваш первый запрос также очень медленный. Соответствующий запрос SELECT взял мою настройку + - в 20 раз дольше, чем принятый ответ. 03 янв. 132013-01-03 08:24:54

+7

@parvus - вопрос помечен SQL Server, а не MySQL. Синтаксис в SQL Server прекрасен. Кроме того, MySQL, как известно, плохо оптимизирует подзапросы [см. Здесь здесь] (http://stackoverflow.com/q/3417074/73226). Этот ответ в SQL Server отлично. Фактически 'NOT IN' часто работает лучше, чем' OUTER JOIN ... NULL'. Я бы добавил в запрос 'HAVING MAX (ID) NOT NULL', хотя, хотя семантически это не было необходимо, поскольку это может улучшить план [пример этого здесь] (http://dba.stackexchange.com/questions/31135/delete-duplicate-records-when-use-text-datatype/31145 # 31145) 04 янв. 132013-01-04 16:27:14

+1

Отлично работает в PostgreSQL 8.4. 05 май. 142014-05-05 23:48:21


9

Используя нижеследующий запрос, мы можем удалять повторяющиеся записи на основе одного столбца или нескольких столбцов. ниже запрос удаляется на основе двух столбцов. Название таблицы: testing и имена столбцов empno,empname

DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1) 
or empname not in 
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1) 

6
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int) 

INSERT INTO car(PersonId,CarId) 
VALUES(1,2),(1,3),(1,2),(2,4) 

--SELECT * FROM car 

;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car) 

DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1) 

15
SELECT DISTINCT * 
     INTO tempdb.dbo.tmpTable 
FROM myTable 

TRUNCATE TABLE myTable 
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable 
DROP TABLE tempdb.dbo.tmpTable 
+5

Усечение не будет работать, если у вас есть ссылки на внешние ключи для myTable. 27 июн. 132013-06-27 14:58:12


7

Я хотел бы упомянуть этот подход, а также может быть полезным, и работает на всех серверах SQL: Довольно часто существует только один - два дубликата, идентификаторы и количество дубликатов. В этом случае:

SET ROWCOUNT 1 -- or set to number of rows to be deleted 
delete from myTable where RowId = DuplicatedID 
SET ROWCOUNT 0 

21

Быстрый и Грязный удалить точную дублированные строки (для небольших таблиц):

select distinct * into t2 from t1; 
delete from t1; 
insert into t1 select * from t2; 
drop table t2; 
+3

Обратите внимание, что вопрос фактически указывает неточное дублирование (dueto row id). 16 июл. 152015-07-16 12:00:39


13

Еще одно простое решение можно найти по ссылке вставили here. Это легко понять и кажется эффективным для большинства подобных проблем. Это для SQL Server, но использование этой концепции более чем приемлемо.

Вот соответствующие части от связанной страницы:

Рассмотрим эти данные:

EMPLOYEE_ID ATTENDANCE_DATE 
A001 2011-01-01 
A001 2011-01-01 
A002 2011-01-01 
A002 2011-01-01 
A002 2011-01-01 
A003 2011-01-01 

Так как же мы можем удалить эти дубликаты данных?

Во-первых, вставить столбец идентификаторов в этой таблице, используя следующий код:

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1) 

Используйте следующий код, чтобы разрешить его:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _ 
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 
+1

«Легко понять», «похоже, эффективный ", но не слово о том, что представляет собой метод. Представьте, что ссылка становится недействительной, какое использование тогда должно было бы знать, что метод * был легко понятен и эффективен? Пожалуйста, подумайте о том, чтобы добавить важные детали описания метода в свой пост, иначе это не ответ. 07 авг. 132013-08-07 12:42:07

  0

Этот метод полезен для таблиц, в которых у вас еще нет определенного идентификатора. Часто вам нужно избавиться от дубликатов, чтобы определить первичный ключ! 05 ноя. 132013-11-05 21:25:39

  0

@JeffDavis - работает версия ROW_NUMBER отлично подходит для этого случая, и вам не нужно будет доходить до того, чтобы добавить новый столбец, прежде чем начать. 02 мар. 142014-03-02 11:51:27


36

Это удалит повторяющиеся строки, кроме первого строка

DELETE 
FROM 
    Mytable 
WHERE 
    RowID NOT IN (
     SELECT 
      MIN(RowID) 
     FROM 
      Mytable 
     GROUP BY 
      Col1, 
      Col2, 
      Col3 
    ) 

см (http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server)

+8

Для mysql он даст ошибку: Ошибка Код: 1093. Вы не можете указывать целевую таблицу «Mytable» для обновления в предложении FROM. но это небольшое изменение будет работать для mysql: DELETE FROM Mytable WHERE RowID NOT IN (SELECT ID FROM (SELECT MIN (RowID) AS ID FROM Mytable GROUP BY Col1, Col2, Col3) AS TEMP) 29 фев. 162016-02-29 18:33:30


10

Другой способ: Создайте новый стол с одинаковыми полями и с уникальным указателем. Затем переместите все данные из старой таблицы в новую таблицу. Автоматически игнорируется SQL SERVER (существует также опция о том, что делать, если будет повторяющееся значение: игнорировать, прерывать или sth) дублировать значения. Таким образом, у нас есть одна и та же таблица без повторяющихся строк. Если вы не хотите уникальный индекс, после передачи данных вы можете отказаться от него.

В частности, для больших таблиц вы можете использовать DTS (пакет SSIS для импорта/экспорта данных), чтобы быстро переносить все данные в новую таблицу с уникальной индексацией. Для 7 миллионов строк требуется всего несколько минут.


7
DELETE 
FROM 
    table_name T1 
WHERE 
    rowid > (
     SELECT 
      min(rowid) 
     FROM 
      table_name T2 
     WHERE 
      T1.column_name = T2.column_name 
    ); 
  0

Привет, Teena, вы пропустили таблицу Alice name T1 после комментария об удалении, иначе это будет синтаксическое исключение. 01 авг. 172017-08-01 11:21:34


6
DELETE 
FROM MyTable 
WHERE NOT EXISTS (
       SELECT min(RowID) 
       FROM Mytable 
       WHERE (SELECT RowID 
        FROM Mytable 
        GROUP BY Col1, Col2, Col3 
        )) 
       ); 

13

Я думал, что я разделю свое решение, так как он работает в особых условиях. В моем случае таблица с повторяющимися значениями не имела внешнего ключа (потому что значения были дублированы из другого db).

begin transaction 
-- create temp table with identical structure as source table 
Select * Into #temp From tableName Where 1 = 2 

-- insert distinct values into temp 
insert into #temp 
select distinct * 
from tableName 

-- delete from source 
delete from tableName 

-- insert into source from temp 
insert into tableName 
select * 
from #temp 

rollback transaction 
-- if this works, change rollback to commit and execute again to keep you changes!! 

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


16

Я предпочитаю подзапрос \ с count (*)> 1 решением для внутреннего соединения, потому что мне было легче его читать, и было очень легко перейдите в инструкцию SELECT, чтобы проверить, что будет удалено, прежде чем запускать ее.

--DELETE FROM table1 
--WHERE id IN ( 
    SELECT MIN(id) FROM table1 
    GROUP BY col1, col2, col3 
    -- could add a WHERE clause here to further filter 
    HAVING count(*) > 1 
--) 
  0

Не удаляет ли он все записи, которые отображаются во внутреннем запросе. Нам нужно удалить только дубликаты и сохранить оригинал. 15 май. 152015-05-15 20:05:21

+2

Вы возвращаете только тот, у которого самый низкий id, на основе min (id) в предложении select. 18 май. 152015-05-18 00:24:51

  0

Да, но вопрос не спрашивал о том, как вернуть строки, которые нужно удалить, но он спрашивает, как удалить повторяющиеся строки. Можете ли вы рассказать о том, как я могу удалить строки, которые возвратил запрос? 18 май. 152015-05-18 13:36:35

+2

Раскомментируйте первую, вторую и последнюю строки запроса. 18 май. 152015-05-18 16:25:25

+6

Это не очистит все дубликаты. Если у вас есть три строки, которые являются дубликатами, она будет выбирать только строку с MIN (id) и удалить ее, оставив две строки слева, которые являются дубликатами. 06 ноя. 152015-11-06 23:48:21

+2

Тем не менее, я снова использовал этот оператор снова и снова, так что он фактически достигнет прогресса, вместо того, чтобы отключить синхронизацию соединения или компьютер перейдет спать. Я изменил его на «MAX (id)», чтобы устранить последние дубликаты, и добавил 'LIMIT 1000000' во внутренний запрос, поэтому ему не пришлось бы сканировать всю таблицу. Это показало прогресс намного быстрее, чем другие ответы, которые, казалось бы, часами зависали. После того, как таблица была обрезана до управляемого размера, вы можете завершить другие запросы. Совет. Убедитесь, что col1/col2/col3 имеет индексы для группы. 07 ноя. 152015-11-07 19:44:25


41
DELETE LU 
FROM (SELECT *, 
       Row_number() 
       OVER ( 
        partition BY col1, col1, col3 
        ORDER BY rowid DESC) [Row] 
     FROM mytable) LU 
WHERE [row] > 1 
  0

Я получаю это сообщение на azure SQL DW: Предложение FROM в настоящее время не поддерживается в инструкции DELETE. 22 окт. 162016-10-22 06:54:20


12

Использование КТР. Идея заключается в том, чтобы присоединиться на один или несколько столбцов, которые формируют дубликат записи, а затем удалить какой вам нравится:

;with cte as (
    select 
     min(PrimaryKey) as PrimaryKey 
     UniqueColumn1, 
     UniqueColumn2 
    from dbo.DuplicatesTable 
    group by 
     UniqueColumn1, UniqueColumn1 
    having count(*) > 1 
) 
delete d 
from dbo.DuplicatesTable d 
inner join cte on 
    d.PrimaryKey > cte.PrimaryKey and 
    d.UniqueColumn1 = cte.UniqueColumn1 and 
    d.UniqueColumn2 = cte.UniqueColumn2; 
+1

Я думаю, что вам не хватает И в вашем ПРИСОЕДИНЕНИИ. 16 мар. 162016-03-16 20:32:50


13

Этот запрос показал очень хорошую производительность для меня:

DELETE tbl 
FROM 
    MyTable tbl 
WHERE 
    EXISTS (
     SELECT 
      * 
     FROM 
      MyTable tbl2 
     WHERE 
      tbl2.SameValue = tbl.SameValue 
     AND tbl.IdUniqueValue < tbl2.IdUniqueValue 
    ) 

он удален строки 1M в чуть больше 30 секунд из таблицы 2M (50% дубликатов)


5

Я хочу просмотреть строки, которые вы собираетесь удалить, и сохранить контроль над тем, какие из повторяющихся строк сохранить. См http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

with MYCTE as (
    SELECT ROW_NUMBER() OVER (
    PARTITION BY DuplicateKey1 
       ,DuplicateKey2 -- optional 
    ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed 
) RN 
    FROM MyTable 
) 
DELETE FROM MYCTE 
WHERE RN > 1 

19

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

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

by keeping original

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 

without keeping original

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) 

10

Используйте этот

WITH tblTemp as 
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name) 
    As RowNumber,* FROM <table_name> 
) 
DELETE FROM tblTemp where RowNumber >1 

1
alter table MyTable add sno int identity(1,1) 
    delete from MyTable where sno in 
    (
    select sno from (
    select *, 
    RANK() OVER (PARTITION BY RowID,Col3 ORDER BY sno DESC)rank 
    From MyTable 
    )T 
    where rank>1 
    ) 

    alter table MyTable 
    drop column sno 

0

Теперь давайте посмотрим elasticalsearch таблицу, в которой эти таблицы имеет дублированную строк и Id является идентичным уник поле. Мы знаем, существует ли какой-либо id по групповому критерию, тогда мы можем удалить другие строки из этой группы. Моя манера показывает эти критерии.

Так много случаев этой нити находятся в подобном состоянии. Просто измените критерии своей целевой группы по вашему делу для удаления повторяющихся (дублированных) строк.

DELETE 
FROM elasticalsearch 
WHERE Id NOT IN 
       (SELECT min(Id) 
        FROM elasticalsearch 
        GROUP BY FirmId,FilterSearchString 
        ) 

веселит

+3

Не могли бы вы объяснить, как/почему ваш код работает? Это позволит OP и другим пользователям понять и применить ваши методы (где это применимо) в другом месте. Ответы только на код [обескуражены] (http://meta.stackexchange.com/q/148272/284827) и подлежат удалению. - [Во время обзора] (http://stackoverflow.com/review/low-quality-posts/10940576) 18 янв. 162016-01-18 23:51:45

+1

ok я объяснил свой мой ответ Вай Ха Ли, вдохновляющий код, показывает все подробности 19 янв. 162016-01-19 07:15:47


6

Другой способ сделать это: - используется

DELETE A 
FROM TABLE A, 
     TABLE B 
WHERE A.COL1 = B.COL1 
     AND A.COL2 = B.COL2 
     AND A.UNIQUEFIELD > B.UNIQUEFIELD 
  0

Что отличает этот существующий ответ от 20 августа 2008 года? - http://stackoverflow.com/a/18934/692942 08 мар. 162016-03-08 14:43:58


1

Иногда мягкий удаления механизм, в котором записывается дата, чтобы указать дату удален. В этом случае оператор UPDATE может использоваться для обновления этого поля на основе повторяющихся записей.

UPDATE MY_TABLE 
    SET DELETED = getDate() 
WHERE TABLE_ID IN (
    SELECT x.TABLE_ID 
     FROM MY_TABLE x 
     JOIN (SELECT min(TABLE_ID) id, COL_1, COL_2, COL_3 
       FROM MY_TABLE d 
      GROUP BY d.COL_1, d.COL_2, d.COL_3 
      HAVING count(*) > 1) AS d ON d.COL_1 = x.COL_1 
            AND d.COL_2 = x.COL_2 
            AND d.COL_3 = x.COL_3 
            AND d.TABLE_ID <> x.TABLE_ID 
      /*WHERE x.COL_4 <> 'D' -- Additional filter*/) 

Этот метод послужил мне хорошо для довольно умеренных таблиц, содержащих ~ 30 миллионов строк с высоким и низким количеством дублирования.


7

Это самый простой способ для удаления дубликатов записи

DELETE FROM tblemp WHERE id IN 
(
    SELECT MIN(id) FROM tblemp 
    GROUP BY title HAVING COUNT(id)>1 
) 

http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105

  0

не работает на mysql 19 ноя. 162016-11-19 19:16:26

  0

не будет работать, если id дублируется 28 ноя. 172017-11-28 13:46:40

  0

Почему кто-то поддерживает это? Если у вас более двух одинаковых идентификаторов, это НЕ будет работать.Вместо этого напишите: удалите из tblemp, где id не в (выберите min (id) из группы tblemp по названию) 18 янв. 182018-01-18 10:39:15


11

Для Fetch повторяющихся строк:

SELECT 
name, email, COUNT(*) 
FROM 
users 
GROUP BY 
name, email 
HAVING COUNT(*) > 1 

Удаление повторяющихся строк:

DELETE users 
WHERE rowid NOT IN 
SELECT MIN(rowid) 
FROM users 
GROUP BY name, email); 

1

Я знаю, что этот вопрос уже ответил, но я создал очень полезный зр, который будет создавать динамическую ВЕЬЕТЕ для таблицы дублей:

CREATE PROCEDURE sp_DeleteDuplicate @tableName varchar(100), @DebugMode int =1 
AS 
BEGIN 
SET NOCOUNT ON; 

IF(OBJECT_ID('tempdb..#tableMatrix') is not null) DROP TABLE #tableMatrix; 

SELECT ROW_NUMBER() OVER(ORDER BY name) as rn,name into #tableMatrix FROM sys.columns where [object_id] = object_id(@tableName) ORDER BY name 

DECLARE @MaxRow int = (SELECT MAX(rn) from #tableMatrix) 
IF(@MaxRow is null) 
    RAISERROR ('I wasn''t able to find any columns for this table!',16,1) 
ELSE 
    BEGIN 
DECLARE @i int =1 
DECLARE @Columns Varchar(max) =''; 

WHILE (@i <= @MaxRow) 
BEGIN 
    SET @[email protected]+(SELECT '['+name+'],' from #tableMatrix where rn = @i) 

    SET @i = @i+1; 
END 

---DELETE LAST comma 
SET @Columns = LEFT(@Columns,LEN(@Columns)-1) 

DECLARE @Sql nvarchar(max) = ' 
WITH cteRowsToDelte 
    AS (
SELECT ROW_NUMBER() OVER (PARTITION BY '[email protected]+' ORDER BY (SELECT 0)) as rowNumber,* FROM '[email protected] 
+') 

DELETE FROM cteRowsToDelte 
WHERE rowNumber > 1; 
' 
SET NOCOUNT OFF; 
    IF(@DebugMode = 1) 
     SELECT @Sql 
    ELSE 
     EXEC sp_executesql @Sql 
    END 
END 

Так что, если вы создаете таблицу так :

IF(OBJECT_ID('MyLitleTable') is not null) 
    DROP TABLE MyLitleTable 


CREATE TABLE MyLitleTable 
(
    A Varchar(10), 
    B money, 
    C int 
) 
--------------------------------------------------------- 

    INSERT INTO MyLitleTable VALUES 
    ('ABC',100,1), 
    ('ABC',100,1), -- only this row should be deleted 
    ('ABC',101,1), 
    ('ABC',100,2), 
    ('ABCD',100,1) 

    ----------------------------------------------------------- 

    exec sp_DeleteDuplicate 'MyLitleTable',0 

Он удалит все дубликаты из вашей таблицы. Если вы запустите его без второго параметра, он вернет инструкцию SQL для запуска.

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