如何删除重复的行?

Sei*_*bar 1254 t-sql sql-server duplicates

从相当大的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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ett 1125

假设没有空,你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
Run Code Online (Sandbox Code Playgroud)

如果您有GUID而不是整数,则可以替换

MIN(RowId)
Run Code Online (Sandbox Code Playgroud)

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))
Run Code Online (Sandbox Code Playgroud)

  • 这会有效吗?`删除MyTable WHERE RowId NOT IN(选择MIN(RowId)FROM MyTable GROUP BY Col1,Col2,Col3);` (319认同)
  • @GeorgSchölly提供了一个优雅的答案.我在桌面上使用它,我的PHP bug创建了重复的行. (16认同)
  • @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.然后我继续执行计划,猜猜是什么?LEFT JOIN的查询成本为18%,NOT IN为82%,对我来说是一个惊喜.我可能做过一些我不应该做的事情,反之亦然,如果这是真的,我真的很想知道. (12认同)
  • 抱歉,为什么`删除MyTable FROM MyTable`语法正确?在文档[here](http://technet.microsoft.com/en-us/library/ms189835(v = sql.110)中,我没有看到将"DELETE"作为选项放在"DELETE"之后. ASPX).对不起,如果这对其他人显而易见; 我只是想学习SQL的新手.更重要的是它为什么起作用:在那里包括表名的区别是什么? (12认同)
  • @Andriy - 在SQL Server中,"LEFT JOIN"效率低于"NOT 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-exists-vs-not-in/我认为"不存在"的表现最好.尽管可以避免,但这三个都将生成一个带有自联接的计划. (10认同)
  • @GeorgSchölly:这个陈述也适用于SQLite.谢谢! (3认同)
  • @levininja - 请参阅FROM table_source(允许在DELETE中使用FROM和JOIN的T-SQL扩展)和FROM table_alias(FROM是可选的); 第一个MyTable是table_alias,第二个是table_source. (3认同)
  • 我知道来得很晚,但是http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/。如果这些列是可空的,则NOT IN的行为会不同,并且性能会非常差。这就是为什么我建议“不存在”的原因。 (2认同)
  • 令人惊讶的是,考虑到这个问题的必要性,这是多么复杂 - 已经在几个需要这种事情的项目上工作过.核心SQL真的迫切需要一种更简单的方法,特别是考虑到这个问题和其他类似评论的评论和评论数量. (2认同)
  • @Georg的解决方案错误:您无法在FROM子句中指定目标表“产品”进行更新 (2认同)
  • 要记住的一件事是,如果您的表处于活动状态(即始终插入新条目),则最好使用在当前时间之前结束的受限时间段运行此查询.如果外部查询而不是子查询读取新行,则自连接可能会导致不匹配.在这种情况下,可以删除非重复行. (2认同)
  • @Georg:对于一个包含很多行的表,其中只有极少数是应该删除的重复项,反转查询以减少IN参数的数量可以使查询更快:`DELETE FROM myTable WHERE id IN(SELECT id FROM myTable EXCEPT(SELECT MIN(id)id FROM myTable GROUP BY col1,col2,col3));` (2认同)

Mar*_*ith 743

另一种可行的方法是

; 

--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;
Run Code Online (Sandbox Code Playgroud)

我在ORDER BY (SELECT 0)上面使用,因为它是任意哪一行在出现平局时保留.

例如,为了保留最新的一个,RowID你可以使用ORDER BY RowID DESC

执行计划

对于此,执行计划通常比接受的答案更简单,更有效,因为它不需要自联接.

执行计划

但情况并非总是如此.GROUP BY可能首选解决方案的地方是优先选择散列聚合而不是流聚合的情况.

ROW_NUMBER,而该解决方案将永远给几乎相同的计划GROUP BY策略更加灵活.

执行计划

可能有利于散列聚合方法的因素是

  • 分区列上没有有用的索引
  • 相对较少的群体,每组中具有相对较多的重复

在第二种情况的极端版本​​中(如果每个组中都有很多重复的组很少),也可以考虑简单地插入行以保存到新表中然后TRUNCATE复制它们并将其复制回以最小化日志记录与删除行的比例非常高.

  • 如果我可以添加:接受的答案不适用于使用`uniqueidentifier`的表.这个更简单,适用于任何桌子.谢谢马丁. (26认同)
  • 与接受的答案不同,这也适用于没有键(`RowId`)进行比较的表. (16认同)
  • 这是一个非常棒的答案!当我意识到那里有重复之前我删除了旧的PK时它起了作用.+100 (15认同)
  • 我建议在DBA.SE上询问并回答这个问题(带有这个答案).然后我们可以将它添加到[我们的规范答案列表](http://meta.dba.stackexchange.com/q/708/2660). (12认同)
  • 另一方面,这个版本不适用于所有SQL Server版本 (8认同)
  • @David - 它适用于2005年以上.现在是2015年. (5认同)

Jon*_*way 146

有一篇关于删除 Microsoft支持站点上的重复项的文章.这是相当保守的 - 他们让你在不同的步骤中做所有事情 - 但它应该适用于大表.

我过去曾经使用过自联接来做这个,虽然它可能会被HAVING子句搞定:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField
Run Code Online (Sandbox Code Playgroud)


小智 94

以下查询对于删除重复行很有用.本例中的表具有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) 
Run Code Online (Sandbox Code Playgroud)

下面的脚本说明用法GROUP BY,HAVING,ORDER BY在一个查询中,并返回带有重复列,其计结果.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
Run Code Online (Sandbox Code Playgroud)

  • @parvus - 问题是标记SQL Server而不是MySQL.SQL Server中的语法很好.此外,MySQL在优化子查询方面非常糟糕[参见此处](http://stackoverflow.com/q/3417074/73226).这个答案在SQL Server中很好.事实上,"NOT IN"通常比"OUTER JOIN ... NULL"表现得更好.我会在查询中添加一个"HAVING MAX(ID)IS NOT NULL",尽管从语义上讲它不应该是必要的,因为这可以改进计划[这里的例子](http://dba.stackexchange.com/questions/31135 /删除重复的 - 记录 - 时 - 使用文本,数据类型/ 31145#31145) (8认同)
  • 在PostgreSQL 8.4中运行良好. (2认同)

Sof*_*eek 59

delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid
Run Code Online (Sandbox Code Playgroud)

Postgres的:

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
Run Code Online (Sandbox Code Playgroud)

  • @Lankymart因为postgres用户也来了。看这个答案的分数。 (2认同)
  • 我已经在一些受欢迎的SQL问题中看到了这一点,例如[here](/sf/ask/266038601/),[here]( /sf/ask/90533131/)和[here](/sf/ask/13639671/ / concatenate-many-rows-in-single-text-string)。OP得到了他的回答,其他所有人也得到了一些帮助。没问题,恕我直言。 (2认同)

Jit*_*aji 42

DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 
Run Code Online (Sandbox Code Playgroud)


Sye*_*med 38

这将删除第一行以外的重复行

DELETE
FROM
    Mytable
WHERE
    RowID NOT IN (
        SELECT
            MIN(RowID)
        FROM
            Mytable
        GROUP BY
            Col1,
            Col2,
            Col3
    )
Run Code Online (Sandbox Code Playgroud)

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

  • 对于mysql,它将给出错误:错误代码:1093.您不能在FROM子句中为更新指定目标表'Mytable'.但这个小改动将适用于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) (10认同)

Sha*_*r K 32

我更喜欢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
Run Code Online (Sandbox Code Playgroud)

没有保持原创

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)
Run Code Online (Sandbox Code Playgroud)


小智 23

获取重复行:

SELECT
name, email, COUNT(*)
FROM 
users
GROUP BY
name, email
HAVING COUNT(*) > 1
Run Code Online (Sandbox Code Playgroud)

删除重复行:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      
Run Code Online (Sandbox Code Playgroud)


小智 22

快速和脏删除精确重复的行(对于小表):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;
Run Code Online (Sandbox Code Playgroud)

  • 请注意,该问题实际上指定了非精确复制(dueto row id). (3认同)

Jam*_*ico 20

我更喜欢子查询\具有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
--)
Run Code Online (Sandbox Code Playgroud)

  • 这不会清理所有重复项.如果你有3行是重复的,它只会选择具有MIN(id)的行,并删除那一行,留下两行是重复的. (7认同)
  • 您只返回id最低的那个,基于select子句中的min(id). (3认同)
  • 取消注释查询的第一行,第二行和最后一行. (2认同)
  • 尽管如此,我最后一次又一次地重复使用这个语句,以便它实际上取得进展,而不是让连接超时或计算机进入睡眠状态.我将其更改为"MAX(id)"以消除后者的重复,并将"LIMIT 1000000"添加到内部查询中,这样就不必扫描整个表.这显示了比其他答案快得多的进展,这似乎会持续数小时.将表修剪为可管理的大小后,您可以完成其他查询.提示:确保col1/col2/col3具有group by的索引. (2认同)

小智 16

SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable
Run Code Online (Sandbox Code Playgroud)

  • 如果您对myTable有外键引用,则截断将不起作用. (5认同)

Rub*_*ren 14

我想我会分享我的解决方案,因为它在特殊情况下有效.我的情况下,具有重复值的表没有外键(因为这些值是从另一个数据库复制的).

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!!
Run Code Online (Sandbox Code Playgroud)

PS:在处理这样的事情时,我总是使用一个事务,这不仅可以确保所有内容都作为一个整体执行,而且还允许我在不冒任何风险的情况下进行测试.但是当然你应该备份,以确保......


Ost*_*ati 14

使用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;
Run Code Online (Sandbox Code Playgroud)


Dra*_*ško 14

这个查询对我来说表现非常好:

DELETE tbl
FROM
    MyTable tbl
WHERE
    EXISTS (
        SELECT
            *
        FROM
            MyTable tbl2
        WHERE
            tbl2.SameValue = tbl.SameValue
        AND tbl.IdUniqueValue < tbl2.IdUniqueValue
    )
Run Code Online (Sandbox Code Playgroud)

它从2M的表中删除了超过30秒的1M行(50%重复)


Nit*_*eek 13

另外一个简单的解决方案可以在这里粘贴的链接找到.这个容易掌握,似乎对大多数类似的问题都有效.它适用于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
Run Code Online (Sandbox Code Playgroud)

那么我们如何删除那些重复数据呢?

首先,使用以下代码在该表中插入标识列:

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)  
Run Code Online (Sandbox Code Playgroud)

使用以下代码解决它:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 
Run Code Online (Sandbox Code Playgroud)


Cra*_*aig 12

这是关于删除重复项的另一篇好文章.

它讨论了为什么它很难:" SQL基于关系代数,并且重复在关系代数中不会发生,因为在集合中不允许重复. "

临时表解决方案,以及两个mysql示例.

将来您是要在数据库级别还是从应用程序的角度来防止它.我建议数据库级别,因为您的数据库应该负责维护参照完整性,开发人员只会导致问题;)


Jac*_*itt 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)
Run Code Online (Sandbox Code Playgroud)

基本上,对于表中的每一行,子选择查找与所考虑的行完全相同的所有行的顶部RowID.因此,您最终会得到一个表示"原始"非重复行的RowID列表.


chr*_*035 11

我有一个表,我需要保留不重复的行.我不确定速度或效率.

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )
Run Code Online (Sandbox Code Playgroud)

  • 这假定最多有1个重复. (7认同)

Har*_*ris 11

用这个

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
Run Code Online (Sandbox Code Playgroud)


İsm*_*vuz 10

另一种方法是创建一个具有相同字段和唯一索引的新表.然后将所有数据从旧表移动到新表.自动SQL SERVER忽略(如果存在重复值,还有一个关于该怎么做的选项:ignore,interrupt或sth)重复值.所以我们有相同的表没有重复的行.如果您不想要唯一索引,则可以在传输数据之后删除它.

特别是对于较大的表,您可以使用DTS(SSIS包来导入/导出数据),以便将所有数据快速传输到新的唯一索引表.对于700万行,它只需要几分钟.


小智 9

  1. 创建具有相同结构的新空白表

  2. 像这样执行查询

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
    Run Code Online (Sandbox Code Playgroud)
  3. 然后执行此查询

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1
    
    Run Code Online (Sandbox Code Playgroud)


小智 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)
Run Code Online (Sandbox Code Playgroud)


小智 9

这是删除重复记录的最简单方法

 DELETE FROM tblemp WHERE id IN 
 (
  SELECT MIN(id) FROM tblemp
   GROUP BY  title HAVING COUNT(id)>1
 )
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么有人赞成这个?如果您有两个以上相同的 ID,这将不起作用。而是写:从 tblemp 中删除,其中 id 不在(按标题从 tblemp 组中选择 min(id)) (2认同)

DrP*_*zza 7

从应用程序级别(不幸的).我同意防止重复的正确方法是在数据库级别通过使用唯一索引,但在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 
Run Code Online (Sandbox Code Playgroud)

另外,varchar(2048)对我来说听起来很腥(生活中有些东西是2048字节,但这种情况非常罕见); 它真的不是varchar(max)吗?


小智 7

我会提到这种方法,它可能会有所帮助,适用于所有SQL服务器:通常只有一个 - 两个重复,并且Ids和重复计数是已知的.在这种情况下:

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0
Run Code Online (Sandbox Code Playgroud)


小智 7

DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
Run Code Online (Sandbox Code Playgroud)


Ana*_*dke 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)
Run Code Online (Sandbox Code Playgroud)


Jay*_*res 6

DELETE 
FROM MyTable
WHERE NOT EXISTS (
              SELECT min(RowID)
              FROM Mytable
              WHERE (SELECT RowID 
                     FROM Mytable
                     GROUP BY Col1, Col2, Col3
                     ))
               );
Run Code Online (Sandbox Code Playgroud)


Lau*_*ubi 6

我想要预览要删除的行,并控制要保留的重复行.见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
Run Code Online (Sandbox Code Playgroud)


yuv*_*uvi 6

另一种方法: -

DELETE A
FROM   TABLE A,
       TABLE B
WHERE  A.COL1 = B.COL1
       AND A.COL2 = B.COL2
       AND A.UNIQUEFIELD > B.UNIQUEFIELD 
Run Code Online (Sandbox Code Playgroud)