删除mysql中的重复行

3phpmpom  于 2021-06-20  发布在  Mysql
关注(0)|答案(25)|浏览(352)

我有一个包含以下字段的表:

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查询来完成。

vddsk6oq

vddsk6oq1#

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);

根据科斯塔诺斯在评论中的建议:
上面唯一慢的查询是delete,用于数据库非常大的情况。此查询可以更快:

DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
0kjbasz6

0kjbasz62#

您可以轻松地从此代码中删除重复记录。。

$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;
}
ma8fv8wu

ma8fv8wu3#

我有一个表,它忘记在id行中添加主键。虽然它的id是自动递增的,但是有一天,一个东西在数据库上重放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上添加主键

kokeuurv

kokeuurv4#

删除mysql表上的重复项是一个常见的问题,这通常是因为缺少一个约束来避免这些重复项。但这个共同的问题通常伴随着特定的需要。。。这确实需要具体的方法。方法应该有所不同,例如,取决于数据的大小、应保留的重复条目(通常是第一个或最后一个条目)、是否要保留索引,或者是否要对重复数据执行任何其他操作。
mysql本身也有一些特殊性,比如在执行表更新时不能引用from上的同一个表(这将引发mysql错误#1093)。这个限制可以通过使用带有临时表的内部查询来克服(正如上面一些方法所建议的)。但在处理大数据源时,这种内部查询的性能不会特别好。
但是,确实存在一种更好的方法来删除重复项,这种方法既高效又可靠,并且可以很容易地适应不同的需要。
一般的想法是创建一个新的临时表,通常添加一个唯一的约束以避免进一步的重复,并将以前表中的数据插入到新表中,同时处理重复项。这种方法依赖于简单的mysql insert查询,创建一个新的约束以避免进一步的重复,并且不需要使用内部查询来搜索重复项和应该保存在内存中的临时表(因此也适合大数据源)。
这就是如何实现的。假设我们有一个表employee,包含以下列:

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#u eployee表,其结构与employee表完全相同
第2行为新的tmp#u eployee表添加了一个惟一的约束,以避免任何重复
第3行按id扫描原始employee表,将新的employee条目插入新的tmp#u eployee表,同时忽略重复的条目
第#4行重命名表,这样新的employee表将保留所有条目,而不保留重复项,并且在backup#employee表中保留前一个数据的备份副本
⇒ 使用这种方法,160万个寄存器在不到200秒内转换成6k。
chetan,按照此过程,您可以快速轻松地删除所有重复项,并通过运行以下命令创建唯一约束:

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行,orderbyid 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#u duplicates
在第4行,插入到。。。在重复密钥更新查询是用来执行一个额外的更新时,重复发现(在这种情况下,增加计数器)插入到。。。在重复密钥更新查询可用于执行不同类型的更新找到的重复。

✔ 用于重新生成自动增量字段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字段,以便db引擎自动生成一个新字段

✔ 进一步的变化

根据期望的行为,许多进一步的修改也是可行的。例如,以下查询将使用第二个临时表来保存最后一个条目,而不是第一个条目;对发现的重复项增加计数器;另外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;
ia2d9nvy

ia2d9nvy5#

删除表中的重复记录。

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);
wxclj1h5

wxclj1h56#

我找到了一个简单的方法(保持最新)

DELETE t1 FROM tablename t1 INNER JOIN tablename t2 
WHERE t1.id < t2.id AND t1.column1 = t2.column1 AND t1.column2 = t2.column2;
9rbhqvlz

9rbhqvlz7#

此解决方案将把重复项移到一个表中,把唯一项移到另一个表中。

-- 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
pvabu6sv

pvabu6sv8#

如果您有一个包含大量记录的大表,那么上述解决方案将不起作用或花费太多时间。然后我们有一个不同的解决方案

-- 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;
nr9pn0ug

nr9pn0ug9#

-- Here is what I used, and it works:
create table temp_table like my_table;
-- t_id is my unique column
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;
zlwx9yxi

zlwx9yxi10#

从版本8.0(2018)开始,mysql最终支持窗口功能。
窗口功能既方便又高效。下面是一个演示如何使用它们来解决此分配的解决方案。
在子查询中,我们可以使用 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
)
ki1q1bka

ki1q1bka11#

更快的方法是将不同的行插入到临时表中。使用delete,我花了几个小时从一个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;
mi7gmzs6

mi7gmzs612#

一个非常简单的方法是添加一个 UNIQUE 3列上的索引。当你写下 ALTER 声明,包括 IGNORE 关键字。像这样:

ALTER IGNORE TABLE jobs
ADD UNIQUE INDEX idx_name (site_id, title, company);

这将删除所有重复的行。作为一个额外的好处,未来 INSERTs 重复的将出错。像往常一样,您可能希望在运行这样的操作之前进行备份。。。

cedebl8k

cedebl8k13#

如果不想更改列属性,那么可以使用下面的查询。
因为您有一个具有唯一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中,可以使用空安全相等运算符(又称“spaceship运算符”)进一步简化它:

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`;
qgelzfjb

qgelzfjb14#

为了复制具有唯一列的记录,例如col1、col2、col3不应该被复制(假设我们在表结构中遗漏了3列unique,并且表中有多个重复条目)

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;

希望能帮助德夫。

kq0g1dla

kq0g1dla15#

简单易懂且无主键的解决方案:
1) 添加新的布尔列

alter table mytable add tokeep boolean;

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

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

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

update ignore mytable set tokeep = true;

4) 删除尚未标记为保留的行

delete from mytable where tokeep is null;

5) 删除添加的列

alter table mytable drop tokeep;

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

相关问题