我有一个包含以下字段的表:
id (Unique)
url (Unique)
title
company
site_id
现在,我需要删除具有相同 title, company and site_id
的行。一种方法是使用以下 SQL 和脚本 (PHP
):
SELECT title, site_id, location, id, count( * )
FROM jobs
GROUP BY site_id, company, title, location
HAVING count( * ) >1
运行此查询后,我可以使用服务器端脚本删除重复项。
但是,我想知道这是否只能使用 SQL 查询来完成。
HAVING
)不起作用;它将删除所有重复的行,而我认为要求是保留一个。
一个非常简单的方法是在 3 列上添加一个 UNIQUE
索引。编写 ALTER
语句时,请包含 IGNORE
关键字。像这样:
ALTER IGNORE TABLE jobs
ADD UNIQUE INDEX idx_name (site_id, title, company);
这将删除所有重复的行。作为一个额外的好处,未来的重复 INSERTs
将出错。与往常一样,您可能希望在运行此类操作之前进行备份...
如果您不想更改列属性,则可以使用下面的查询。
由于您有一个具有唯一 ID 的列(例如,auto_increment
列),您可以使用它来删除重复项:
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-safe equal operator(又名 "spaceship operator")进一步简化它:
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 对引用您要从中删除的表有限制。您可以使用临时表解决此问题,例如:
create temporary table tmpTable (id int);
insert into 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,适用于数据库非常大的情况。此查询可能会更快:
DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
DELETE
,还有 INSERT
到临时表,我花了很长时间。所以 tmp 表的索引可以帮助很多,create index tmpTable_id_index on tmpTable (id)
,至少对我来说。
create temporary table tmpTable (id int, PRIMARY KEY (id));
删除 MySQL 表上的重复项是一个常见问题,这通常是缺少约束的结果,以避免事先避免这些重复项。但是这个常见问题通常伴随着特定的需求......确实需要特定的方法。方法应该有所不同,具体取决于例如数据的大小、应该保留的重复条目(通常是第一个或最后一个)、是否有要保留的索引,或者我们是否要执行任何额外的操作对重复数据的操作。
MySQL 本身也有一些特殊性,例如在执行表 UPDATE 时无法在 FROM 原因上引用同一个表(它会引发 MySQL 错误 #1093)。可以通过使用带有临时表的内部查询来克服此限制(如上面某些方法所建议的那样)。但是这种内部查询在处理大数据源时表现不佳。
但是,确实存在一种更好的方法来删除重复项,它既高效又可靠,并且可以轻松适应不同的需求。
一般的想法是创建一个新的临时表,通常添加一个唯一约束以避免进一步重复,并将以前表中的数据插入到新表中,同时注意重复。这种方法依赖于简单的 MySQL INSERT 查询,创建一个新的约束来避免进一步的重复,并且不需要使用内部查询来搜索重复项和应该保存在内存中的临时表(因此也适合大数据源)。
这就是它可以实现的方式。鉴于我们有一个表员工,具有以下列:
employee (id, first_name, last_name, start_date, ssn)
为了删除具有重复 ssn 列的行,并仅保留找到的第一个条目,可以遵循以下过程:
-- 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 行创建一个新的 tmp_eployee 表,其结构与雇员表完全相同
第 2 行向新的 tmp_eployee 表添加 UNIQUE 约束以避免任何进一步的重复
第 3 行按 id 扫描原始员工表,将新员工条目插入新的 tmp_eployee 表,同时忽略重复的条目
第 4 行重命名表,以便新员工表包含所有条目而没有重复项,并且以前数据的备份副本保存在 backup_employee 表中
⇒ 使用这种方法,1.6M 寄存器在不到 200 秒的时间内转换为 6k。
Chetan,按照此过程,您可以快速轻松地删除所有重复项并通过运行以下命令创建一个 UNIQUE 约束:
CREATE TABLE tmp_jobs LIKE jobs;
ALTER TABLE tmp_jobs ADD UNIQUE(site_id, title, company);
INSERT IGNORE INTO tmp_jobs SELECT * FROM jobs ORDER BY id;
RENAME TABLE jobs TO backup_jobs, tmp_jobs TO jobs;
当然,这个过程可以进一步修改,以适应删除重复项时的不同需求。下面是一些例子。
✔ 保留最后一个条目而不是第一个条目的变化
有时我们需要保留最后一个重复的条目而不是第一个。
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT * FROM employee ORDER BY id DESC;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
在第 3 行,ORDER BY id DESC 子句使最后一个 ID 优先于其余 ID
✔ 对重复项执行某些任务的变化,例如对找到的重复项进行计数
有时我们需要对找到的重复条目进行一些进一步的处理(例如保持重复的计数)。
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;
在第 3 行,创建了一个新列 n_duplicates
在第 4 行,INSERT INTO ... ON DUPLICATE KEY UPDATE 查询用于在发现重复项时执行附加更新(在这种情况下,增加计数器) INSERT INTO ... ON DUPLICATE KEY UPDATE 查询可以是用于对找到的重复项执行不同类型的更新。
✔ 用于重新生成自动增量字段 id 的变化
有时我们使用自增字段,为了使索引尽可能紧凑,我们可以利用删除重复项在新临时表中重新生成自增字段。
CREATE TABLE tmp_employee LIKE employee;
ALTER TABLE tmp_employee ADD UNIQUE(ssn);
INSERT IGNORE INTO tmp_employee SELECT (first_name, last_name, start_date, ssn) FROM employee ORDER BY id;
RENAME TABLE employee TO backup_employee, tmp_employee TO employee;
在第 3 行,不是选择表上的所有字段,而是跳过 id 字段,以便数据库引擎自动生成一个新字段
✔ 更多变化
许多进一步的修改也是可行的,具体取决于所需的行为。例如,以下查询将使用第二个临时表,除了 1) 保留最后一个条目而不是第一个条目;和 2) 增加对找到的重复项的计数器;也 3) 重新生成自动增量字段 id,同时保持输入顺序与以前的数据相同。
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;
如果 IGNORE
语句在我的情况下不起作用,您可以使用以下语句:
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;
还有另一种解决方案:
DELETE t1 FROM my_table t1, my_table t2 WHERE t1.id < t2.id AND t1.my_field = t2.my_field AND t1.my_field_2 = t2.my_field_2 AND ...
一个易于理解且无需主键的解决方案:
添加一个新的布尔列 alter table mytable add tokeep boolean;在重复的列和新列上添加约束 alter table mytable 添加约束 preventdupe unique (mycol1, mycol2, tokeep);将布尔列设置为 true。由于新的约束 update ignore mytable set tokeep = true; 这只会在重复的行之一上成功;从 mytable 中删除未标记为 tokeep delete 的行,其中 tokeep 为空;删除添加的列 alter table mytable drop tokeep;
我建议您保留添加的约束,以便将来防止出现新的重复项。
这将删除标题、公司和站点具有相同值的重复行。第一次出现将被保留,其余所有重复项将被删除
DELETE t1 FROM tablename t1
INNER JOIN tablename t2
WHERE
t1.id < t2.id AND
t1.title = t2.title AND
t1.company=t2.company AND
t1.site_ID=t2.site_ID;
如果您有一个包含大量记录的大表,那么上述解决方案将不起作用或花费太多时间。然后我们有不同的解决方案
-- 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;
我有这个 SQLServer 的查询片段,但我认为它可以在其他 DBMS 中使用,只需稍作改动:
DELETE
FROM Table
WHERE Table.idTable IN (
SELECT MAX(idTable)
FROM idTable
GROUP BY field1, field2, field3
HAVING COUNT(*) > 1)
我忘了告诉你,这个查询不会删除重复行中 id 最低的行。如果这对您有用,请尝试以下查询:
DELETE
FROM jobs
WHERE jobs.id IN (
SELECT MAX(id)
FROM jobs
GROUP BY site_id, company, title, location
HAVING COUNT(*) > 1)
ERROR 1093: You can't specify target table 'Table' for update in FROM clause
中删除的表中进行选择
"You can't specify target table 'Table' for update in FROM..."
错误,请使用:DELETE FROM Table WHERE Table.idTable IN ( SELECT MAX(idTable) FROM (SELECT * FROM idTable) AS tmp GROUP BY field1, field2, field3 HAVING COUNT(*) > 1)
强制 MySQL 创建临时表。但是在大型数据集中它非常慢......在这种情况下,我会推荐 Andomar 的代码,它要快得多。
更快的方法是将不同的行插入到临时表中。使用删除,我花了几个小时从 800 万行的表中删除重复项。使用 insert 和 distinct,只用了 13 分钟。
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;
TRUNCATE TABLE tableName
,第 5 行应为 INSERT INTO tableName SELECT * FROM tempTableName;
使用 DELETE JOIN 语句删除重复行 MySQL 为您提供了 DELETE JOIN 语句,您可以使用该语句快速删除重复行。
以下语句删除重复行并保留最高 id:
DELETE t1 FROM contacts t1
INNER JOIN
contacts t2 WHERE
t1.id < t2.id AND t1.email = t2.email;
我找到了一个简单的方法。 (保持最新)
DELETE t1 FROM tablename t1 INNER JOIN tablename t2
WHERE t1.id < t2.id AND t1.column1 = t2.column1 AND t1.column2 = t2.column2;
t1
附近给出了意外的令牌
适用于所有情况的简单快速:
CREATE TEMPORARY TABLE IF NOT EXISTS _temp_duplicates AS (SELECT dub.id FROM table_with_duplications dub GROUP BY dub.field_must_be_uniq_1, dub.field_must_be_uniq_2 HAVING COUNT(*) > 1);
DELETE FROM table_with_duplications WHERE id IN (SELECT id FROM _temp_duplicates);
从 8.0 (2018) 版本开始,MySQL 终于支持 window functions。
窗口函数既方便又高效。这是一个演示如何使用它们来解决此任务的解决方案。
在子查询中,我们可以使用 ROW_NUMBER()
为 column1/column2
组中的表中的每条记录分配一个位置,按 id
排序。如果没有重复,记录将获得行号 1
。如果存在重复,它们将按 id
升序编号(从 1
开始)。
一旦记录在子查询中正确编号,外部查询只会删除行号不是 1 的所有记录。
询问 :
DELETE FROM tablename
WHERE id IN (
SELECT id
FROM (
SELECT
id,
ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id) rn
FROM output
) t
WHERE rn > 1
)
rn = 1
。然后我们删除原始文件并重命名。
每当我谷歌“从 mysql 中删除重复项”时,我都会一直访问这个页面,但是对于我的 theIGNORE 解决方案不起作用,因为我有一个 InnoDB mysql 表
此代码随时可以更好地工作
CREATE TABLE tableToclean_temp LIKE tableToclean;
ALTER TABLE tableToclean_temp ADD UNIQUE INDEX (fontsinuse_id);
INSERT IGNORE INTO tableToclean_temp SELECT * FROM tableToclean;
DROP TABLE tableToclean;
RENAME TABLE tableToclean_temp TO tableToclean;
tableToclean = 您需要清理的表的名称
tableToclean_temp = 创建和删除的临时表
此解决方案会将重复项移动到一个表中,并将唯一项移动到另一个表中。
-- 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
SELECT * FROM jobs GROUP BY site_id, company, title, location
?
使用 DELETE JOIN
语句删除重复行:
DELETE t1 FROM table_name t1
JOIN table_name t2
WHERE
t1.id < t2.id AND
t1.title = t2.title AND t1.company = t2.company AND t1.site_id = t2.site_id;
t1.id < t2.id
而不是 t1.id <> t2.id
?如果 t1 和 t2 互换,这将防止人为错误。
DELETE...JOIN
语法的答案,包括自然连接和内部连接。这似乎没有添加任何新内容。
删除表中的重复记录。
delete from job s
where rowid < any
(select rowid from job k
where s.site_id = k.site_id and
s.title = k.title and
s.company = k.company);
或者
delete from job s
where rowid not in
(select max(rowid) from job k
where s.site_id = k.site_id and
s.title = k.title and
s.company = k.company);
这是我使用的,它有效:
create table temp_table like my_table;
t_id 是我唯一的专栏
insert into temp_table (id) select id from my_table GROUP by t_id;
delete from my_table where id not in (select id from temp_table);
drop table temp_table;
为了复制具有唯一列的记录,例如 COL1、COL2、COL3 不应该被复制(假设我们在表结构中遗漏了 3 列唯一并且在表中创建了多个重复条目)
DROP TABLE TABLE_NAME_copy;
CREATE TABLE TABLE_NAME_copy LIKE TABLE_NAME;
INSERT INTO TABLE_NAME_copy
SELECT * FROM TABLE_NAME
GROUP BY COLUMN1, COLUMN2, COLUMN3;
DROP TABLE TABLE_NAME;
ALTER TABLE TABLE_NAME_copy RENAME TO TABLE_NAME;
希望对开发者有所帮助。
我有一个表忘记在 id 行中添加主键。虽然在 id 上有 auto_increment。但是有一天,一个东西在数据库上重放了 mysql bin 日志,其中插入了一些重复的行。
我删除重复的行
选择唯一的重复行并导出它们 select T1.* from table_name T1 inner join (select count(*) as c,id from table_name group by id) T2 on T1.id = T2.id where T2.c > 1 group by T1.id;按 id 删除重复的行从导出的数据中插入行。然后在id上添加主键
如果您尝试删除其中一个重复项并保留另一个,这是完美的。请注意,如果没有子查询,您将收到 #1093 错误。
DELETE FROM table_name
WHERE id IN (
SELECT * FROM (SELECT n.id FROM table_name n
WHERE n.column2 != "value"
GROUP BY n.column HAVING COUNT(n.column ) > 1) x
)
我想更具体地说明我删除了哪些记录,所以这是我的解决方案:
delete
from jobs c1
where not c1.location = 'Paris'
and c1.site_id > 64218
and exists
(
select * from jobs c2
where c2.site_id = c1.site_id
and c2.company = c1.company
and c2.location = c1.location
and c2.title = c1.title
and c2.site_id > 63412
and c2.site_id < 64219
)
您可以轻松地从此代码中删除重复记录..
$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;
}
我不得不对文本字段执行此操作,并且遇到了索引上 100 个字节的限制。
我通过添加一列、对字段进行 md5 哈希以及进行更改来解决此问题。
ALTER TABLE table ADD `merged` VARCHAR( 40 ) NOT NULL ;
UPDATE TABLE SET merged` = MD5(CONCAT(`col1`, `col2`, `col3`))
ALTER IGNORE TABLE table ADD UNIQUE INDEX idx_name (`merged`);
set session old_alter_table=1;