从表中删除重复记录 - SQL查询

6

我需要从表格中仅删除重复的行,就像我的表中有3个重复的行,我的查询将从这3个重复的行中删除2个。

我该怎么做?请帮帮我。


@Matt - 我看到你做的那个了 :) - jensgram
这似乎是一个面试问题。使用哪个数据库?是否有ID?是否允许使用临时表? - Adriaan Stander
4
我不打算回答,因为有人(可能是提问者)会立即对所有答案进行投票否定! - Tony Andrews
2
实际上,如果不知道具体的DBMS,这个问题是无法回答的:根据定义,在表中通常没有办法区分真正的重复行。然而,在实践中,大多数DBMS都提供了像Oracle的ROWID这样的结构,可以区分本质上重复的行。 - Tony Andrews
2
OP没有足够的声望来对答案进行投票。 - MartW
显示剩余8条评论
7个回答

12

请尝试下面的查询,它一定能够达到你的目标。

SET ROWCOUNT 1
DELETE test
FROM test a
WHERE (SELECT COUNT(*) FROM test b WHERE b.name = a.name) > 1
WHILE @@rowcount > 0
  DELETE test
  FROM test a
  WHERE (SELECT COUNT(*) FROM test b WHERE b.name = a.name) > 1
SET ROWCOUNT 0

其中test是您的表名


2
@Muhammad - 你是对的。我在Dor的回答中也留了一条评论表达了同样的意思。这是正确的答案,因为它没有假设Santanu问题中的任何“额外”内容。我正在尝试理解这个答案,并且必须承认我只有一点点知道它为什么有效。它使用了ROWCOUNT和WHILE,但我还不太理解。学习新知识总是好的!...而我通常是SQL问题中得到不可能的答案的人 :-( - Mark Brittingham
1
马克,我认为你应该把这个发表为答案,这样我就可以点赞了! - Zoidberg
2
好的 - 我现在明白了 WHILE 循环 - 它与 Delete 操作并没有(神奇地)联系。SET ROWCOUNT 1 确保以下操作仅适用于一行,而不是完全面向集合。然后,Delete 操作删除一个重复 a.name 的行。WHILE 循环利用了结果 @@rowcount 并开始循环。当没有更多重复项时,循环停止。非常棒。但是 - 我仍在研究 Delete TblName From TblName Where X 结构...这是必需的才能使其工作 - 只需要弄清楚原因即可。 - Mark Brittingham
1
@Zoidberg - 我会提供一种比Dor答案中发现的更简单的删除方法。不过我想要看到Muhammad的回答获得更多赞。那是一个很棒的回答,因为它迫使我学习新的东西。 - Mark Brittingham
1
现在Muhammad的答案已经被“接受”-我提供自己的答案,它不需要循环。最终,我会使用我的答案(基于我下面所指出的原因),但是这个答案才是我浏览StackOverflow的原因:这里有很多非常聪明和经验丰富的人。 - Mark Brittingham
显示剩余2条评论

4

虽然这不是一个单一的语句,但在SQL Server中可以正常工作:

Declare @cnt int; 
Select @cnt=COUNT(*) From DupTable Where (Col1=1);  -- Assumes you are trying to delete the duplicates where some condition (e.g. Col1=1) is true.
Delete Top (@cnt-1) From DupTable

它还不需要任何额外的假设(比如存在另一列使每行唯一)。毕竟,Santanu说过这些行是重复的而不仅仅是一列。
然而,在我看来,正确的答案是获取真正的表结构。也就是给这个表添加一个IDENTITY列,以便您可以使用单个SQL命令来完成工作。像这样:
ALTER TABLE dbo.DupTable ADD
    IDCol int NOT NULL IDENTITY (1, 1)
GO

然后删除操作就很简单了:
DELETE FROM DupTable WHERE IDCol NOT IN 
   (SELECT MAX(IDCol) FROM DupTable GROUP BY Col1, Col2, Col3)

3
DELETE FROM Table t1, Table t2 WHERE t1.colDup = t2.colDup AND t1.date < t2.date

将从Table中删除每个重复行(在列colDup上),除了最旧的(即最低的date)。


我不喜欢的是,它假设有一列可以区分这些重复行,但考虑到这个问题中的细节数量... - Zoidberg
你说得很对,但另一方面,“细节的数量……”。 - jensgram

2
DELETE FROM `mytbl`
    INNER JOIN (
        SELECT 1 FROM `mytbl`
        GROUP BY `duplicated_column` HAVING COUNT(*)=2
    ) USING(`id`)

编辑:

抱歉,上面的查询无法工作。

假设表结构:

id int 自增

num int # <-- 这是包含重复值的列

以下查询语句在MySQL中可以运行(已经验证):

DELETE `mytbl` FROM `mytbl` 
    INNER JOIN 
    (
        SELECT `num` FROM `mytbl`
        GROUP BY `num` HAVING COUNT(*)=2
    ) AS `tmp` USING (`num`)

这个查询会删除“num”列中存在2个(不多不少)重复值的行。

编辑(再次):

我建议在“num”列上添加一个键。

编辑(#3):

如果作者想要删除重复的,下面的代码在MySQL中是可行的(我已经试过):

DELETE `delete_duplicated_rows` FROM `delete_duplicated_rows`
    NATURAL JOIN (
        SELECT *
        FROM `delete_duplicated_rows`
        GROUP BY `num1` HAVING COUNT(*)=2
    ) AS `der`

假设表结构如下:

CREATE TABLE `delete_duplicated_rows` (
  `num1` tinyint(4) DEFAULT NOT NULL,
  `num2` tinyint(4) DEFAULT NOT NULL
) ENGINE=MyISAM;

1
我认为这个解决方案实际上并没有遵循问题中规定的限制,是吗?Santanu指出所有都是重复的。拥有一个IDENTITY列意味着这些行不是重复的,并且使得这个问题变得相当容易。Muhammad的答案更好。 - Mark Brittingham
@Mark Brittingham:你说得对!我已经编辑了我的答案,并添加了另一个解决方案。 - Dor

1

如果你有想要删除的行的ID,那么...

DELETE FROM table WHERE id IN (1, 4, 7, [id numbers to delete...])

@Zoidberg 的问题说他有3个重复项,需要删除两个。如果他知道这些ID,那么这就足够了。 - user110714
再次,问题的细节不清楚...所以我可以理解混乱。 - Zoidberg

1
  -- Just to demonstrates Marks example          
    . 
        -- START === 1.0.dbo..DuplicatesTable.TableCreate.sql
    /****** Object:  Table [dbo].[DuplicatesTable] 
        Script Date: 03/29/2010 21:24:02 ******/
      IF EXISTS (SELECT * FROM sys.objects 
     WHERE 
object_id = OBJECT_ID(N'[dbo].[DuplicatesTable]') 
AND type in (N'U'))
        DROP TABLE [dbo].[DuplicatesTable]
    GO

    /****** Object:  Table [dbo].[DuplicatesTable]    
Script Date: 03/29/2010 21:24:02 ******/
    SET ANSI_NULLS ON
    GO

    SET QUOTED_IDENTIFIER ON
    GO

    CREATE TABLE [dbo].[DuplicatesTable](
        [ColA] [varchar](10) NOT NULL, -- the name of the DuplicatesTable
        [ColB] [varchar](10) NULL,  -- the description of the e DuplicatesTable 
     ) 


    /* 
    <doc> 
    Models a DuplicatesTable for 
    </doc>

    */


    GO


    --============================================================ DuplicatesTable START
    declare @ScriptFileName varchar(2000)
    SELECT @ScriptFileName = '$(ScriptFileName)'
    SELECT @ScriptFileName + ' --- DuplicatesTable START =========================================' 
    declare @TableName varchar(200)
    select @TableName = 'DuplicatesTable'

    SELECT 'SELECT name from sys.tables where name =''' + @TableName + ''''
    SELECT name from sys.tables 
    where name = @TableName

    DECLARE @TableCount INT 
    SELECT @TableCount  = COUNT(name ) from sys.tables 
        where name =@TableName

    if @TableCount=1
    SELECT ' DuplicatesTable PASSED. The Table ' + @TableName + ' EXISTS ' 
    ELSE 
    SELECT ' DuplicatesTable FAILED. The Table ' + @TableName + ' DOES NOT EXIST ' 
    SELECT @ScriptFileName + ' --- DuplicatesTable END =========================================' 
    --============================================================ DuplicatesTable END

    GO


    -- END ===  1.0.dbo..DuplicatesTable.TableCreate.sql

    . 
    -- START === 1.1..dbo..DuplicatesTable.TableInsert.sql

    BEGIN TRANSACTION;
    INSERT INTO [dbo].[DuplicatesTable]([ColA], [ColB])
    SELECT   N'ColA', N'ColB' UNION ALL
    SELECT N'ColA', N'ColB' UNION ALL
    SELECT  N'ColA', N'ColB' UNION ALL
    SELECT  N'ColA', N'ColB' UNION ALL
    SELECT  N'ColA', N'ColB' UNION ALL
    SELECT  N'ColA', N'ColB' UNION ALL
    SELECT  N'ColA', N'ColB' UNION ALL
    SELECT  N'ColA1', N'ColB1' UNION ALL
    SELECT  N'ColA1', N'ColB1' UNION ALL
    SELECT  N'ColA1', N'ColB1' UNION ALL
    SELECT  N'ColA1', N'ColB1' UNION ALL
    SELECT  N'ColA1', N'ColB1' UNION ALL
    SELECT  N'ColA1', N'ColB1' UNION ALL
    SELECT  N'ColA1', N'ColB1'
    COMMIT;
    RAISERROR (N'[dbo].[DuplicatesTable]: Insert Batch: 1.....Done!', 10, 1) WITH NOWAIT;
    GO


    -- END ===  1.1..dbo..DuplicatesTable.TableInsert.sql

    . 
    -- START === 2.0.RemoveDuplicates.Script.sql
    ALTER TABLE dbo.DuplicatesTable ADD
            DuplicatesTableId int NOT NULL IDENTITY (1, 1)
    GO

    -- Then the delete is trivial:
    DELETE FROM dbo.DuplicatesTable WHERE DuplicatesTableId NOT IN 
         (SELECT MAX(DuplicatesTableId) FROM dbo.DuplicatesTable GROUP BY ColA , ColB)

         Select * from DuplicatesTable ;  
    -- END ===  2.0.RemoveDuplicates.Script.sql

1

我认为每个表都有唯一的标识符。 因此,如果存在,则可以编写以下查询: 从Table1 t1中删除Table1,其中2>=(select count(id)from Table1 where dupColumn = t1.dupColumn),并且 t1.id不在(select max(id)from Table1 where dupColumn = t1.dupColumn)

哎呀。似乎只能使用第二个过滤器 从Table1 t1中删除Table1,其中 t1.id不在(select max(id)from Table1 where dupColumn = t1.dupColumn)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接