如何删除SQL Server中的重复行?


415

如何删除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

我已经尝试了一些查询,但是我认为它们依赖于行ID,因为我没有得到期望的结果。例如:

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

5
这不是对第一个链接的欺骗。该问题中没有行ID,链接的问题中有行ID。非常不一样。
Alien Technology

更改“ SELECT ID FROM table GROUP BY id HAVING”以具有聚合功能,例如MAX / MIN,它应该可以工作。
搞砸了

Answers:


785

我喜欢CTE,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中的效率比outside(AND COl1='John')高。通常,您应该在CTE中应用过滤器。
蒂姆·施密特

1
@ omachu23:您可以在CTE中使用任何SQL(除订购外),因此如果要按Johns:进行过滤...FROM dbo.Table1 WHERE Col1='John'。这是小提琴:sqlfiddle.com
#!6

1
最简单的解决方案可能只是set rowcount 1 delete from t1 where col1=1 and col2=1因为看到这里
Zorgarath

15
此答案只会删除col1中重复的行。将“选择”中的列添加到“分区依据”,例如,使用答案中的选择:RN = ROW_NUMBER()OVER(PARTITION BY col1,col2,col3,col4,col5,col6,col7 ORDER BY col1)
rlee

158

我希望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)

2
窗口功能是一个很好的解决方案。
罗伯·凯西

2
我有点困惑。您从CTE而不是原始表中删除了它。那么它是怎样工作的?
Bigeyes

8
@Bigeyes从CTE删除记录将从实际物理表中删除相应的记录。(因为CTE包含对实际记录的引用)。
Shamseer K

我不知道这是不是这种情况,直到这篇文章...谢谢
Zakk Diaz

1
为什么要同时删除原始副本和副本副本?我不明白为什么您不希望只删除重复项并保留另一个。
Rich

52

无需使用CTEROW_NUMBER()您可以仅通过使用group by with MAX功能删除记录,这是示例

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

4
该查询将删除非重复记录。
Derek Smalls

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)... count(*)> 1)?
布伦特

1
我不认为有必要使用had或union,这将足以满足需要:从搜索中删除ID不在的位置(从搜索组中按网址选择min(id))
Christopher Yang

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]

进入对象资源管理器并删除旧表。

用旧表的名称重命名新表。


这是我在介绍资料中学习并使用的最简单的方法。
eric

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
如果我有1万行,我怎么知道我重复了多少行?
Fearghal 2014年

@Fearghal尝试“通过primaryKey从myTable组中选择primaryKey,count(*);”
oabarca 2014年

1
但是,如果重复行的数量不同,该怎么办?也就是说,a行有2条记录,b行有5条记录,c行没有重复的记录
Thermite 2014年

1
@ user2070775如果所有行中只有一个子集具有重复项,而在那些重复项中,有一些重复了两次,而有些则被重复了三四次呢?
Thermite 2014年

@ user2070775我错过了您说“只需删除几行”的部分。在页面上也有关于set rowcount的警告,在将来的sql版本中,它将不会影响更新或删除语句
Thermite 2014年

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脚本,您将在每页1,000,000行中看到结果,您可以更改每页的行号以更频繁地查看结果。

  9. 请注意,我设置了IDENTITY_INSERTon和off,因为其中一列包含自动增量ID,我也在复制

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;

该查询两次引用联系人表,因此,它使用表别名t1t2

输出为:

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;

   

B)使用中间表删除重复的行

下面显示了使用中间表删除重复行的步骤:

    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 server中,可以通过许多方法完成此操作,最简单的方法是:将重复行表中的不同行插入到新的临时表中。然后从重复行表中删除所有数据,然后从临时表中插入没有重复项的所有数据,如下所示。

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

   select * from table

使用通用表表达式(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

参考https://support.microsoft.com/en-us/help/139444/how-to-remove-dumove-rows-from-a-table-in-sql-server

删除重复项的想法涉及

  • a)保护那些不重复的行
  • b)保留多个一起被视为重复的行之一。

一步步

  • 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

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和临时表等的答案一样。

而我要做的只是通过使用MAX汇总ID列。

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

注意:您可能需要多次运行它才能删除重复项,因为这一次只会删除一组重复的行。


这将不起作用,因为它将删除所有重复项而不保留原始文件。OP要求保留原始记录。
0xdd

2
不正确,max会给您满足条件的max ID。如果事实并非如此,请证明您的不赞成票。
搞砸了

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;

从巨大的(几百万个记录)表中删除重复项可能需要很长时间。我建议您对所选行的临时表进行批量插入,而不要删除。'-使用CTE AS(选择名称,ROW_NUMBER()覆盖(按名称顺序按名称排列)ID从@TB重写您的代码(请注意3RD线)*从CTE ID = = 1的#unique_records中写入; '
伊曼纽尔·布尔

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))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.