Remove duplicate rows in MySQL
我有一个包含以下字段的表:
现在,我需要删除具有相同
1 2 3 4 |
运行此查询后,我可以使用服务器端脚本删除重复项。
但是,我想知道是否只能使用SQL查询来完成。
一个非常简单的方法是在3列上添加
这将删除所有重复的行。作为一个额外的好处,未来的
如果您不想更改列属性,则可以使用下面的查询。
由于您的列具有唯一ID(例如,
1 2 3 4 5 6 7 8 9 10 11 12 13 | DELETE `a` FROM `jobs` AS `a`, `jobs` AS `b` WHERE -- IMPORTANT: Ensures one version remains -- Change"ID" to your unique column's name `a`.`ID` < `b`.`ID` -- Any duplicates you want to check for AND (`a`.`title` = `b`.`title` OR `a`.`title` IS NULL AND `b`.`title` IS NULL) AND (`a`.`company` = `b`.`company` OR `a`.`company` IS NULL AND `b`.`company` IS NULL) AND (`a`.`site_id` = `b`.`site_id` OR `a`.`site_id` IS NULL AND `b`.`site_id` IS NULL); |
在MySQL中,您可以使用NULL安全相等运算符(也称为"太空飞船运营商")进一步简化它:
1 2 3 4 5 6 7 8 9 10 11 12 13 | DELETE `a` FROM `jobs` AS `a`, `jobs` AS `b` WHERE -- IMPORTANT: Ensures one version remains -- Change"ID" to your unique column's name `a`.`ID` < `b`.`ID` -- Any duplicates you want to check for AND `a`.`title` <=> `b`.`title` AND `a`.`company` <=> `b`.`company` AND `a`.`site_id` <=> `b`.`site_id`; |
MySQL对引用要删除的表有限制。您可以使用临时表解决此问题,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | create temporary table tmpTable (id int); insert tmpTable (id) select id from YourTable yt where exists ( select * from YourTabe yt2 where yt2.title = yt.title and yt2.company = yt.company and yt2.site_id = yt.site_id and yt2.id > yt.id ); delete from YourTable where ID in (select id from tmpTable); |
从Kostanos在评论中的建议:
对于拥有非常大的数据库的情况,上面唯一的慢查询是DELETE。此查询可能更快:
如果
1 2 3 4 5 6 7 8 | CREATE TABLE your_table_deduped like your_table; INSERT your_table_deduped SELECT * FROM your_table GROUP BY index1_id, index2_id; RENAME TABLE your_table TO your_table_with_dupes; RENAME TABLE your_table_deduped TO your_table; #OPTIONAL ALTER TABLE `your_table` ADD UNIQUE `unique_index` (`index1_id`, `index2_id`); #OPTIONAL DROP TABLE your_table_with_dupes; |
删除MySQL表上的重复项是一个常见的问题,这通常是缺少约束的结果,以避免事先重复这些重复。但这个常见问题通常伴随着特定的需求......确实需要特定的方法。该方法应该根据,例如,数据的大小,应该保留的重复条目(通常是第一个或最后一个),是否存在索引,或者我们是否要执行任何其他对重复数据采取行动。
MySQL本身也有一些特殊性,例如在执行表UPDATE时无法在FROM原因上引用同一个表(它会引发MySQL错误#1093)。通过使用带有临时表的内部查询(如上面某些方法所建议的),可以克服此限制。但是这种内部查询在处理大数据源时不会特别好。
然而,确实存在一种更好的方法来消除重复,这既是高效又可靠的,并且可以很容易地适应不同的需求。
一般的想法是创建一个新的临时表,通常添加一个唯一的约束来避免进一步的重复,并将前一个表中的数据插入到新表中,同时处理重复项。这种方法依赖于简单的MySQL INSERT查询,创建一个新的约束以避免进一步的重复,并且跳过使用内部查询来搜索重复项的需要以及应该保存在内存中的临时表(因此也适合大数据源)。
这是如何实现的。鉴于我们有一名表员工,请使用以下列:
1 | employee (id, first_name, last_name, start_date, ssn) |
为了删除具有重复ssn列的行,并且仅保留找到的第一个条目,可以遵循以下过程:
1 2 3 4 5 6 7 8 9 10 11 | -- create a new tmp_eployee table CREATE TABLE tmp_employee LIKE employee; -- add a unique constraint ALTER TABLE tmp_employee ADD UNIQUE(ssn); -- scan over the employee table to insert employee entries INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id; -- rename tables RENAME TABLE employee TO backup_employee, tmp_employee TO employee; |
技术说明
?使用这种方法,1.6M寄存器在不到200s的时间内转换为6k。
Chetan,按照这个过程,您可以通过运行以下命令快速轻松地删除所有重复项并创建UNIQUE约束:
1 2 3 4 5 6 7 |
当然,在删除重复项时,可以进一步修改此过程以使其适应不同的需要。一些例子如下。
?保留最后一个条目而不是第一个条目的变化
有时我们需要保留最后一个重复的条目,而不是第一个。
1 2 3 4 5 6 7 |
?在重复项上执行某些任务的变体,例如对发现的重复项进行计数
有时我们需要对找到的重复条目执行一些进一步处理(例如保留重复项的计数)。
1 2 3 4 5 6 7 8 9 | CREATE TABLE tmp_employee LIKE employee; ALTER TABLE tmp_employee ADD UNIQUE(ssn); ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0; INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1; RENAME TABLE employee TO backup_employee, tmp_employee TO employee; |
INSERT INTO ... ON DUPLICATE KEY UPDATE查询可用于对找到的重复项执行不同类型的更新。
?用于重新生成自动增量字段ID的变体
有时我们使用自动增量字段,为了使索引保持尽可能紧凑,我们可以利用删除重复项来重新生成新临时表中的自动增量字段。
1 2 3 4 5 6 7 |
?更多变化
根据所需的行为,许多进一步的修改也是可行的。例如,以下查询将使用第二个临时表,除了1)保留最后一个条目而不是第一个条目; 2)增加一个重复的计数器; 3)重新生成自动增量字段id,同时保持在前一数据上的输入顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | CREATE TABLE tmp_employee LIKE employee; ALTER TABLE tmp_employee ADD UNIQUE(ssn); ALTER TABLE tmp_employee ADD COLUMN n_duplicates INT DEFAULT 0; INSERT INTO tmp_employee SELECT * FROM employee ORDER BY id DESC ON DUPLICATE KEY UPDATE n_duplicates=n_duplicates+1; CREATE TABLE tmp_employee2 LIKE tmp_employee; INSERT INTO tmp_employee2 SELECT (first_name, last_name, start_date, ssn) FROM tmp_employee ORDER BY id; DROP TABLE tmp_employee; RENAME TABLE employee TO backup_employee, tmp_employee2 TO employee; |
好。
还有另一种解决方案:
1 |
我有这个查询snipet for SQLServer但我认为它可以在其他DBMS中使用,几乎没有变化:
1 2 3 4 5 6 7 |
我忘了告诉你这个查询不会删除重复行ID最低的行。如果这适用于您尝试此查询:
1 2 3 4 5 6 7 |
更快的方法是将不同的行插入临时表。使用删除,我花了几个小时从一个800万行的表中删除重复项。使用insert和distinct,只花了13分钟。
1 2 3 4 5 6 | CREATE TABLE tempTableName LIKE tableName; CREATE INDEX ix_all_id ON tableName(cellId,attributeId,entityRowId,value); INSERT INTO tempTableName(cellId,attributeId,entityRowId,value) SELECT DISTINCT cellId,attributeId,entityRowId,value FROM tableName; TRUNCATE TABLE tableName; INSERT INTO tableName SELECT * FROM tempTableName; DROP TABLE tempTableName; |
所有情况都简单快捷:
1 2 3 |
如果你有一个包含大量记录的大表,那么上面的解决方案将无法工作或花费太多时间。然后我们有不同的解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 | -- Create temporary table CREATE TABLE temp_table LIKE table1; -- Add constraint ALTER TABLE temp_table ADD UNIQUE(title, company,site_id); -- Copy data INSERT IGNORE INTO temp_table SELECT * FROM table1; -- Rename and drop RENAME TABLE table1 TO old_table1, temp_table TO table1; DROP TABLE old_table1; |
我找到了一个简单的方法。 (保持最新)
1 2 |
一个易于理解且无需主键的解决方案:
1)添加一个新的布尔列
2)在重复列和新列上添加约束
1 |
3)将布尔列设置为true。由于新约束,这将仅在其中一个重复行上成功
4)删除尚未标记为保养的行
5)删除添加的列
我建议您保留您添加的约束,以便将来阻止新的重复项。
我随时随地访问此页面谷歌"删除重复的形式mysql",但我的theIGNORE解决方案不起作用,因为我有一个InnoDB mysql表
此代码随时可以更好地工作
1 2 3 4 5 |
tableToclean =您需要清理的表的名称
tableToclean_temp =创建和删除的临时表
此解决方案将重复项移动到一个表中,并将uniques移到另一个表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | -- speed up creating uniques table if dealing with many rows CREATE INDEX temp_idx ON jobs(site_id, company, title, location); -- create the table with unique rows INSERT jobs_uniques SELECT * FROM ( SELECT * FROM jobs GROUP BY site_id, company, title, location HAVING count(1) > 1 UNION SELECT * FROM jobs GROUP BY site_id, company, title, location HAVING count(1) = 1 ) x -- create the table with duplicate rows INSERT jobs_dupes SELECT * FROM jobs WHERE id NOT IN (SELECT id FROM jobs_uniques) -- confirm the difference between uniques and dupes tables SELECT COUNT(1) AS jobs, (SELECT COUNT(1) FROM jobs_dupes) + (SELECT COUNT(1) FROM jobs_uniques) AS sum FROM jobs |
从版本8.0(2018)开始,MySQL最终支持窗口函数。
窗口功能既方便又高效。这是一个解决方案,演示如何使用它们来解决此分配。
在子查询中,我们可以使用
一旦在子查询中对记录进行了适当编号,外部查询就会删除行号不为1的所有记录。
查询:
1 2 3 4 5 6 7 8 9 10 11 |
使用DELETE JOIN语句删除重复的行
MySQL为您提供了DELETE JOIN语句,您可以使用该语句快速删除重复的行。
以下语句删除重复行并保留最高ID:
删除表中的重复记录。
1 2 3 4 5 6 |
要么
1 2 3 4 5 6 |
1 2 3 4 5 6 |
为了复制具有唯一列的记录,例如不应复制COL1,COL2,COL3(假设我们在表结构中错过了3列唯一列,并且已将多个重复条目放入表中)
1 2 3 4 5 6 7 |
希望能帮助开发。
TL; TR;
可以在mysqltutorial.org网站上找到解决此问题的大大描述的教程:
如何删除MySQL中的重复行
非常清楚地显示了如何以三种不同的方式删除重复的行:
A)使用
B)使用中间表
C)使用
我希望它会帮助某人。
我想更具体一点,我删除了哪些记录,所以这里是我的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
我必须使用文本字段执行此操作,并且在索引上遇到了100个字节的限制。
我通过添加一个列,执行字段的md5哈希以及执行alter来解决了这个问题。
1 2 3 |
您可以轻松地从此代码中删除重复记录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | $qry = mysql_query("SELECT * from cities"); while($qry_row = mysql_fetch_array($qry)) { $qry2 = mysql_query("SELECT * from cities2 where city = '".$qry_row['city']."'"); if(mysql_num_rows($qry2) > 1){ while($row = mysql_fetch_array($qry2)){ $city_arry[] = $row; } $total = sizeof($city_arry) - 1; for($i=1; $i<=$total; $i++){ mysql_query("delete from cities2 where town_id = '".$city_arry[$i][0]."'"); } } //exit; } |