ลบแถวที่ซ้ำกันใน MySQL


375

ฉันมีตารางที่มีฟิลด์ต่อไปนี้:

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


1
คำถามด่วน: ไม่ต้องการให้มีการซ้ำกัน (ชื่อ, บริษัท , site_id) หรือไม่? ถ้าเป็นเช่นนั้นฉันจะตั้งค่าข้อ จำกัด ในฐานข้อมูลเพื่อบังคับใช้ชื่อเรื่อง บริษัท และ site_id ให้ไม่ซ้ำใคร ซึ่งหมายความว่าคุณไม่จำเป็นต้องมีกระบวนการล้างข้อมูล และใช้ SQL เพียงบรรทัดเดียวเท่านั้น
J. Polfer

1
โปรดอ้างอิงลิงค์ของ stackoverflowมันใช้งานได้ดีสำหรับฉัน

ฉันสามารถแนะนำวิธีแก้ปัญหานี้ (โพสต์ในหัวข้ออื่น): stackoverflow.com/a/4685232/195835
Simon East

คุณสามารถตรวจสอบคำตอบนี้ได้
Jose Rui Santos

คำตอบ:


607

วิธีง่ายๆในการทำเช่นนี้คือการเพิ่มUNIQUEดัชนีในคอลัมน์ 3 คอลัมน์ เมื่อคุณเขียนALTERคำสั่งรวมถึงIGNOREคำหลัก ชอบมาก

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

นี่จะเป็นการวางแถวที่ซ้ำกันทั้งหมด ในฐานะที่เป็นประโยชน์เพิ่มเติมในอนาคตINSERTsที่ซ้ำกันจะผิดพลาด เช่นเคยคุณอาจต้องการสำรองข้อมูลก่อนใช้งานสิ่งนี้ ...


8
ที่น่าสนใจแต่ข้อสันนิษฐานของประโยค IGNORE ที่ทำขึ้นสำหรับการลบรายการที่ซ้ำกันนั้นเป็นข้อกังวลที่อาจไม่ตรงกับความต้องการ ค่าที่ไม่ถูกต้องถูกตัดทอนให้เข้ากับการจับคู่ที่ดีที่สุดซึ่งเป็นที่ยอมรับสำหรับคุณหรือไม่
OMG Ponies

75
เพียงบันทึกถ้าคุณใช้ InnoDB แล้วคุณอาจมีปัญหากับมันมีข้อผิดพลาดที่รู้จักกันเกี่ยวกับการใช้ ALTER IGNORE TABLE กับฐานข้อมูล InnoDB
DarkMantis

27
ข้อผิดพลาดดังกล่าว @DarkMantis เรียกและวิธีการแก้ปัญหาของมัน
Jordan Arseno

42
สำหรับตาราง InnoDB ให้ดำเนินการค้นหาต่อไปนี้ก่อน:set session old_alter_table=1;
shock_one

51
นี่ไม่รองรับอีกต่อไปใน 5.7.4, dev.mysql.com/doc/refman/5.7/en/alter-table.html
Ray Baxter

180

หากคุณไม่ต้องการแก้ไขคุณสมบัติคอลัมน์คุณสามารถใช้แบบสอบถามด้านล่าง

เนื่องจากคุณมีคอลัมน์ที่มี 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 ที่ปลอดภัย (aka "ตัวดำเนินการยานอวกาศ" ):

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

3
วิธีการแก้ปัญหานี้ทำงานไม่ถูกต้องฉันพยายามสร้างระเบียนที่ซ้ำกันและทำบางสิ่งบางอย่างเช่น (รับผลกระทบ 20 แถว) แต่ถ้าคุณเรียกใช้อีกครั้งจะแสดงให้คุณเห็น (มีผลกระทบ 4 แถว) และต่อ ๆ ไปเรื่อย ๆ ซึ่งค่อนข้างน่าสงสัยและนี่คือสิ่งที่ดีที่สุดสำหรับฉันมันเกือบจะเหมือนกัน แต่ใช้งานได้ในครั้งเดียวฉันแก้ไขวิธีแก้ปัญหา
Nassim

1
@ Nassim: คุณต้องทำอะไรที่แตกต่างจากคำตอบนี้เพราะมันใช้งานได้ดีสำหรับฉัน (ใน MySQL)
Lawrence Dol

3
สำหรับทุกคนที่สับสนเช่นฉันจำเป็นต้องใช้เงื่อนไขการเปรียบเทียบค่า NULL เนื่องจากค่า NULL ไม่เท่ากับ NULL ใน MySQL หากคอลัมน์ที่เกี่ยวข้องรับประกันว่าจะไม่เป็น NULL คุณสามารถละทิ้งข้อกำหนดเหล่านี้ได้
เอียน

3
ใช่คำตอบที่ยอมรับนั้นไม่ถูกต้องอีกต่อไปเนื่องจาก MYSQL 5.7 ดังนั้นนี่ควรเป็นคำตอบที่ยอมรับได้เนื่องจากเป็นสากลและไม่ต้องการสร้างตารางชั่วคราวเช่นกัน
นั้น Ben

1
ช้ามากหากมีสำเนาจำนวนมากของระเบียนที่กำหนด (เช่น 100 จะลดลงถึง 1) และหลายระเบียนที่มีเงื่อนไขนั้น แนะนำstackoverflow.com/a/4685232/199364แทน IMHO เสมอใช้วิธีการเชื่อมโยง; มันเป็นเทคนิคที่เร็วกว่าโดยเนื้อแท้
ToolmakerSteve

78

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

3
@ Randomar ใช้งานได้ดียกเว้นเมื่อหนึ่งในเขตข้อมูลในตำแหน่งที่ประโยคมี nulls ตัวอย่าง: sqlfiddle.com/#!2/983f3/1
coder

1
แทรก SQL มีราคาแพงหรือไม่ ฉันสงสัยเพราะมันหมดเวลาในฐานข้อมูล MySQL ของฉัน
Cassio

4
การสืบค้นที่ช้าเพียงอย่างเดียวนี่คือการลบหนึ่งในกรณีที่คุณมีฐานข้อมูลขนาดใหญ่ ข้อความค้นหานี้อาจเร็วกว่า:DELETE FROM YourTable USING YourTable, tmpTable WHERE YourTable.id=tmpTable.id
Kostanos

@ Kostanos ไม่เพียงDELETEแต่ยังINSERTไปที่ตารางชั่วคราวฉันใช้เวลานานมาก ดังนั้นดัชนีสำหรับตาราง tmp สามารถช่วยได้มากcreate index tmpTable_id_index on tmpTable (id)อย่างน้อยสำหรับฉัน
Jiezhi.G

1
หากตารางของคุณมีขนาดใหญ่ก็ควรค่าแก่การเพิ่มดัชนีด้วย:create temporary table tmpTable (id int, PRIMARY KEY (id));
ดัลลัสคลาร์ก

44

หาก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;

1
ใช้งานได้ดีถ้าคุณมีการตั้งค่า innoDB ด้วยข้อ จำกัด ของรหัสต่างประเทศ
magdmartin

@magdmartin แต่ข้อ จำกัด ต่างประเทศจะป้องกันการลบตารางหรือไม่
Basilevs

1
คำสั่ง IGNORE ไม่ได้ผลสำหรับฉันและสิ่งนี้ใช้งานได้ดีในการขจัดข้อมูลซ้ำซ้อนถึง 5 ล้านแผ่น ไชโย
Mauvis Ledford

32

การลบรายการที่ซ้ำกันในตาราง MySQL เป็นปัญหาที่พบได้ทั่วไปซึ่งเป็นผลมาจากข้อ จำกัด ที่ขาดหายไปเพื่อหลีกเลี่ยงรายการที่ซ้ำกันก่อนถึงมือ แต่ปัญหาที่พบบ่อยนี้มักจะมาพร้อมกับความต้องการที่เฉพาะเจาะจง ... ที่ต้องมีวิธีการเฉพาะ วิธีการควรแตกต่างกันไปขึ้นอยู่กับขนาดของข้อมูลรายการที่ซ้ำที่ควรเก็บไว้ (โดยทั่วไปเป็นรายการแรกหรือรายการสุดท้าย) ไม่ว่าจะมีดัชนีที่จะเก็บไว้หรือไม่ว่าเราต้องการดำเนินการเพิ่มเติมใด ๆ การดำเนินการกับข้อมูลที่ซ้ำกัน

นอกจากนี้ยังมีลักษณะเฉพาะบางอย่างใน MySQL เช่นไม่สามารถอ้างอิงตารางเดียวกันในสาเหตุ FROM เมื่อดำเนินการตาราง UPDATE (จะทำให้เกิดข้อผิดพลาด 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;

คำอธิบายทางเทคนิค

  • Line # 1 สร้างตารางtmp_eployeeใหม่ที่มีโครงสร้างเหมือนกับตารางพนักงาน
  • Line # 2 เพิ่มข้อ จำกัด UNIQUE ให้กับตารางtmp_eployeeใหม่เพื่อหลีกเลี่ยงการซ้ำซ้อนเพิ่มเติม
  • บรรทัดที่ 3 สแกนตารางพนักงานเดิมโดยใช้รหัสการแทรกรายการพนักงานใหม่ลงในตารางtmp_eployeeใหม่ในขณะที่ละเว้นรายการซ้ำ
  • บรรทัดที่ # 4 เปลี่ยนชื่อตารางเพื่อให้ตารางพนักงานใหม่เก็บรายการทั้งหมดโดยไม่มีรายการที่ซ้ำกันและสำเนาสำรองของข้อมูลเดิมจะถูกเก็บไว้ในตารางbackup_employee

⇒เมื่อใช้วิธีนี้การลงทะเบียน 1.6M จะถูกแปลงเป็น 6k ในเวลาน้อยกว่า 200 วินาที

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ทำให้ 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สามารถ ใช้เพื่อดำเนินการอัพเดตประเภทต่าง ๆ สำหรับรายการซ้ำที่พบ

✔ความผันแปรสำหรับการสร้างรหัสฟิลด์ที่เพิ่มขึ้นอัตโนมัติ

บางครั้งเราใช้เขตข้อมูลที่เพิ่มขึ้นอัตโนมัติและเพื่อให้ดัชนีมีขนาดกะทัดรัดที่สุดเท่าที่จะทำได้เราสามารถใช้ประโยชน์จากการลบข้อมูลที่ซ้ำกันเพื่อสร้างฟิลด์ที่เพิ่มขึ้นอัตโนมัติในตารางชั่วคราวใหม่

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) สร้างรหัสฟิลด์ที่เพิ่มขึ้นอัตโนมัติอีกครั้งในขณะที่รักษาลำดับการเข้าใช้เหมือนเดิมกับข้อมูลเดิม

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;

27

มีวิธีแก้ไขปัญหาอื่น:

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

4
สิ่งนี้แตกต่างจากคำตอบของ @ rehriff ซึ่งเขาส่งมา 6 เดือนก่อนหน้านี้อย่างไร
Lawrence Dol

@ ลอว์เรนซ์ดอลฉันเดาว่ามันอ่านง่ายขึ้นอีกหน่อยและฉันคิดว่าคำตอบของเขาไม่เหมือนกันในเวลาที่ฉันตอบและฉันคิดว่าคำตอบของเขาได้รับการแก้ไข
Mostafa -T

1
อืมม มันใช้เวลานานเกินไปสำหรับฉันในขณะที่จำนวนบันทึกไม่มาก!
SUB

8

หากคุณมีตารางจำนวนมากที่มีบันทึกจำนวนมากการแก้ปัญหาข้างต้นจะไม่ทำงานหรือใช้เวลามากเกินไป จากนั้นเราก็มีทางออกที่แตกต่าง

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

6

ฉันมีส่วนย่อยแบบสอบถามนี้สำหรับ 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)

สิ่งนี้จะไม่ทำงานหากมีกลุ่มที่ซ้ำกันมากกว่าสองกลุ่ม
OMG Ponies

11
น่าเสียดายที่ MySQL ไม่อนุญาตให้คุณเลือกจากตารางที่คุณลบออกERROR 1093: You can't specify target table 'Table' for update in FROM clause
Andomar

1
เพื่อแก้ไข"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 ซึ่งเร็วกว่ามาก
lepe

6

วิธีที่เร็วกว่าคือการแทรกแถวที่แตกต่างกันลงในตารางชั่วคราว ใช้การลบฉันใช้เวลาสองสามชั่วโมงเพื่อลบรายการที่ซ้ำกันออกจากตารางจำนวน 8 ล้านแถว ใช้การแทรกและชัดเจนใช้เวลาเพียง 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;  

1
บรรทัดที่ 4 ของคุณควรพูดTRUNCATE TABLE tableNameและบรรทัดที่ 5 ควรบอกว่าINSERT INTO tableName SELECT * FROM tempTableName;
ซานา

5

โซลูชันที่เข้าใจง่ายและทำงานได้โดยไม่มีคีย์หลัก:

1) เพิ่มคอลัมน์บูลีนใหม่

alter table mytable add tokeep boolean;

2) เพิ่มข้อ จำกัด ในคอลัมน์ที่ซ้ำกันและคอลัมน์ใหม่

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

3) ตั้งค่าคอลัมน์บูลีนเป็นจริง สิ่งนี้จะสำเร็จในแถวที่ซ้ำหนึ่งแถวเนื่องจากข้อ จำกัด ใหม่

update ignore mytable set tokeep = true;

4) ลบแถวที่ไม่ได้ทำเครื่องหมายเป็น tokeep

delete from mytable where tokeep is null;

5) วางคอลัมน์เพิ่ม

alter table mytable drop tokeep;

ฉันขอแนะนำให้คุณรักษาข้อ จำกัด ที่คุณเพิ่มไว้เพื่อป้องกันการซ้ำซ้อนใหม่ในอนาคต


1
วิธีนี้ใช้งานได้ดีใน mysql 5.7where ทางออกที่ได้รับการยอมรับไม่ทำงานอีกต่อไป
Robin31

5

ลบแถวที่ซ้ำกันโดยใช้คำสั่ง 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;


4

ง่ายและรวดเร็วสำหรับทุกกรณี:

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

รหัสข้อผิดพลาด: 1,055 นิพจน์ # 2 ของรายการ SELECT ไม่ได้อยู่ในกลุ่มตามข้อและประกอบด้วยคอลัมน์ที่ไม่รวม 'dub.id' ซึ่งไม่ได้ขึ้นอยู่กับหน้าที่ของคอลัมน์ในกลุ่มตามข้อ; สิ่งนี้เข้ากันไม่ได้กับ sql_mode = only_full_group_by
Swoogan

คุณสามารถปิดการใช้งาน "hard control" ด้วย sql_mode ดูstackoverflow.com/questions/23921117/disable-only-full-group-by
artemiuz

4

การดำเนินการนี้จะลบแถวที่ซ้ำกันด้วยค่าเดียวกันสำหรับชื่อ บริษัท และเว็บไซต์ การเกิดขึ้นครั้งแรกจะถูกเก็บไว้และส่วนที่ซ้ำกันทั้งหมดจะถูกลบ

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;

มันช้า (5w + แถวล็อครอหมดเวลา) แต่ใช้งานได้
yurenchen

3

ฉันไปที่หน้านี้ทุกครั้งที่ฉัน google "ลบแบบฟอร์มที่ซ้ำกัน mysql" แต่สำหรับโซลูชัน theORE ของฉันไม่ทำงานเพราะฉันมีตาราง 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 = ตารางชั่วคราวที่สร้างและลบ


2

การแก้ปัญหานี้จะย้ายที่ซ้ำกันในตารางหนึ่งและไม่ซ้ำเข้าไปอีก

-- 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?
timctran

2

ขณะที่รุ่น 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
)

1

หากต้องการลบระเบียนที่ซ้ำกันในตาราง

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

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

0

เพื่อที่จะทำซ้ำระเบียนที่มีคอลัมน์ที่ไม่ซ้ำกันเช่น 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;

หวังว่าจะช่วยนักพัฒนา


0

TL; TR;

บทช่วยสอนที่อธิบายอย่างมากเพื่อแก้ไขปัญหานี้สามารถดูได้ที่mysqltutorial.orgเว็บไซต์ :

วิธีการลบแถวที่ซ้ำกันใน MySQL

มันแสดงให้เห็นอย่างชัดเจนถึงวิธีการลบแถวที่ซ้ำกันในสามวิธี :

A)การใช้DELETE JOINคำสั่ง

B)การใช้ตารางกลาง

C)การใช้ROW_NUMBER()ฟังก์ชั่น

ฉันหวังว่ามันจะช่วยให้ใครบางคน


0

ฉันมีตารางที่ลืมเพิ่มคีย์หลักในแถว id แม้ว่าจะมี auto_increment ใน ID แต่วันหนึ่งมีสิ่งหนึ่งที่เล่นซ้ำ mysql bin log ในฐานข้อมูลซึ่งแทรกแถวที่ซ้ำกันบางส่วน

ฉันลบแถวที่ซ้ำกันด้วย

  1. เลือกแถวที่ซ้ำกันที่ไม่ซ้ำกันและส่งออก

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;

  1. ลบแถวที่ซ้ำกันโดยใช้ ID

  2. แทรกแถวจากข้อมูลที่ส่งออก

  3. จากนั้นเพิ่มคีย์หลักในรหัส


-2

ฉันชอบที่จะเจาะจงมากขึ้นว่าจะลบระเบียนใดดังนั้นนี่คือโซลูชันของฉัน:

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
)

-4

คุณสามารถลบระเบียนที่ซ้ำกันออกจากรหัสนี้ได้อย่างง่ายดาย ..

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

3
นี่เป็นงานฐานข้อมูลที่แย่มากควรทำในฐานข้อมูลซึ่งจะเร็วกว่ามากแทนที่จะส่งข้อมูลอย่างต่อเนื่องระหว่าง php / mysql เพราะคุณรู้ดีกว่าอีก
สูงสุด

-4

ฉันต้องทำเช่นนี้กับเขตข้อมูลข้อความและมาถึงขีด จำกัด ของ 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`);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.