Как я могу удалить повторяющиеся строки?
каков наилучший способ удалить дубликаты строк из довольно большого 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
30 ответов:
предполагая отсутствие нулей, вы
GROUP BYуникальные колонки, иSELECTtheMIN (or MAX)RowId как строка для хранения. Затем удалите все, что не имеет идентификатора строки: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)))
еще один возможный способ сделать это -
; --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Выполнение Планов
план выполнения для этого часто проще и эффективнее, чем в принятом ответе, поскольку он не требует самоконтроля присоединяться.
но это не всегда так. Одно место, где
GROUP BYрешение может быть предпочтительным является ситуации, когда hash aggregate было бы выбрано в предпочтении к агрегату потока.The
ROW_NUMBERрешение всегда будет давать в значительной степени тот же план, тогда какGROUP BYстратегия является более гибкой.факторы, которые могут способствовать хэш общий подход будет
- нет полезного индекса в Столбцах секционирования
- относительно меньше групп с относительно большим количеством дубликатов в каждой группе
в крайних версиях этого второго случая (если есть очень мало групп с большим количеством дубликатов в каждой) можно также рассмотреть возможность простой вставки строк, чтобы сохранить в новой таблице, то
TRUNCATE-ing оригинал и копирование их обратно, чтобы свести к минимуму ведение журнала по сравнению с удалением очень высокой пропорция рядов.
есть хорошая статья о удаление дубликатов на сайте поддержки Microsoft. Это довольно консервативно - они у вас все делают в отдельных шагах - но это должно хорошо работать против больших таблиц.
я использовал self-joins для этого в прошлом, хотя его, вероятно, можно было бы украсить предложением HAVING:
DELETE dupes FROM MyTable dupes, MyTable fullTable WHERE dupes.dupField = fullTable.dupField AND dupes.secondDupField = fullTable.secondDupField AND dupes.uniqueField > fullTable.uniqueField
следующий запрос используется для удаления повторяющихся строк. Таблица в этом примере
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
delete t1 from table t1, table t2 where t1.columnA = t2.columnA and t1.rowid>t2.rowidPostgres:
delete from table t1 using table t2 where t1.columnA = t2.columnA and t1.rowid > t2.rowid
DELETE LU FROM (SELECT *, Row_number() OVER ( partition BY col1, col1, col3 ORDER BY rowid DESC) [Row] FROM mytable) LU WHERE [row] > 1
это приведет к удалению повторяющихся строк, за исключением первой строки
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)
Я бы предпочел CTE для удаления повторяющихся строк из таблицы sql server
настоятельно рекомендуем следовать этой статье ::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)
быстрый и грязный, чтобы удалить точные повторяющиеся строки (для небольших таблиц):
select distinct * into t2 from t1; delete from t1; insert into t1 select * from t2; drop table t2;
Я предпочитаю решение подзапроса\having 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 --)
SELECT DISTINCT * INTO tempdb.dbo.tmpTable FROM myTable TRUNCATE TABLE myTable INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable DROP TABLE tempdb.dbo.tmpTable
Чтобы Получить Повторяющиеся Строки:
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);
еще одно простое решение можно найти по ссылке вставлено здесь. Это легко понять и, кажется, эффективно для большинства подобных проблем. Это для 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)
Я думал, что поделюсь своим решением, так как оно работает в особых обстоятельствах. В моем случае таблица с повторяющимися значениями не имеют внешнего ключа (потому что значения дублируются из другой БД).
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: при работе над такими вещами я всегда использую транзакцию, это не только гарантирует, что все выполняется в целом, но и позволяет мне тестировать, не рискуя ничем. Но, конечно, вы должны взять резервную копию в любом случае, чтобы быть уверенным...
этот запрос показал очень хорошую производительность:
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% дубликатов)
С помощью CTE. Идея состоит в том, чтобы присоединиться к одному или нескольким столбцам, которые образуют повторяющуюся запись, а затем удалить то, что вам нравится:
;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;
вот еще одна хорошая статья на удаление дубликатов.
он обсуждает, почему его трудно:"SQL основан на реляционной алгебре, и дубликаты не могут встречаться в реляционной алгебре, потому что дубликаты не допускаются в наборе."
решение временной таблицы и два примера mysql.
в будущем вы собираетесь предотвратить это на уровне базы данных или с точки зрения приложения. Я бы предложил уровень базы данных, потому что ваша база данных должна отвечать за поддержание ссылочной целостности, разработчики просто вызовет проблем ;)
Да, конечно. Используйте временную таблицу. Если вы хотите одно, не очень эффективное утверждение, которое "работает", вы можете пойти с:
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 всех строк, которые точно такие же, как рассматриваемая строка. Таким образом, вы получаете список идентификаторов строк, которые представляют собой "исходные" неповторяющиеся строки.
У меня была таблица, где мне нужно было сохранить не повторяющиеся строки. Я не уверен в скорости или эффективности.
DELETE FROM myTable WHERE RowID IN ( SELECT MIN(RowID) AS IDNo FROM myTable GROUP BY Col1, Col2, Col3 HAVING COUNT(*) = 2 )
другой путь создать новый таблица с такими же полями и с уникальным индексом. Тогда перенести все данные из старой таблицы в новую таблицу. Автоматически SQL SERVER игнорирует (есть также опция о том, что делать, если будет повторяющееся значение: игнорировать, прерывать или sth) повторяющиеся значения. Так у нас же таблица без повторяющихся строк. Если вы не хотите уникальный индекс, после передачи данных вы можете удалить это.
особенно для больших таблиц вы можете использовать DTS (пакет SSIS для импорта / экспорта данных) для быстрой передачи всех данных в новую уникально индексированную таблицу. Для 7 миллионов строк это займет всего несколько минут.
использовать
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
С помощью ниже запроса мы можем в состоянии удалить повторяющиеся записи на основе одного столбца или нескольких столбцов. ниже запрос удаляется на основе двух столбцов. имя таблицы:
testingи имена столбцовempno,empnameDELETE 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)
создать новую пустую таблицу с такой же структурой
выполнить запрос вот так
INSERT INTO tc_category1 SELECT * FROM tc_category GROUP BY category_id, application_id HAVING count(*) > 1затем выполните этот запрос
INSERT INTO tc_category1 SELECT * FROM tc_category GROUP BY category_id, application_id HAVING count(*) = 1
Это самый простой способ, чтобы удалить повторяющиеся записи
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
Я бы упомянул этот подход, а также он может быть полезен и работает на всех серверах SQL: Довольно часто существует только один - два дубликата, а идентификаторы и количество дубликатов известны. В этом случае:
SET ROWCOUNT 1 -- or set to number of rows to be deleted delete from myTable where RowId = DuplicatedID SET ROWCOUNT 0
С уровня приложения (к сожалению). Я согласен с тем, что правильный способ предотвращения дублирования на уровне базы данных заключается в использовании уникального индекса, но в SQL Server 2005 индекс может быть только 900 байт, и мое поле varchar(2048) сдувает это.
Я не знаю, насколько хорошо он будет работать, но я думаю, что вы можете написать триггер для обеспечения этого, даже если вы не можете сделать это напрямую с индексом. Что-то например:
-- 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 байт, но это довольно необычно); это действительно не должно быть varchar(max)?
DELETE FROM table_name T1 WHERE rowid > ( SELECT min(rowid) FROM table_name T2 WHERE T1.column_name = T2.column_name );
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)
DELETE FROM MyTable WHERE NOT EXISTS ( SELECT min(RowID) FROM Mytable WHERE (SELECT RowID FROM Mytable GROUP BY Col1, Col2, Col3 )) );
другой способ сделать это :--
DELETE A FROM TABLE A, TABLE B WHERE A.COL1 = B.COL1 AND A.COL2 = B.COL2 AND A.UNIQUEFIELD > B.UNIQUEFIELD


Comments