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

Ali*_*der 154 mysql duplicates

我需要DELETEMySQL表上为指定的sid复制行.

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

DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"
Run Code Online (Sandbox Code Playgroud)

像这样的东西,但我不知道该怎么做.

use*_*291 210

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

ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)
Run Code Online (Sandbox Code Playgroud)

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

  • 注意:这将保留最早的重复记录并删除较新的记录.如果你想保持最新,你不能用'ALTER IGNORE`做到这一点. (24认同)
  • [从MySQL 5.7.4开始,删除了ALTER TABLE的IGNORE子句,并且它的使用会产生错误.](http://dev.mysql.com/doc/refman/5.7/en/alter-table.html) (24认同)
  • 这可能会在MySQL> 5.5上失败,如果是这样,请使用"set session old_alter_table = 1;" 和"set session old_alter_table = 0;" 声明之前和之后 (13认同)
  • 似乎不适用于InnoDB.我跑了`ALTER TABLE foo ENGINE MyISAM`来解决它,之后改变了引擎. (9认同)
  • 这在 MySQL 5.0.67 上对我来说效果很好,我很欣赏漂亮的单线。@GeoffreyBooth,我想知道他们为什么在 v5.7.4 中删除了这种行为。有任何想法吗? (2认同)
  • 正如其他人所指出的,这在 MySQL >= 5.7 的版本中已被弃用。请参阅此处:https://dev.mysql.com/worklog/task/?id=7395 我不知道为什么不推荐使用它。 (2认同)
  • @delatbabel弃用它的原因在您链接的页面中给出. (2认同)

Abh*_*y_D 121

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

employee (first_name, last_name, start_date)
Run Code Online (Sandbox Code Playgroud)

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

delete
from employee using employee,
    employee e1
where employee.id > e1.id
    and employee.first_name = e1.first_name  
Run Code Online (Sandbox Code Playgroud)


Kam*_*zot 56

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

有临时表

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

DROP TABLE table;
RENAME TABLE table_temp TO table;
Run Code Online (Sandbox Code Playgroud)

由于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
)
Run Code Online (Sandbox Code Playgroud)

  • GROUP-ing为您分组的字段值的每个组合仅生成一个结果行.因此将删除重复项. (4认同)
  • 我喜欢第一种方式,这里太优雅了!:乙 (4认同)

Eri*_*ski 49

删除MySQL中的重复行,演练

创建表并插入一些行:

create table penguins(foo int, bar varchar(15), baz datetime);
insert into penguins values(1, 'skipper', now());
insert into penguins values(1, 'skipper', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(4, 'rico', now());
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)
Run Code Online (Sandbox Code Playgroud)

然后删除重复项:

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)
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)
Run Code Online (Sandbox Code Playgroud)

结果:

create table penguins(foo int, bar varchar(15)); 
insert into penguins values(1, 'skipper'); 
insert into penguins values(1, 'skipper'); 
insert into penguins values(3, 'kowalski'); 
insert into penguins values(3, 'kowalski'); 
insert into penguins values(3, 'kowalski'); 
insert into penguins values(4, 'rico'); 
select * from penguins; 
    # +------+----------+ 
    # | foo  | bar      | 
    # +------+----------+ 
    # |    1 | skipper  | 
    # |    1 | skipper  | 
    # |    3 | kowalski | 
    # |    3 | kowalski | 
    # |    3 | kowalski | 
    # |    4 | rico     | 
    # +------+----------+ 
Run Code Online (Sandbox Code Playgroud)

删除语句是做什么的

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

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

具有别名'a'的表企鹅将被连接在称为别名'b'的表企鹅的子集上.作为子集的右手表'b'找到按foo和bar分组的最大时间戳.这与左手表'a'相匹配.左边的(foo,bar,baz)表格中的每一行都有.右手子集'b'具有(maxtimestamp,foo,bar),其仅与最大的那个匹配.

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

在运行此表之前备份表.

防止在此表上再次发生此问题:

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

  • 纯粹为马达加斯加参考加价! (5认同)
  • 注意:如果您的表具有自动递增的“ ID”列,则“ ON”子句只需要与“ ID”列匹配,就不需要其他内容。 (2认同)

use*_*739 13

这似乎总是对我有用:

CREATE TABLE NoDupeTable LIKE DupeTable; 
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;
Run Code Online (Sandbox Code Playgroud)

这保留了每个欺骗和其他非欺骗记录的最低ID.

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

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

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

移动fwd,根据这两个字段创建任何重复记录变得不可能.


sea*_*ers 13

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

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

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);
Run Code Online (Sandbox Code Playgroud)

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


小智 7

这是一个简单的答案:

delete a from target_table a left JOIN (select max(id_field) as id, field_being_repeated  
    from target_table GROUP BY field_being_repeated) b 
    on a.field_being_repeated = b.field_being_repeated
      and a.id_field = b.id_field
    where b.id_field is null;
Run Code Online (Sandbox Code Playgroud)


小智 7

以下适用于所有表

CREATE TABLE `noDup` LIKE `Dup` ;
INSERT `noDup` SELECT DISTINCT * FROM `Dup` ;
DROP TABLE `Dup` ;
ALTER TABLE `noDup` RENAME `Dup` ;
Run Code Online (Sandbox Code Playgroud)


xti*_*ian 6

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

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

1) 添加一个新的布尔列

alter table mytable add tokeep boolean;
Run Code Online (Sandbox Code Playgroud)

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

alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);
Run Code Online (Sandbox Code Playgroud)

3) 将布尔列设置为 true。由于新的约束,这只会在重复的行之一上成功

update ignore mytable set tokeep = true;
Run Code Online (Sandbox Code Playgroud)

4) 删除没有被标记为 tokeep 的行

delete from mytable where tokeep is null;
Run Code Online (Sandbox Code Playgroud)

5)删除添加的列

alter table mytable drop tokeep;
Run Code Online (Sandbox Code Playgroud)

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


ric*_*ell 5

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

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

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


tem*_*ehm 5

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;
Run Code Online (Sandbox Code Playgroud)


Wer*_*ner 5

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

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

添加一个临时引用列来标记唯一性:

ALTER TABLE `yourtable` ADD `unique` VARCHAR(3) NOT NULL AFTER `lastcolname`;
Run Code Online (Sandbox Code Playgroud)

=> 这将在您的表格中添加一列。

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

UPDATE IGNORE `yourtable` SET `unique` = 'Yes' WHERE 1;
Run Code Online (Sandbox Code Playgroud)

=> 你会发现你的重复记录不会被标记为唯一 = '是',换句话说,每组重复记录中只有一个被标记为唯一。

删除所有不唯一的内容:

DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';
Run Code Online (Sandbox Code Playgroud)

=> 这将删除所有重复记录。

删除列...

ALTER TABLE `yourtable` DROP `unique`;
Run Code Online (Sandbox Code Playgroud)