如何删除MySQL表上的重复项?

时间:2010-04-13 14:42:21

标签: mysql duplicates

我需要在 DELETE 表格中为指定的sid MySQL重复行。

如何使用SQL查询执行此操作?

DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"

像这样,但我不知道该怎么做。

25 个答案:

答案 0 :(得分:201)

这样可以在不制作新表的情况下删除重复项

ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)

注意:只有当索引适合内存时才能正常工作

答案 1 :(得分:117)

假设您有一个表employee,其中包含以下列:

employee (first_name, last_name, start_date)

要删除带有重复first_name列的行:

delete
from employee using employee,
    employee e1
where employee.id > e1.id
    and employee.first_name = e1.first_name  

答案 2 :(得分:54)

删除所有SID-s的重复项后,不仅是单个。

使用临时表

CREATE TABLE table_temp AS
SELECT * FROM table GROUP BY title, SID;

DROP TABLE table;
RENAME TABLE table_temp TO table;

由于temp_table是新创建的,因此它没有索引。删除重复项后,您需要重新创建它们。您可以使用SHOW INDEXES IN table

检查表中的索引

没有临时表:

DELETE FROM `table` WHERE id IN (
  SELECT all_duplicates.id FROM (
    SELECT id FROM `table` WHERE (`title`, `SID`) IN (
      SELECT `title`, `SID` FROM `table` GROUP BY `title`, `SID` having count(*) > 1
    )
  ) AS all_duplicates 
  LEFT JOIN (
    SELECT id FROM `table` GROUP BY `title`, `SID` having count(*) > 1
  ) AS grouped_duplicates 
  ON all_duplicates.id = grouped_duplicates.id 
  WHERE grouped_duplicates.id IS NULL
)

答案 3 :(得分:47)

删除MySQL中的重复行,演练

创建表并插入一些行:

dev-db> create table penguins(foo int, bar varchar(15), baz datetime);
Query OK, 0 rows affected (0.07 sec)
dev-db> insert into penguins values(1, 'skipper', now());
dev-db> insert into penguins values(1, 'skipper', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(3, 'kowalski', now());
dev-db> insert into penguins values(4, 'rico', now());
Query OK, 6 rows affected (0.07 sec)
dev-db> select * from penguins;
+------+----------+---------------------+
| foo  | bar      | baz                 |
+------+----------+---------------------+
|    1 | skipper  | 2014-08-25 14:21:54 |
|    1 | skipper  | 2014-08-25 14:21:59 |
|    3 | kowalski | 2014-08-25 14:22:09 |
|    3 | kowalski | 2014-08-25 14:22:13 |
|    3 | kowalski | 2014-08-25 14:22:15 |
|    4 | rico     | 2014-08-25 14:22:22 |
+------+----------+---------------------+
6 rows in set (0.00 sec)

然后删除重复项:

dev-db> delete a
    -> from penguins a
    -> left join(
    -> select max(baz) maxtimestamp, foo, bar
    -> from penguins
    -> group by foo, bar) b
    -> on a.baz = maxtimestamp and
    -> a.foo = b.foo and
    -> a.bar = b.bar
    -> where b.maxtimestamp IS NULL;
Query OK, 3 rows affected (0.01 sec)

<强>结果:

dev-db> select * from penguins;
+------+----------+---------------------+
| foo  | bar      | baz                 |
+------+----------+---------------------+
|    1 | skipper  | 2014-08-25 14:21:59 |
|    3 | kowalski | 2014-08-25 14:22:15 |
|    4 | rico     | 2014-08-25 14:22:22 |
+------+----------+---------------------+
3 rows in set (0.00 sec)

删除语句

的内容是什么

伪代码:按要删除重复项的两列对行进行分组。使用最大聚合选择要保留的每个组的一行。左连接返回左表中的所有行,右表中的匹配行。在这种情况下,左表包含表中的所有行,右表只包含那些为NULL的行(不是每个组要保留的一行)。删除这些行,每组只留下唯一的一行。

更多技术说明,您应该如何阅读sql delete语句:

表企鹅与别名&#39; a&#39;留在表格企鹅的一个子集上,称为别名&#39; b&#39;。右手表&#39; b&#39;这是一个子集,找到按foo和bar分组的最大时间戳。这与左手表格相匹配&#39; a&#39;。左边的(foo,bar,baz)表格中的每一行都有。右手子集&#39; b&#39;有一个(maxtimestamp,foo,bar),它只与最大的那个匹配。

不是那个max的每一行都有值maxtimestamp为NULL。过滤掉那些NULL行,你有一组按foo和bar分组的所有行,它们不是最新的时间戳baz。删除那些。

在运行此表之前备份表。

防止此问题再次出现在此表中:

如果你有这个工作,它会把你的重复行&#34;火。大。你的工作还没有完成。在表上(在这两列上)定义新的复合唯一键,以防止在第一个位置添加更多重复项。就像一个好的免疫系统一样,在插入时甚至不允许将坏行放到桌子上。稍后所有这些添加重复项的程序都会播放他们的抗议,当你修复它们时,这个问题再也不会出现了。

答案 4 :(得分:12)

在我自己遇到这个问题之后,在一个庞大的数据库中,我对任何其他答案的表现都没有给我留下足够的印象。我想只保留最新的重复行,并删除其余的行。

在没有临时表的单查询语句中,这对我来说效果最好,

DELETE e.*
FROM employee e
WHERE id IN
 (SELECT id
   FROM (SELECT MIN(id) as id
          FROM employee e2
          GROUP BY first_name, last_name
          HAVING COUNT(*) > 1) x);

唯一需要注意的是,我必须多次运行查询,但即便如此,我发现它比其他选项更适合我。

答案 5 :(得分:12)

这似乎对我有用:

CREATE TABLE NoDupeTable LIKE DupeTable; 
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;

保持每个欺骗和其他非欺骗记录的最低ID。

我还采取了以下措施,以便在删除后不再出现欺骗问题:

CREATE TABLE NoDupeTable LIKE DupeTable; 
Alter table NoDupeTable Add Unique `Unique` (CommonField1,CommonField2);
INSERT IGNORE NoDupeTable SELECT * FROM DupeTable;

换句话说,我创建第一个表的副本,在我不想重复的字段上添加唯一索引,然后执行Insert IGNORE,其优点是不会像正常情况那样失败Insert第一次尝试根据这两个字段添加重复记录,而忽略任何此类记录。

移动fwd,无法根据这两个字段创建任何重复记录。

答案 6 :(得分:7)

这是一个简单的答案:

this

答案 7 :(得分:6)

以下适用于所有表格

CREATE TABLE `noDup` LIKE `Dup` ;
INSERT `noDup` SELECT DISTINCT * FROM `Dup` ;
DROP TABLE `Dup` ;
ALTER TABLE `noDup` RENAME `Dup` ;

答案 8 :(得分:5)

这项工作让我删除旧记录:

delete from table where id in 
(select min(e.id)
    from (select * from table) e 
    group by column1, column2
    having count(*) > 1
); 

您可以将min(e.id)替换为max(e.id)以删除最新记录。

答案 9 :(得分:4)

delete p from 
product p
inner join (
    select max(id) as id, url from product 
    group by url 
    having count(*) > 1
) unik on unik.url = p.url and unik.id != p.id;

答案 10 :(得分:4)

此过程将删除表中的所有重复项(包括倍数),保留最后一个副本。这是Retrieving last record in each group

的扩展

希望这对某人有用。

DROP TABLE IF EXISTS UniqueIDs;
CREATE Temporary table UniqueIDs (id Int(11));

INSERT INTO UniqueIDs
    (SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON
    (T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields 
    AND T1.ID < T2.ID)
    WHERE T2.ID IS NULL);

DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);

答案 11 :(得分:2)

另一种简单方法......使用UPDATE IGNORE:

U必须在一列或多列上使用索引(类型索引)。 创建一个新的临时引用列(不是索引的一部分)。在此列中,通过使用ignore子句更新它来标记唯一身份用户。一步一步:

添加临时参考列以标记唯一标识符:

ALTER TABLE `yourtable` ADD `unique` VARCHAR(3) NOT NULL AFTER `lastcolname`;

=&GT;这将为您的表添加一列。

更新表格,尝试将所有内容标记为唯一,但忽略由于重复密钥问题而导致的错误(将跳过记录):

UPDATE IGNORE `yourtable` SET `unique` = 'Yes' WHERE 1;

=&GT;您会发现您的重复记录不会被标记为unique ='Yes',换句话说,每组重复记录中只有一个会被标记为唯一。

删除所有不唯一的内容:

DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';

=&GT;这将删除所有重复记录。

放下专栏......

ALTER TABLE `yourtable` DROP `unique`;

答案 12 :(得分:2)

我发现Werner的解决方案above是最方便的,因为它无论是否存在主键都有效,不会乱用表,使用面向未来的普通sql,这是非常容易理解的。

正如我在评论中所述,但该解决方案尚未得到妥善解释。 所以这是我的,基于它。

1)添加一个新的布尔列

alter table mytable add tokeep boolean;

2)在重复列和新列上添加约束

alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);

3)将boolean列设置为true。由于新约束

,这只会在其中一个重复行上成功
update ignore mytable set tokeep = true;

4)删除尚未标记为“保养”的行

delete from mytable where tokeep is null;

5)删除添加的列

alter table mytable drop tokeep;

我建议您保留您添加的约束,以便将来阻止新的重复项。

答案 13 :(得分:2)

如果您要保留ID值最低的行:

 DELETE n1 FROM 'yourTableName' n1, 'yourTableName' n2 WHERE n1.id > n2.id AND n1.email = n2.email

如果要保留具有最高id值的行:

 DELETE n1 FROM 'yourTableName' n1, 'yourTableName' n2 WHERE n1.id < n2.id AND n1.email = n2.email

答案 14 :(得分:1)

删除MySQL表上的重复项是一个常见问题,通常会带来特定需求。如果有人感兴趣,请在这里(Remove duplicate rows in MySQL)解释如何使用临时表以可靠和快速的方式删除MySQL重复项,同样有效处理大数据源(带有不同用例的示例)。 / p>

Ali,在您的情况下,您可以运行以下内容:

-- create a new temporary table
CREATE TABLE tmp_table1 LIKE table1;

-- add a unique constraint    
ALTER TABLE tmp_table1 ADD UNIQUE(sid, title);

-- scan over the table to insert entries
INSERT IGNORE INTO tmp_table1 SELECT * FROM table1 ORDER BY sid;

-- rename tables
RENAME TABLE table1 TO backup_table1, tmp_table1 TO table1;

答案 15 :(得分:0)

爱@ric的答案,但如果你有一个非常大的桌子(我试图运行时我得到The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay)它似乎不起作用。所以我将连接查询限制为只考虑重复的行,最后我得到了:

DELETE a FROM penguins a
    LEFT JOIN (SELECT COUNT(baz) AS num, MIN(baz) AS keepBaz, foo
        FROM penguins
        GROUP BY deviceId HAVING num > 1) b
        ON a.baz != b.keepBaz
        AND a.foo = b.foo
    WHERE b.foo IS NOT NULL

在这种情况下,WHERE子句允许MySQL忽略任何没有重复的行,并且如果这是副本的第一个实例也将忽略,因此只会忽略后续重复项。将MIN(baz)更改为MAX(baz)以保留最后一个实例而不是第一个实例。

答案 16 :(得分:0)

适用于大型表:

 CREATE Temporary table duplicates AS select max(id) as id, url from links group by url having count(*) > 1;

 DELETE l from links l inner join duplicates ld on ld.id = l.id WHERE ld.id IS NOT NULL;

要将最早的更改max(id)删除为min(id)

答案 17 :(得分:0)

delete from `table` where `table`.`SID` in 
    (
    select t.SID from table t join table t1 on t.title = t1.title  where t.SID > t1.SID
)

答案 18 :(得分:0)

这将使列column_name成为主键,同时忽略所有错误。因此,它将删除column_name的重复值的行。

ALTER IGNORE TABLE `table_name` ADD PRIMARY KEY (`column_name`);

答案 19 :(得分:0)

我认为这可以通过基本上复制表并清空它然后只将不同的值放回其中来工作,但请在对大量数据执行之前仔细检查它。

创建表格的副本

  

像oldtablename一样创建表temp_table;   从oldtablename;

插入temp_table select *

清空原始表格

  

从oldtablename删除*;

将复制的表中的所有不同值复制回原始表

  

通过firstname,lastname,dob

从temp_table组中插入oldtablename SELECT *

删除临时表。

  

Drop table temp_table

您需要按照要保持不同的字段进行分组。

答案 20 :(得分:0)

DELETE T2
FROM   table_name T1
JOIN   same_table_name T2 ON (T1.title = T2.title AND T1.ID <> T2.ID)

答案 21 :(得分:0)

这是我通常消除重复的方式

  1. 添加一个临时列,将其命名为任意名称(我将其称为活动列)
  2. 根据您认为不应重复的字段进行分组,并将其活动字段设置为1,分组依据只会为该列选择重复值之一(不会选择重复项)
  3. 删除有效值为零的那些
  4. 下拉列处于活动状态
  5. 可选地(如果适合您的目的),为这些列添加唯一索引,以免再次重复

答案 22 :(得分:-2)

您可以使用DISTINCT子句选择“已清理”列表(here是一个非常简单的示例,如何做到这一点。)

答案 23 :(得分:-3)

如果算上它们可行吗,然后为删除查询添加一个限制,只留下一个?

例如,如果您有两个或更多,请按以下方式编写查询:

DELETE FROM table WHERE SID = 1 LIMIT 1;

答案 24 :(得分:-5)

从表中删除重复数据时只有几个基本步骤:

  • 备份你的桌子!
  • 找到重复的行
  • 删除重复的行

以下是完整教程:https://blog.teamsql.io/deleting-duplicate-data-3541485b3473