ค้นหาระเบียนที่ซ้ำกันใน MySQL


650

ฉันต้องการดึงระเบียนที่ซ้ำกันในฐานข้อมูล MySQL สามารถทำได้ด้วย:

SELECT address, count(id) as cnt FROM list
GROUP BY address HAVING cnt > 1

ซึ่งผลลัพธ์ใน:

100 MAIN ST    2

ฉันต้องการดึงเพื่อให้มันแสดงแต่ละแถวที่ซ้ำกัน สิ่งที่ต้องการ:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

มีความคิดเกี่ยวกับวิธีการนี้ที่สามารถทำได้? ฉันพยายามหลีกเลี่ยงการทำอันแรกจากนั้นค้นหารายการที่ซ้ำกันด้วยแบบสอบถามที่สองในรหัส

คำตอบ:


684

กุญแจสำคัญคือการเขียนแบบสอบถามนี้เพื่อให้มันสามารถใช้เป็นแบบสอบถามย่อย

SELECT firstname, 
   lastname, 
   list.address 
FROM list
   INNER JOIN (SELECT address
               FROM   list
               GROUP  BY address
               HAVING COUNT(id) > 1) dup
           ON list.address = dup.address;

69
ระวังด้วยคำสั่งย่อย คิวรีย่อยคือ / อาจไม่ดีอย่างน่าขันสำหรับข้อกังวลด้านประสิทธิภาพ หากสิ่งนี้จำเป็นต้องเกิดขึ้นบ่อยครั้งและ / หรือมีระเบียนที่ซ้ำกันจำนวนมากฉันจะพิจารณาย้ายการประมวลผลออกจากฐานข้อมูลและเป็นชุดข้อมูล
bdwakefield

11
มันเป็นคิวรีย่อยที่ไม่มีการเชื่อมโยงดังนั้นจึงไม่ควรเลวร้ายถ้าสมมติว่าการสืบค้นเพียงอย่างเดียวไม่ได้ออกแบบมาไม่ดี
ʞɔıu

น่ารัก เดาว่านี่คือระบบที่ล้อมรอบ "ข้อผิดพลาด 1248 (42000): ทุกตารางที่ได้รับจะต้องมีนามแฝงของตัวเอง"
doublejosh

3
นี่เป็นความคิดที่ถูกต้อง แต่อีกครั้งดังต่อไปนี้จะใช้งานได้เฉพาะหากที่อยู่ได้รับการรับรองว่าเป็นมาตรฐาน ...
Matt

30
+1 ด้วยแบบสอบถามนี้คุณสามารถค้นหารายการที่ซ้ำกัน แต่ยังเพิ่มขึ้นเป็นสามเท่า, เพิ่มเป็นสี่เท่า ..... และอื่น ๆ
albanx

352
SELECT date FROM logs group by date having count(*) >= 2

5
นี่เป็นคิวรี่การทำงานที่ง่ายที่สุดที่จะใช้กับ Laravel เพียงแค่ต้องเพิ่ม->having(DB::raw('count(*)'), '>', 2)ไปยังแบบสอบถาม ขอบคุณมาก!
Kovah

1
ใช้งานได้ดีกับตาราง 10 ล้านแถว นี่ควรเป็นคำตอบที่ดีที่สุด
Terry Lin

13
ระวังด้วยคำตอบนี้ มันจะส่งคืนหนึ่งในรายการที่ซ้ำกัน หากคุณมีระเบียนเดียวกันมากกว่า 2 สำเนาคุณจะไม่เห็นพวกเขาทั้งหมดและหลังจากลบบันทึกที่ส่งคืนคุณจะยังคงมีข้อมูลที่ซ้ำกันในตารางของคุณ
Mikiko Jane

7
ทำไม>=2? เพียงแค่ใช้HAVING COUNT(*) > 1
BadHorsie

2
@TerryLin พิจารณาว่านี่ไม่ได้แก้ปัญหาที่ระบุไว้ แต่เดิม (ซึ่งเป็นวิธีการคืนรายการที่ซ้ำกันทั้งหมด) ฉันไม่เห็นด้วย
Michael

198

ทำไมไม่เพียงเข้าร่วมตารางด้วยตัวเอง?

SELECT a.firstname, a.lastname, a.address
FROM list a
INNER JOIN list b ON a.address = b.address
WHERE a.id <> b.id

ต้องการ DISTINCT หากที่อยู่อาจมีมากกว่าสองครั้ง


20
ฉันทดสอบสิ่งนี้ด้วยและมันก็ช้าลงเกือบ 6 เท่าเมื่อเทียบกับวิธีแก้ปัญหาที่ยอมรับในสถานการณ์ของฉัน (MySQL ล่าสุดตาราง 120.000 แถว) นี่อาจเป็นเพราะต้องการตารางชั่วคราวให้เรียกใช้อธิบายทั้งคู่เพื่อดูความแตกต่าง

4
ฉันเปลี่ยนส่วนสุดท้ายของข้อความค้นหาเป็นWHERE a.id > b.idเพื่อกรองรายการที่ซ้ำกันที่ใหม่กว่าเท่านั้นด้วยวิธีนี้ฉันสามารถทำDELETEผลลัพธ์โดยตรง สลับการเปรียบเทียบเพื่อแสดงรายการซ้ำที่เก่ากว่า
Stoffe

1
ใช้เวลา 50 วินาทีในการทำงาน @ คำตอบของ doublejosh ใช้เวลา 0.13 วินาที
antonagestam

ฉันต้องเพิ่มว่าคำตอบนี้ให้คำตอบที่ซ้ำกันทั้งๆที่ในกรณีที่หนึ่งที่อยู่เป็นสามเท่าแถวเอาท์พุทเป็นสองเท่า หากเป็นสี่เท่าฉันเชื่อว่าคำตอบจะเพิ่มเป็นสามเท่า
Wli

ฉันทดสอบสิ่งนี้ใน leetcode " leetcode.com/problems/duplicate-emails " มันเร็วกว่าเมื่อเปรียบเทียบกับคิวรีย่อย
คลื่น

56

ฉันพยายามเลือกคำตอบที่ดีที่สุดสำหรับคำถามนี้ แต่ฉันก็ค่อนข้างสับสน จริง ๆ แล้วฉันต้องการสิ่งนั้นในสนามเดียวจากโต๊ะของฉัน ตัวอย่างต่อไปนี้จากลิงค์นี้ทำงานได้ดีมากสำหรับฉัน:

SELECT COUNT(*) c,title FROM `data` GROUP BY title HAVING c > 1;

ทำงานเหมือนจับใจ!
Vinícius

47
select `cityname` from `codcities` group by `cityname` having count(*)>=2

นี่เป็นข้อความค้นหาที่คล้ายกันที่คุณถามและใช้งานได้ 200% และใช้งานง่ายเช่นกัน สนุก!!!


37

มันง่ายกว่านี้ไหม:

SELECT *
FROM tc_tariff_groups
GROUP BY group_id
HAVING COUNT(group_id) >1

?


1
ทำงานให้ฉันโดยที่ฉันต้องประมวลผลแถวที่ซ้ำกันประมาณ 10,000 แถวเพื่อทำให้มันไม่เหมือนใครเร็วกว่าการโหลดทั้งหมด 600,000 แถว
adrianTNT

1
ง่ายขึ้นมาก
Shwet

35

ค้นหาผู้ใช้ที่ซ้ำกันด้วยที่อยู่อีเมลด้วยข้อความค้นหานี้ ...

SELECT users.name, users.uid, users.mail, from_unixtime(created)
FROM users
INNER JOIN (
  SELECT mail
  FROM users
  GROUP BY mail
  HAVING count(mail) > 1
) dupes ON users.mail = dupes.mail
ORDER BY users.mail;

2
หากต้องการค้นหาสำเนาที่แท้จริงคุณจะต้องใช้แบบสอบถามภายใน นี่เป็นวิธีที่เร็วกว่าคำตอบอื่น ๆ
antonagestam

20

เราสามารถพบรายการที่ซ้ำกันขึ้นอยู่กับมากกว่าหนึ่งช่องเช่นกันสำหรับกรณีเหล่านั้นคุณสามารถใช้รูปแบบด้านล่าง

SELECT COUNT(*), column1, column2 
FROM tablename
GROUP BY column1, column2
HAVING COUNT(*)>1;

16

ค้นหาที่อยู่ที่ซ้ำกันนั้นซับซ้อนกว่าที่คิดโดยเฉพาะถ้าคุณต้องการความแม่นยำ แบบสอบถาม MySQL ไม่เพียงพอในกรณีนี้ ...

ฉันทำงานที่SmartyStreetsที่ซึ่งเราทำการตรวจสอบความถูกต้องและยกเลิกการทำซ้ำและสิ่งอื่น ๆ และฉันได้เห็นความท้าทายที่หลากหลายด้วยปัญหาที่คล้ายกัน

มีบริการของบุคคลที่สามหลายแห่งซึ่งจะตั้งค่าสถานะรายการซ้ำให้คุณ การทำเช่นนี้เพียงอย่างเดียวกับแบบสอบถามย่อย MySQL จะไม่คำนึงถึงความแตกต่างในรูปแบบและมาตรฐานของที่อยู่ USPS (สำหรับที่อยู่ในสหรัฐฯ) มีแนวทางบางประการในการสร้างมาตรฐานเหล่านี้ แต่มีเพียงผู้ค้าจำนวนหนึ่งเท่านั้นที่ได้รับการรับรองให้ดำเนินการดังกล่าว

ดังนั้นฉันขอแนะนำคำตอบที่ดีที่สุดสำหรับคุณคือส่งออกตารางเป็นไฟล์ CSV และส่งไปยังตัวประมวลผลรายการที่มีความสามารถ หนึ่งในนั้นคือเครื่องมือตรวจสอบที่อยู่ SmartyStreets ซึ่งจะทำให้คุณได้ภายในไม่กี่วินาทีถึงสองสามนาทีโดยอัตโนมัติ มันจะตั้งค่าสถานะแถวที่ซ้ำกันด้วยเขตข้อมูลใหม่ที่เรียกว่า "ซ้ำ" และความคุ้มค่าของYมัน


6
+1 สำหรับการเห็นปัญหาที่เกี่ยวข้องกับการจับคู่สตริงที่อยู่แม้ว่าคุณอาจต้องการระบุว่าคำถาม "ระเบียนที่ซ้ำกัน" ของ OP ไม่ซับซ้อนในตัวเอง แต่เมื่อเปรียบเทียบที่อยู่
เรื่อง

13

โซลูชันอื่นจะใช้นามแฝงของตารางเช่น:

SELECT p1.id, p2.id, p1.address
FROM list AS p1, list AS p2
WHERE p1.address = p2.address
AND p1.id != p2.id

สิ่งที่คุณทำจริง ๆ ในกรณีนี้คือการนำตารางรายการดั้งเดิมสร้างตารางเก็บสองp - p 1และp 2 - จากนั้นจากนั้นทำการเข้าร่วมในคอลัมน์ที่อยู่ (บรรทัด 3) บรรทัดที่ 4 ทำให้แน่ใจว่าระเบียนเดียวกันไม่แสดงหลายครั้งในชุดผลลัพธ์ของคุณ ("ซ้ำกันซ้ำ")


1
ใช้งานได้ดี หาก WHERE กำลังตรวจสอบด้วย LIKE จะพบอะพอสโทรฟีเช่นกัน ทำให้การสืบค้นช้าลง แต่ในกรณีของฉันมันเป็นตัวจับเวลา
gossi


10

จะเป็นการเลือกรายการที่ซ้ำกันในหนึ่งตารางผ่านไม่มีแบบสอบถามย่อย

SELECT  *
FROM    (
        SELECT  ao.*, (@r := @r + 1) AS rn
        FROM    (
                SELECT  @_address := 'N'
                ) vars,
                (
                SELECT  *
                FROM
                        list a
                ORDER BY
                        address, id
                ) ao
        WHERE   CASE WHEN @_address <> address THEN @r := 0 ELSE 0 END IS NOT NULL
                AND (@_address := address ) IS NOT NULL
        ) aoo
WHERE   rn > 1

แบบสอบถามนี้มีการเลียนแบบตามจริงROW_NUMBER()ปรากฏในOracleและSQL Server

ดูบทความในบล็อกของฉันสำหรับรายละเอียด:


20
ไม่ใช่เพื่อ nitpick แต่FROM (SELECT ...) aooเป็น subquery :-P
Rocket Hazmat

8

สิ่งนี้จะแสดงจำนวนการซ้ำซ้อนและการเรียงลำดับผลลัพธ์โดยไม่ต้องเข้าร่วม

SELECT  `Language` , id, COUNT( id ) AS how_many
FROM  `languages` 
GROUP BY  `Language` 
HAVING how_many >=2
ORDER BY how_many DESC

สมบูรณ์แบบเพราะมันยังบอกว่ามีกี่รายการซ้ำ
denis

4
 SELECT firstname, lastname, address FROM list
 WHERE 
 Address in 
 (SELECT address FROM list
 GROUP BY address
 HAVING count(*) > 1)

ลองอันนี้เหมือนกัน แต่ดูเหมือนจะเพิ่งแขวน เชื่อว่าการส่งคืนจากแบบสอบถามภายในไม่เป็นไปตามรูปแบบพารามิเตอร์ IN
doublejosh

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

ฉันเป็นห่วง IN ต้องใช้รายการที่คั่นด้วยเครื่องหมายจุลภาคแทนที่จะเป็นคอลัมน์ซึ่งผิด นี่คือข้อความค้นหาที่ใช้งานได้สำหรับฉัน:SELECT users.name, users.uid, users.mail, from_unixtime(created) FROM users INNER JOIN ( SELECT mail FROM users GROUP BY mail HAVING count(mail) > 1 ) dup ON users.mail = dup.mail ORDER BY users.mail, users.created;
doublejosh

4
select * from table_name t1 inner join (select distinct <attribute list> from table_name as temp)t2 where t1.attribute_name = t2.attribute_name

สำหรับตารางของคุณมันจะเป็นสิ่งที่ชอบ

select * from list l1 inner join (select distinct address from list as list2)l2 where l1.address=l2.address

แบบสอบถามนี้จะให้รายการที่อยู่ที่แตกต่างทั้งหมดในตารางรายการของคุณ ... ฉันไม่แน่ใจว่าจะใช้งานได้อย่างไรถ้าคุณมีค่าคีย์หลักสำหรับชื่อ ฯลฯ


4

ขั้นตอนการสืบค้นลบที่ซ้ำกันเร็วที่สุด:

/* create temp table with one primary column id */
INSERT INTO temp(id) SELECT MIN(id) FROM list GROUP BY (isbn) HAVING COUNT(*)>1;
DELETE FROM list WHERE id IN (SELECT id FROM temp);
DELETE FROM temp;

2
สิ่งนี้ชัดเจนว่าจะลบเฉพาะระเบียนแรกจากรายการที่ซ้ำกันแต่ละกลุ่ม
Palec

4

แบบสอบถามนี้เป็นการส่วนตัวได้แก้ไขปัญหาของฉัน:

SELECT `SUB_ID`, COUNT(SRV_KW_ID) as subscriptions FROM `SUB_SUBSCR` group by SUB_ID, SRV_KW_ID HAVING subscriptions > 1;

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

นี่คือคอลัมน์ตาราง:

| SUB_SUBSCR_ID | int(11)     | NO   | PRI | NULL    | auto_increment |
| MSI_ALIAS     | varchar(64) | YES  | UNI | NULL    |                |
| SUB_ID        | int(11)     | NO   | MUL | NULL    |                |    
| SRV_KW_ID     | int(11)     | NO   | MUL | NULL    |                |

หวังว่ามันจะเป็นประโยชน์สำหรับคุณเช่นกัน!


3
SELECT t.*,(select count(*) from city as tt where tt.name=t.name) as count FROM `city` as t where (select count(*) from city as tt where tt.name=t.name) > 1 order by count desc

แทนที่เมืองด้วยตารางของคุณ แทนที่ชื่อด้วยชื่อฟิลด์ของคุณ



0
    Find duplicate Records:

    Suppose we have table : Student 
    student_id int
    student_name varchar
    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

    Now we want to see duplicate records
    Use this query:


   select student_name,student_id ,count(*) c from student group by student_id,student_name having c>1;

+--------------------+------------+---+
| student_name        | student_id | c |
+---------------------+------------+---+
| usman               |        101 | 3 |
| muhammadusmanyaqoob |        103 | 2 |
+---------------------+------------+---+

0

หากต้องการดูแถวที่ซ้ำกันอย่างรวดเร็วคุณสามารถเรียกใช้แบบสอบถามแบบง่าย ๆ ได้

ที่นี่ฉันกำลังสอบถามตารางและแสดงรายการแถวที่ซ้ำกันทั้งหมดด้วย user_id เดียวกัน, market_place และ sku:

select user_id, market_place,sku, count(id)as totals from sku_analytics group by user_id, market_place,sku having count(id)>1;

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

ตรวจสอบอีกครั้งก่อนว่าจะลบข้อมูลที่ถูกต้องหรือไม่ ที่นี่ฉันกำลังเลือกระเบียนในรายการที่ซ้ำกันซึ่งจะถูกลบ (โดย id เฉพาะ)

select a.user_id, a.market_place,a.sku from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

จากนั้นฉันเรียกใช้คิวรีการลบเพื่อลบสิ่งที่ซ้ำกัน:

delete a from sku_analytics a inner join sku_analytics b where a.id< b.id and a.user_id= b.user_id and a.market_place= b.market_place and a.sku = b.sku;

สำรองข้อมูล, ตรวจสอบอีกครั้ง, ตรวจสอบ, ยืนยันข้อมูลสำรองจากนั้นดำเนินการ


-1

select address from list where address = any (select address from (select address, count(id) cnt from list group by address having cnt > 1 ) as t1) order by address

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


-1

คำตอบที่ดีที่สุดคือPowerlordและฉันอยากจะแนะนำการเปลี่ยนแปลงอีกหนึ่งข้อ: ให้ใช้ LIMIT เพื่อให้แน่ใจว่า db จะไม่รับโหลดมากเกินไป:

SELECT firstname, lastname, list.address FROM list
INNER JOIN (SELECT address FROM list
GROUP BY address HAVING count(id) > 1) dup ON list.address = dup.address
LIMIT 10

มันเป็นนิสัยที่ดีที่จะใช้ LIMIT ถ้าไม่มีที่และเมื่อทำการเข้าร่วม เริ่มต้นด้วยค่าเล็ก ๆ น้อย ๆ ตรวจสอบว่าคิวรีนั้นหนักเพียงใดจากนั้นเพิ่มขีด จำกัด


สิ่งนี้มีส่วนช่วยอะไรได้บ้าง?
Kennet Celeste
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.