Как я могу удалить дубликаты строк?

Какой лучший способ удалить дубликаты строк из довольно большой таблицы 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
Комментарии к вопросу (3)
Решение

Предполагая отсутствие нулевых значений, вы GROUP BY уникальные столбцы и SELECT MIN (или 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)))
Комментарии (30)

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

; 

--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 будет выбран вместо агрегата потока.

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

Факторы, которые могут способствовать подходу хеш-совокупности, будут

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

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

Комментарии (16)

На сайте поддержки Microsoft есть хорошая статья о удалении дубликатов. Это довольно консервативно - они заставляют вас делать все за отдельные шаги - но это должно хорошо работать против больших столов.

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

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField
Комментарии (0)

Следующий запрос полезен для удаления дубликатов строк. Таблица в этом примере имеет 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 
Комментарии (4)
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
Комментарии (4)
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 
Комментарии (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)

Комментарии (1)

Я бы предпочел 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 RN1

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

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)
Комментарии (0)

Получить дубликаты строк:

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)

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

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;
Комментарии (1)

Я предпочитаю подзапрос \ имеющий 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
--)
Комментарии (6)
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
Комментарии (1)

Использование 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;
Комментарии (1)

Я думал, что поделюсь своим решением, так как оно работает при особых обстоятельствах. В моем случае таблица с дублирующимися значениями не имела внешнего ключа (потому что значения были продублированы из другого 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: при работе над такими вещами я всегда использую транзакцию, это не только гарантирует, что все выполнено в целом, но также позволяет мне тестировать, не рискуя ничем. Но, конечно, вы должны сделать резервную копию в любом случае, просто чтобы быть уверенным...

Комментарии (0)

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

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% дубликатов)

Комментарии (0)

Еще одно простое решение можно найти по вставленной ссылке здесь. Этот легко понять и кажется эффективным для большинства подобных проблем. Это для 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) 
Комментарии (3)

Вот еще одна хорошая статья о удалении дубликатов.

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

Решение таблицы темпа и два примера mysql.

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

Комментарии (1)

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

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, которые представляют «оригинальные» недублированные строки.

Комментарии (0)

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

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )
Комментарии (2)

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

WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
   As RowNumber,* FROM 
)
DELETE FROM tblTemp where RowNumber >1
Комментарии (0)