【问题标题】:How can I delete duplicate rows in a table如何删除表中的重复行
【发布时间】:2008-09-18 11:35:27
【问题描述】:

我有一张有 3 列的表格。没有主键,所以可以有重复的行。我只需要保留一个并删除其他的。知道如何做到这一点是 Sql Server 吗?

【问题讨论】:

    标签: sql sql-server database


    【解决方案1】:

    我会选择 DISTINCT 行并将它们放入临时表中,然后删除源表并从临时表中复制回数据。 编辑: 现在使用代码 sn-p!

    INSERT INTO TABLE_2 
    SELECT DISTINCT * FROM TABLE_1
    GO
    DELETE FROM TABLE_1
    GO
    INSERT INTO TABLE_1
    SELECT * FROM TABLE_2
    GO
    

    【讨论】:

    • 这是最干净和最通用的解决方案,因为你有磁盘空间(最后的边界)
    • 那么就没有办法使用 SQL 查询吗?
    • 其实就是三个查询: INSERT INTO TABLE_2 SELECT DISTINCT * FROM TABLE_1 GO DELETE FROM TABLE_1 GO INSERT INTO TABLE_1 SELECT * FROM TABLE_2 GO
    • 如果存在依赖于该表的表,这可能会失败。
    • 不太可能:为没有主键的表创建 FK 是不安全的(如果这就是您对“依赖”的意思)...
    【解决方案2】:

    添加一个标识列作为代理主键,并使用它来标识要删除的三行中的两行。

    我会考虑在之后保留标识列,或者如果这是某种链接表,请在其他列上创建复合主键。

    【讨论】:

    • 添加标识列肯定会有所帮助。 SQL Server 将生成一个幽灵列以使每条记录唯一,但您将无法查询此列。标识列将减少一些开销并保证唯一性。
    【解决方案3】:

    当您的 PK 只是所有表列的子集时,以下示例也适用。

    (注意:我更喜欢插入另一个代理 id 列的方法。但也许这个解决方案也很方便。)

    首先找到重复的行:

    SELECT col1, col2, count(*)
    FROM t1
    GROUP BY col1, col2
    HAVING count(*) > 1
    

    如果只有少数,可以手动删除:

    set rowcount 1
    delete from t1
    where col1=1 and col2=1
    

    “rowcount”的值应该是 n-1 倍的重复数。在此示例中,有 2 个重复项,因此行数为 1。如果您有多个重复行,则必须对每个唯一主键执行此操作。

    如果您有很多重复项,则将每个键复制一次到另一个表中:

    SELECT col1, col2, col3=count(*)
    INTO holdkey
    FROM t1
    GROUP BY col1, col2
    HAVING count(*) > 1
    

    然后复制密钥,但消除重复项。

    SELECT DISTINCT t1.*
    INTO holddups
    FROM t1, holdkey
    WHERE t1.col1 = holdkey.col1
    AND t1.col2 = holdkey.col2
    

    在您的密钥中,您现在拥有唯一的密钥。检查你是否没有得到任何结果:

    SELECT col1, col2, count(*)
    FROM holddups
    GROUP BY col1, col2
    

    从原表中删除重复项:

    DELETE t1
    FROM t1, holdkey
    WHERE t1.col1 = holdkey.col1
    AND t1.col2 = holdkey.col2
    

    插入原始行:

    INSERT t1 SELECT * FROM holddups
    

    顺便说一句,为了完整性:在 Oracle 中,您可以使用一个隐藏字段(rowid):

    DELETE FROM our_table
    WHERE rowid not in
    (SELECT MIN(rowid)
    FROM our_table
    GROUP BY column1, column2, column3... ;
    

    见:Microsoft Knowledge Site

    【讨论】:

    • 你应该提到你是从微软的支持网站得到的。 support.microsoft.com/kb/139444
    • @Tony:没错。为我辩护:我在我的本地编程 wiki 中复制了这个,甚至不知道它来自哪里。
    【解决方案4】:

    这是我使用的方法when I asked this question -

    DELETE MyTable 
    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
    

    【讨论】:

      【解决方案5】:

      这是一种使用公用表表达式 CTE 的方法。它不涉及循环,没有新列或任何东西,也不会导致任何不需要的触发器触发(由于删除+插入)。

      灵感来自this article

      CREATE TABLE #temp (i INT)
      
      INSERT INTO #temp VALUES (1)
      INSERT INTO #temp VALUES (1)
      INSERT INTO #temp VALUES (2)
      INSERT INTO #temp VALUES (3)
      INSERT INTO #temp VALUES (3)
      INSERT INTO #temp VALUES (4)
      
      SELECT * FROM #temp
      
      ;
      WITH [#temp+rowid] AS
      (SELECT ROW_NUMBER() OVER (ORDER BY i ASC) AS ROWID, * FROM #temp)
      DELETE FROM [#temp+rowid] WHERE rowid IN 
      (SELECT MIN(rowid) FROM [#temp+rowid] GROUP BY i HAVING COUNT(*) > 1)
      
      SELECT * FROM #temp
      
      DROP TABLE #temp   
      

      【讨论】:

      • 非常好。我总是对 CTE 的能力感到惊讶。
      • @Jonas - 我的朋友,这很酷。它只是解决了我遇到的一个问题。谢谢!
      【解决方案6】:

      这是一个艰难的情况。在不知道您的特定情况(表大小等)的情况下,我认为您最好的方法是添加一个标识列,填充它,然后根据它删除。您可以稍后删除该列,但我建议您应该保留它,因为它确实是一个很好的东西在表格中

      【讨论】:

        【解决方案7】:

        清理完当前的混乱后,您可以添加一个包含表中所有字段的主键。这将使您不再陷入困境。 当然,这个解决方案可以很好地破坏现有代码。这也必须处理。

        【讨论】:

          【解决方案8】:

          你能在表中添加一个主键标识字段吗?

          【讨论】:

            【解决方案9】:

            Manrico Corazzi - 我专攻 Oracle,而不是 MS SQL,所以您必须告诉我这是否可以提高性能:-

            1. 保持与第一步相同 - 将不同的值从 TABLE1 插入到 TABLE2。
            2. 删除表 1。 (我认为删除应该比删除快,就像截断比删除快一样)。
            3. 将 TABLE2 重命名为 TABLE1(节省时间,因为您正在重命名一个对象,而不是将数据从一个表复制到另一个表)。

            【讨论】:

              【解决方案10】:

              这是另一种方式,带有测试数据

              create table #table1 (colWithDupes1 int, colWithDupes2 int)
              insert into #table1
              (colWithDupes1, colWithDupes2)
              Select 1, 2 union all
              Select 1, 2 union all
              Select 2, 2 union all
              Select 3, 4 union all
              Select 3, 4 union all
              Select 3, 4 union all
              Select 4, 2 union all
              Select 4, 2 
              
              
              select * from #table1
              
              set rowcount 1
              select 1
              
              while @@rowcount > 0
              delete #table1  where 1 < (select count(*) from #table1 a2 
                 where #table1.colWithDupes1 = a2.colWithDupes1
              and #table1.colWithDupes2 = a2.colWithDupes2
              )
              
              set rowcount 0
              
              select * from #table1
              

              【讨论】:

                【解决方案11】:

                这个解决方案怎么样:

                首先执行以下查询:

                  select 'set rowcount ' + convert(varchar,COUNT(*)-1) + ' delete from MyTable where field=''' + field +'''' + ' set rowcount 0'  from mytable group by field having COUNT(*)>1
                

                然后你只需要执行返回的结果集

                set rowcount 3 delete from Mytable where field='foo' set rowcount 0
                ....
                ....
                set rowcount 5 delete from Mytable where field='bar' set rowcount 0
                

                当您只有一列时,我已经处理过这种情况,但是很容易将相同的方法应用于多列。如果您希望我发布代码,请告诉我。

                【讨论】:

                  【解决方案12】:

                  怎么样:

                  select distinct * into #t from duplicates_tbl
                  
                  truncate duplicates_tbl
                  
                  insert duplicates_tbl select * from #t
                  
                  drop table #t
                  

                  【讨论】:

                    【解决方案13】:

                    我不确定这是否适用于 DELETE 语句,但这是一种查找重复行的方法:

                     SELECT *
                     FROM myTable t1, myTable t2
                     WHERE t1.field = t2.field AND t1.id > t2.id
                    

                    我不确定你是否可以将“SELECT”更改为“DELETE”(有人想让我知道吗?),但即使你不能,你也可以它进入一个子查询。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-08-11
                      • 2013-02-13
                      • 2017-02-08
                      • 1970-01-01
                      • 2012-10-21
                      相关资源
                      最近更新 更多