删除 MySQL 中的重复行
我有一个包含以下字段的表:
id (Unique)
url (Unique)
title
company
site_id
现在,我需要删除具有相同 title、company 和 site_id
的行。一种方法是使用以下 SQL 和脚本 (PHP):
SELECT title, site_id, location, id, count( * )
FROM jobs
GROUP BY site_id, company, title, location
HAVING count( * ) >1
运行此查询后,我可以使用服务器端脚本删除重复项。
但是,我想知道这是否可以仅使用 SQL 查询来完成。
I have a table with the following fields:
id (Unique)
url (Unique)
title
company
site_id
Now, I need to remove rows having same title, company and site_id
. One way to do it will be using the following SQL along with a script (PHP):
SELECT title, site_id, location, id, count( * )
FROM jobs
GROUP BY site_id, company, title, location
HAVING count( * ) >1
After running this query, I can remove duplicates using a server side script.
But, I want to know if this can be done only using SQL query.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(28)
一个非常简单的方法是在 3 列上添加一个
UNIQUE
索引。当您编写ALTER
语句时,请包含IGNORE
关键字。像这样:这将删除所有重复的行。作为一个额外的好处,未来重复的
INSERT
将会出错。与往常一样,您可能需要在运行类似这样的操作之前进行备份...编辑:在 MySQL 5.7+ 中不再工作
此功能已在 MySQL 5.6 和 在 MySQL 5.7 中被删除,所以它不起作用。
A really easy way to do this is to add a
UNIQUE
index on the 3 columns. When you write theALTER
statement, include theIGNORE
keyword. Like so:This will drop all the duplicate rows. As an added benefit, future
INSERTs
that are duplicates will error out. As always, you may want to take a backup before running something like this...Edit: no longer works in MySQL 5.7+
This feature has been deprecated in MySQL 5.6 and removed in MySQL 5.7, so it doesn't work.
如果您不想更改列属性,则可以使用下面的查询。
由于您有一个具有唯一 ID 的列(例如,
auto_increment
列),因此您可以使用它来删除重复项:在 MySQL 中,您可以使用 NULL 安全等于运算符 (又名 “宇宙飞船运算符”):
If you don't want to alter the column properties, then you can use the query below.
Since you have a column which has unique IDs (e.g.,
auto_increment
columns), you can use it to remove the duplicates:In MySQL, you can simplify it even more with the NULL-safe equal operator (aka "spaceship operator"):
MySQL 对于引用要从中删除的表有限制。您可以使用临时表来解决这个问题,例如:
来自 Kostanos 在评论中的建议:
对于数据库非常大的情况,上面唯一慢的查询是 DELETE。这个查询可能会更快:
MySQL has restrictions about referring to the table you are deleting from. You can work around that with a temporary table, like:
From Kostanos' suggestion in the comments:
The only slow query above is DELETE, for cases where you have a very large database. This query could be faster:
删除 MySQL 表上的重复项是一个常见问题,这通常是由于事先缺少避免这些重复项的约束而导致的。但这个常见问题通常伴随着特定的需求……确实需要特定的方法。该方法应该有所不同,例如,数据的大小、应保留的重复条目(通常是第一个或最后一个)、是否有要保留的索引,或者我们是否要执行任何附加操作对重复数据执行的操作。
MySQL 本身也有一些特殊性,例如在执行表 UPDATE 时无法在 FROM 上引用同一个表(这会引发 MySQL 错误 #1093)。可以通过使用带有临时表的内部查询来克服此限制(如上面某些方法所建议的)。但在处理大数据源时,这种内部查询的性能不会特别好。
然而,确实存在一种更好的方法来删除重复项,该方法既高效又可靠,并且可以轻松适应不同的需求。
总体思路是创建一个新的临时表,通常添加唯一约束以避免进一步重复,并将前一个表中的数据插入到新表中,同时处理重复项。这种方法依赖于简单的 MySQL INSERT 查询,创建一个新的约束以避免进一步的重复,并跳过使用内部查询来搜索重复项和应保存在内存中的临时表的需要(因此也适合大数据源)。
这就是它可以实现的方法。假设我们有一个表 employee,包含以下列:
为了删除具有重复 ssn 列的行,并仅保留找到的第一个条目,以下过程可以遵循:
技术说明
⇒ < em>使用这种方法,1.6M 寄存器在不到 200 秒的时间内转换为 6k。
Chetan,如下过程中,您可以快速轻松地删除所有重复项,并通过运行以下命令创建 UNIQUE 约束:
当然,可以进一步修改此过程以适应删除重复项时的不同需求。下面是一些例子。
✔ 保留最后一个条目而不是第一个条目的变体
有时我们需要保留最后一个重复条目而不是第一个条目。
✔ 对重复项执行某些任务的变体,例如对找到的重复项进行计数
有时我们需要对找到的重复条目执行一些进一步的处理(例如保留重复项的计数)。
INSERT INTO ... ON DUPLICATE KEY UPDATE 查询可用于对找到的重复项执行不同类型的更新。
✔ 重新生成自增字段 id 的变体
有时我们会使用自增字段,为了使索引尽可能紧凑,我们可以利用删除重复项来重新生成自增字段新的临时表。
✔ 其他变体
根据所需的行为,还可以进行许多进一步的修改。例如,以下查询将使用第二个临时表,此外 1) 保留最后一个条目而不是第一个条目; 2) 增加发现的重复项的计数器;另外3)重新生成自增字段id,同时保持之前数据上的条目顺序。
Deleting duplicates on MySQL tables is a common issue, that's genarally the result of a missing constraint to avoid those duplicates before hand. But this common issue usually comes with specific needs... that do require specific approaches. The approach should be different depending on, for example, the size of the data, the duplicated entry that should be kept (generally the first or the last one), whether there are indexes to be kept, or whether we want to perform any additional action on the duplicated data.
There are also some specificities on MySQL itself, such as not being able to reference the same table on a FROM cause when performing a table UPDATE (it'll raise MySQL error #1093). This limitation can be overcome by using an inner query with a temporary table (as suggested on some approaches above). But this inner query won't perform specially well when dealing with big data sources.
However, a better approach does exist to remove duplicates, that's both efficient and reliable, and that can be easily adapted to different needs.
The general idea is to create a new temporary table, usually adding a unique constraint to avoid further duplicates, and to INSERT the data from your former table into the new one, while taking care of the duplicates. This approach relies on simple MySQL INSERT queries, creates a new constraint to avoid further duplicates, and skips the need of using an inner query to search for duplicates and a temporary table that should be kept in memory (thus fitting big data sources too).
This is how it can be achieved. Given we have a table employee, with the following columns:
In order to delete the rows with a duplicate ssn column, and keeping only the first entry found, the following process can be followed:
Technical explanation
⇒ Using this approach, 1.6M registers were converted into 6k in less than 200s.
Chetan, following this process, you could fast and easily remove all your duplicates and create a UNIQUE constraint by running:
Of course, this process can be further modified to adapt it for different needs when deleting duplicates. Some examples follow.
✔ Variation for keeping the last entry instead of the first one
Sometimes we need to keep the last duplicated entry instead of the first one.
✔ Variation for performing some tasks on the duplicates, for example keeping a count on the duplicates found
Sometimes we need to perform some further processing on the duplicated entries that are found (such as keeping a count of the duplicates).
The INSERT INTO ... ON DUPLICATE KEY UPDATE query can be used to perform different types of updates for the duplicates found.
✔ Variation for regenerating the auto-incremental field id
Sometimes we use an auto-incremental field and, in order the keep the index as compact as possible, we can take advantage of the deletion of the duplicates to regenerate the auto-incremental field in the new temporary table.
✔ Further variations
Many further modifications are also doable depending on the desired behavior. As an example, the following queries will use a second temporary table to, besides 1) keep the last entry instead of the first one; and 2) increase a counter on the duplicates found; also 3) regenerate the auto-incremental field id while keeping the entry order as it was on the former data.
如果
IGNORE
语句不像我的情况那样工作,您可以使用以下语句:If the
IGNORE
statement won't work like in my case, you can use the below statement:还有另一种解决方案:
There is another solution :
一个易于理解且无需主键即可使用的解决方案:
添加新的布尔列
在重复列和新列上添加约束
将布尔列设置为 true。由于新的约束,这只会在其中一个重复行上成功
删除尚未标记为要保留的行
, 此操作仅在重复行之一上成功
删除添加的列
我建议您保留添加的约束,以便将来防止新的重复项。
A solution that is simple to understand and works with no primary key:
add a new boolean column
add a constraint on the duplicated columns AND the new column
set the boolean column to true. This will succeed only on one of the duplicated rows because of the new constraint
delete rows that have not been marked as tokeep
drop the added column
I suggest that you keep the constraint you added, so that new duplicates are prevented in the future.
这将删除标题、公司和站点具有相同值的重复行。最后一个出现的将被保留,其余的重复项将被删除(如果您想保留第一个出现并删除其他的,请将 id 上的比较更改为大于例如
t1.id > t2.id)
This will delete the duplicate rows with same values for title, company and site. The last occurrence will be kept and the remaining duplicates will be deleted (if you want to keep the first occurrence and delete the others, change the comparison on id to be greater than e.g.
t1.id > t2.id
)如果您有一个包含大量记录的大表,那么上述解决方案将不起作用或花费太多时间。那么我们有不同的解决方案
if you have a large table with huge number of records then above solutions will not work or take too much time. Then we have a different solution
我有这个用于 SQLServer 的查询片段,但我认为它可以在其他 DBMS 中使用,只需稍作修改:
我忘了告诉你,这个查询不会删除重复行中 id 最低的行。如果这对您有用,请尝试以下查询:
I have this query snipet for SQLServer but I think It can be used in others DBMS with little changes:
I forgot to tell you that this query doesn't remove the row with the lowest id of the duplicated rows. If this works for you try this query:
对于所有情况都简单快速:
Simple and fast for all cases:
我找到了一个简单的方法。 (保持最新)
I found a simple way. (keep latest)
更快的方法是将不同的行插入到临时表中。使用删除,我花了几个小时从 800 万行的表中删除重复项。使用insert和distinct,只花了13分钟。
The faster way is to insert distinct rows into a temporary table. Using delete, it took me a few hours to remove duplicates from a table of 8 million rows. Using insert and distinct, it took just 13 minutes.
使用 DELETE JOIN 语句删除重复行
MySQL 为您提供了 DELETE JOIN 语句,您可以使用它快速删除重复的行。
以下语句删除重复行并保留最高的 id:
Delete duplicate rows using DELETE JOIN statement
MySQL provides you with the DELETE JOIN statement that you can use to remove duplicate rows quickly.
The following statement deletes duplicate rows and keeps the highest id:
从版本 8.0 (2018) 开始,MySQL 终于支持 window功能。
窗口函数既方便又高效。这是一个解决方案,演示了如何使用它们来解决此作业。
在子查询中,我们可以使用
ROW_NUMBER()
为表中column1/column2
组中的每条记录分配位置,按id
排序。如果没有重复项,记录将获得行号1
。如果存在重复,它们将按id
升序编号(从1
开始)。一旦子查询中的记录被正确编号,外部查询就会删除行号不为 1 的所有记录。
查询:
As of version 8.0 (2018), MySQL finally supports window functions.
Window functions are both handy and efficient. Here is a solution that demonstrates how to use them to solve this assignment.
In a subquery, we can use
ROW_NUMBER()
to assign a position to each record in the table withincolumn1/column2
groups, ordered byid
. If there is no duplicates, the record will get row number1
. If duplicate exists, they will be numbered by ascendingid
(starting at1
).Once records are properly numbered in the subquery, the outer query just deletes all records whose row number is not 1.
Query :
每当我搜索“从 mysql 中删除重复项”时,我都会访问此页面,但对于我的 theIGNORE 解决方案不起作用,因为我有一个 InnoDB mysql 表,
此代码在任何时候都可以更好地工作
tableToclean = 您需要清理的表的名称
tableToclean_temp = 临时表表创建和删除
I keep visiting this page anytime I google "remove duplicates form mysql" but for my theIGNORE solutions don't work because I have an InnoDB mysql tables
this code works better anytime
tableToclean = the name of the table you need to clean
tableToclean_temp = a temporary table created and deleted
此解决方案将将重复项移动到一个表中,并将唯一项移动到另一个表中。
This solution will move the duplicates into one table and the uniques into another.
使用
DELETE JOIN
语句删除重复行:Delete duplicate rows with the
DELETE JOIN
statement:删除表中的重复记录。
或者
To Delete the duplicate record in a table.
or
这是我使用的,它有效:
t_id 是我独特的列
Here is what I used, and it works:
t_id is my unique column
为了复制具有唯一列的记录,例如 COL1、COL2、COL3 不应被复制(假设我们在表结构中丢失了 3 个唯一列,并且已在表中添加了多个重复条目)
希望能帮助开发人员。
In Order to duplicate records with unique columns, e.g. COL1,COL2, COL3 should not be replicated (suppose we have missed 3 column unique in table structure and multiple duplicate entries have been made into the table)
Hope will help dev.
如果您想删除其中一个重复项并保留另一个,这是完美的选择。请注意,如果没有子查询,您将收到 #1093 错误。
This is perfect if you are trying to delete one of the duplicates and leave the other. Note that without subqueries you would get a #1093 error.
我有一个表忘记在 id 行中添加主键。虽然 id 有 auto_increment 。但有一天,有人在数据库上重放 mysql bin 日志,其中插入了一些重复的行。
删除重复行
选择唯一的重复行并将其导出
来
按 id 删除重复行
插入导出数据中的行。
然后在id上添加主键
I have a table which forget to add a primary key in the id row. Though is has auto_increment on the id. But one day, one stuff replay the mysql bin log on the database which insert some duplicate rows.
I remove the duplicate row by
select the unique duplicate rows and export them
delete the duplicate rows by id
insert the row from the exported data.
Then add the primary key on id
这是我正在使用的查询,它的工作原理与 gem 类似,
这是查询,
它会在没有任何临时表的情况下删除,
好处:如果有 10000 条记录,每条记录为 2 到 5,那么您需要运行5次清除所有重复,
缺点:如果数据有10000个重复,那么每次需要运行10000次才能清除一个,
请在考虑产品数和重复数后使用此方法
this is the query that i am using it and works like gem,
here is the query,
this deletes without any temporary tables,
benefit: if there are 10000 records with each 2 to 5 then you need to run 5 times to clear all duplicated,
Drawback: if the data is with 10000 duplicates each, then you need to run 10000 times to clear one each time,
use this after considering the product count and the duplicate count
我想更具体地说明我删除了哪些记录,所以这是我的解决方案:
I like to be a bit more specific as to which records I delete so here is my solution:
您可以轻松地从此代码中删除重复的记录。
You can easily delete the duplicate records from this code..
我必须对文本字段执行此操作,并遇到了索引 100 字节的限制。
我通过添加一列、对字段进行 md5 哈希并进行更改来解决这个问题。
I had to do this with text fields and came across the limit of 100 bytes on the index.
I solved this by adding a column, doing a md5 hash of the fields, and the doing the alter.