【问题标题】:How to delete duplicate rows in SQL Server?如何删除 SQL Server 中的重复行?
【发布时间】:2013-08-25 18:45:35
【问题描述】:

我如何删除不存在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)
)

【问题讨论】:

  • 这不是第一个链接的欺骗。在这个问题中没有行 ID,而在链接的问题中有一个行 ID。非常不同。
  • 更改 'SELECT id FROM table GROUP BY id HAVING' 以具有聚合功能,例如MAX/MIN,它应该可以工作。

标签: sql sql-server-2008 duplicates sql-delete


【解决方案1】:

我喜欢 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

由于PARTITION BY col1,此示例通过单个列col1 确定重复项。如果您想包含多个列,只需将它们添加到PARTITION BY

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)

【讨论】:

  • @omachu23:在这种情况下没关系,尽管我认为它在 CTE 中比在外部更有效(AND COl1='John')。通常你应该在 CTE 中应用过滤器。
  • @omachu23:你可以在 CTE 中使用任何 SQL(除了排序),所以如果你想按 Johns 过滤:...FROM dbo.Table1 WHERE Col1='John'。这是小提琴:sqlfiddle.com/#!6/fae73/744/0
  • 最简单的解决方案可能只是set rowcount 1 delete from t1 where col1=1 and col2=1,如here所见
  • 这个答案只会删除 col1 中重复的行。将“select”中的列添加到“partition by”,例如使用答案中的选择:RN = ROW_NUMBER()OVER(PARTITION BY col1,col2,col3,col4,col5,col6,col7 ORDER BY col1)
  • CTE 是什么意思我把它放进去时遇到 sql 错误。
【解决方案2】:

我更喜欢 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)

【讨论】:

  • 加窗功能是一个很好的解决方案。
  • 我有点困惑。您从 CTE 而不是原始表中删除了它。那么它是如何工作的呢?
  • @Bigeyes 从 CTE 删除记录会从实际物理表中删除相应记录。(因为 CTE 包含对实际记录的引用)。
  • 直到这篇文章我才知道是这种情况......谢谢
  • 为什么要同时删除原始文件和副本?我不明白您为什么不想只删除重复项并保留另一个。
【解决方案3】:

如果不使用CTEROW_NUMBER(),您只需使用group by 和MAX 函数即可删除记录,这里是示例

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

【讨论】:

  • 此查询将删除非重复记录。
  • 这很好用,谢谢。 @DerekSmalls 这不会删除我的非重复记录。
  • 或者您可以使用MIN(ID)保留原始记录
  • 虽然这可能在很多情况下都有效,但问题清楚地表明没有唯一的 id。
  • select max (id) 也将返回非重复记录。所以我认为从删除中排除这些 ID 可以正常工作。
【解决方案4】:

如果你没有引用,比如外键,你可以这样做。在测试概念证明并且测试数据被重复时,我经常这样做。

SELECT DISTINCT [col1],[col2],[col3],[col4],[col5],[col6],[col7]

INTO [newTable]

FROM [oldTable]

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

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

【讨论】:

  • 这是我在介绍材料中学到并使用的最简单的方法。
  • 很好奇当 [oldTable] 有数十亿行时,这个答案如何表现良好......
  • 这将在固态驱动器上刻录 TBW,不推荐。
  • 另请注意,执行重命名时,表的权限将丢失
【解决方案5】:
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
)

【讨论】:

  • 你不能重写:where id in (select max(id) ... having count(*) > 1)?
  • 我不认为有任何需要使用have或union,这就足够了:从搜索中删除id不在的地方(通过url从搜索组中选择min(id))
【解决方案6】:

请看下面的删除方式。

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 没有太大意义。

我知道,这个问题是三年前提出的,我的回答是蒂姆发布的另一个版本,但发布只是以防万一它对任何人都有帮助。

【讨论】:

  • 这个比较靠谱
【解决方案7】:

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. 将原始表中的不同行插入到直接表中。

 

第一步,新建一个与原表结构相同的表:

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/

【讨论】:

  • 我的表中有大约 190000 行。对于这么多行,sol 1 不是一个好的选择。溶胶 2 对我来说效果很好。谢谢
【解决方案8】:

删除所有重复项,但最先删除(带有最小 ID)

应该在其他 SQL 服务器(如 Postgres)中同样工作:

DELETE FROM table
WHERE id NOT IN (
   select min(id) from table
   group by col1, col2, col3, col4, col5, col6, col7
)

【讨论】:

  • 什么是“id”列? OP 的数据集中没有“id”
  • @SergeMerzliakov,它是该行的主键。当没有唯一键时,这个答案不应该工作......但是,在一般情况下,大多数读者都有它,所以id 应该对他们有意义。
【解决方案9】:

Microsoft 有一个关于如何删除重复项的非常简洁的指南。查看http://support.microsoft.com/kb/139444

简而言之,当您只有几行要删除时,这是删除重复项的最简单方法:

SET rowcount 1;
DELETE FROM t1 WHERE myprimarykey=1;

myprimarykey 是行的标识符。

我将 rowcount 设置为 1,因为我只有两行重复。如果我有 3 行重复,那么我会将 rowcount 设置为 2,以便删除它看到的前两行,只在表 t1 中留下一个。

希望对大家有所帮助

【讨论】:

  • 如果我有 10k 行,我如何知道我复制了多少行?
  • @Fearghal try "select primaryKey, count(*) from myTable group by primaryKey;"
  • 但是如果有不同数量的重复行怎么办?即a行有2条记录,b行有5条记录,c行没有重复记录
  • @user2070775 如果所有行中只有一个子集有重复项,并且在这些重复项中有些重复了两次,有些重复了三四次怎么办?
  • @user2070775 我错过了你说“只需删除几行”的部分。页面上还有一个关于设置行计数的警告,在未来的 sql 版本中它不会影响更新或删除语句
【解决方案10】:

在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

【讨论】:

    【解决方案11】:

    尝试使用:

    SELECT linkorder
        ,Row_Number() OVER (
            PARTITION BY linkorder ORDER BY linkorder DESC
            ) AS RowNum
    FROM u_links
    

    【讨论】:

      【解决方案12】:

      在尝试了上述建议的解决方案后,该解决方案适用于中小型桌子。 我可以为非常大的表建议该解决方案。因为它在迭代中运行。

      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_INSERT 设置为 on 和 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

      【讨论】:

        【解决方案13】:
        -- 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
        

        【讨论】:

          【解决方案14】:

          您需要根据字段将重复记录分组,然后保留其中一条记录并删除其余记录。 例如:

          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)
          

          【讨论】:

            【解决方案15】:

            从一个巨大的(几百万条记录)表中删除重复项可能需要很长时间。我建议您对选定行的临时表进行批量插入,而不是删除。

            --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;
            

            【讨论】:

              【解决方案16】:

              这可能对你的情况有所帮助

              DELETE t1 FROM table t1 INNER JOIN table t2 WHERE t1.id > t2.id AND t1.col1 = t2.col1 
              

              【讨论】:

              • 由于原始问题的表格中没有id;我认为这并不能真正解决问题。
              • 如果你仔细阅读问题,你会在其中找到id
              【解决方案17】:
              with myCTE
              as
              
              (
              select productName,ROW_NUMBER() over(PARTITION BY productName order by slno) as Duplicate from productDetails
              )
              Delete from myCTE where Duplicate>1
              

              【讨论】:

                【解决方案18】:

                With reference to https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

                删除重复的想法涉及

                • a) 保护那些不重复的行
                • b) 保留一起限定为重复的许多行之一。

                一步一步

                • 1) 首先识别满足重复定义的行 并将它们插入到临时表中,例如 #tableAll 。
                • 2) 在临时表中选择非重复(单行)或不同的行 说#tableUnique。
                • 3) 从连接 #tableAll 的源表中删除以删除 重复。
                • 4) 将#tableUnique 中的所有行插入源表。
                • 5) 删除#tableAll 和#tableUnique

                【讨论】:

                  【解决方案19】:

                  如果您能够临时向表中添加一列,这是一个对我有用的解决方案:

                  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
                                   );
                  

                  验证 DELETE 是否正确执行:

                  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;
                  

                  【讨论】:

                    【解决方案20】:

                    另一种在不丢失信息的情况下一次性删除重复行的方法如下:

                    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
                    

                    【讨论】:

                      【解决方案21】:

                      哦,哇,准备好所有这些答案让我感到很愚蠢,它们就像专家对所有 CTE 和临时表等的回答。

                      我所做的只是使用 MAX 聚合 ID 列。

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

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

                      【讨论】:

                      • 这不起作用,因为它会删除所有重复项而不保留原件。 OP 要求保留原始记录。
                      • 不是真的,max 会给你满足条件的最大 ID。如果这不是真的,请证明您的反对意见。
                      【解决方案22】:
                      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 重写您的代码(注意第 3 行)作为(选择名称,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB) SELECT * INTO #unique_records FROM CTE WHERE ID =1; '
                      【解决方案23】:
                      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))
                      

                      【讨论】:

                        猜你喜欢
                        • 2023-02-06
                        • 2019-12-04
                        • 2017-11-16
                        • 1970-01-01
                        • 2013-02-13
                        • 2012-06-23
                        • 1970-01-01
                        相关资源
                        最近更新 更多