Temukan rekaman duplikat di MySQL

650

Saya ingin menarik catatan duplikat di Database MySQL. Ini dapat dilakukan dengan:

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

Yang mengakibatkan:

100 MAIN ST    2

Saya ingin menariknya sehingga memperlihatkan setiap baris yang merupakan duplikat. Sesuatu seperti:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

Adakah pemikiran tentang bagaimana hal ini dapat dilakukan? Saya mencoba menghindari melakukan yang pertama kemudian mencari duplikat dengan kueri kedua dalam kode.

Chris Bartow
sumber

Jawaban:

684

Kuncinya adalah menulis ulang kueri ini sehingga dapat digunakan sebagai subquery.

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;
Powerlord
sumber
69
Hati-hati dengan sub-kueri. Sub-pertanyaan sangat buruk untuk masalah kinerja. Jika ini perlu sering terjadi dan / atau dengan banyak catatan duplikat saya akan mempertimbangkan untuk memindahkan pemrosesan dari database dan ke dalam dataset.
bdwakefield
11
Ini adalah subquery yang tidak berkorelasi, jadi seharusnya tidak terlalu buruk dengan asumsi salah satu query saja tidak dirancang dengan buruk.
ʞɔıu
Menyenangkan. Kira ini adalah sytax sekitar "ERROR 1248 (42000): Setiap tabel turunan harus memiliki alias sendiri"
doublejosh
3
Ini adalah ide yang tepat, tetapi sekali lagi, seperti di bawah ini, ini hanya berfungsi jika alamat dijamin standar ...
Matt
30
+1 dengan kueri ini, Anda dapat menemukan duplikat tetapi juga rangkap tiga, empat kali lipat ..... dan seterusnya
albanx
352
SELECT date FROM logs group by date having count(*) >= 2
trt
sumber
5
Ini adalah permintaan kerja termudah untuk digunakan dengan Laravel. Baru saja menambahkan ->having(DB::raw('count(*)'), '>', 2)ke kueri. Terimakasih banyak!
Kovah
1
Berfungsi baik dengan 10 juta baris tabel. Ini harus menjadi jawaban terbaik
Terry Lin
13
Hati-hati dengan jawaban ini. Ini hanya mengembalikan satu dari duplikat. Jika Anda memiliki lebih dari 2 salinan dari catatan yang sama Anda tidak akan melihat semuanya, dan setelah menghapus catatan kembali Anda masih akan memiliki duplikat di tabel Anda.
Mikiko Jane
7
Mengapa >=2? Cukup gunakanHAVING COUNT(*) > 1
BadHorsie
2
@ JerryLin Menimbang bahwa ini tidak benar-benar menyelesaikan masalah yang dinyatakan sebelumnya (yang merupakan cara mengembalikan semua duplikat) Saya tidak setuju.
Michael
198

Mengapa tidak BATIN GABUNG saja dengan meja sendiri?

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

PERBEDAAN diperlukan jika alamat bisa ada lebih dari dua kali.

rudolfson
sumber
20
Saya juga menguji ini, dan itu hampir 6 kali lebih lambat dibandingkan dengan solusi yang diterima dalam situasi saya (MySQL terbaru, tabel 120.000 baris). Ini mungkin karena membutuhkan meja sementara, jalankan EXPLAIN pada keduanya untuk melihat perbedaannya.
4
Saya mengubah bagian terakhir dari kueri untuk WHERE a.id > b.idmemfilter duplikat yang lebih baru saja, dengan cara itu saya bisa melakukan DELETElangsung pada hasilnya. Ganti perbandingan untuk mendaftar duplikat yang lebih lama.
Stoffe
1
Ini membutuhkan waktu 50 detik untuk berjalan, jawaban @ doublejosh butuh 0,13 detik.
antonagestam
Saya harus menambahkan bahwa jawaban ini memberikan jawaban duplikat meskipun DI MANA jika dalam satu alamat tiga kali lipat, baris output digandakan. Jika empat kali lipat, saya yakin responsnya akan tiga kali lipat.
Wli
Saya menguji ini di leetcode " leetcode.com/problems/duplicate-emails ". Itu lebih cepat dibandingkan dengan sub-kueri.
mengepul
56

Saya mencoba jawaban terbaik yang dipilih untuk pertanyaan ini, tetapi agak membingungkan saya. Saya benar-benar membutuhkannya hanya pada satu bidang dari meja saya. Contoh berikut dari tautan ini bekerja sangat baik untuk saya:

SELECT COUNT(*) c,title FROM `data` GROUP BY title HAVING c > 1;
Arman Malik
sumber
Bekerja seperti pesona!
Vinícius
47
select `cityname` from `codcities` group by `cityname` having count(*)>=2

Ini adalah permintaan serupa yang Anda minta dan 200% berfungsi dan mudah juga. Nikmati!!!

pratswinz
sumber
37

Bukankah ini lebih mudah:

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

?

Tudor
sumber
1
bekerja untuk saya di mana saya harus memproses ~ 10 000 baris duplikat untuk membuatnya unik, jauh lebih cepat daripada memuat semua 600 000 baris.
adrianTNT
1
sangat mudah
Shwet
35

Temukan pengguna duplikat berdasarkan alamat email dengan kueri ini ...

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;
doublejosh
sumber
2
Untuk menemukan duplikat yang sebenarnya, Anda hanya perlu permintaan dalam. Ini jauh lebih cepat daripada jawaban yang lain.
antonagestam
20

kami dapat menemukan duplikat tergantung pada lebih dari satu bidang juga. Untuk kasus-kasus Anda dapat menggunakan format di bawah ini.

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

Menemukan alamat duplikat jauh lebih kompleks daripada yang terlihat, terutama jika Anda membutuhkan akurasi. Permintaan MySQL tidak cukup dalam hal ini ...

Saya bekerja di SmartyStreets , tempat kami melakukan validasi dan de-duplikasi dan hal-hal lainnya, dan saya telah melihat banyak tantangan beragam dengan masalah yang sama.

Ada beberapa layanan pihak ketiga yang akan menandai duplikat dalam daftar untuk Anda. Melakukan ini hanya dengan subquery MySQL tidak akan menjelaskan perbedaan dalam format dan standar alamat. USPS (untuk alamat AS) memiliki pedoman tertentu untuk membuat standar ini, tetapi hanya segelintir vendor yang disertifikasi untuk melakukan operasi tersebut.

Jadi, saya akan merekomendasikan jawaban terbaik bagi Anda adalah dengan mengekspor tabel ke file CSV, misalnya, dan mengirimkannya ke daftar prosesor yang mampu. Salah satunya adalah Alat Validasi Alamat Massal SmartyStreets yang akan menyelesaikannya dalam beberapa detik hingga beberapa menit secara otomatis. Ini akan menandai duplikat baris dengan bidang baru yang disebut "Gandakan" dan nilai Ydi dalamnya.

Mat
sumber
6
+1 untuk melihat kesulitan yang terlibat dalam pencocokan string alamat, meskipun Anda mungkin ingin menentukan bahwa pertanyaan "duplikat catatan" OP itu sendiri tidak rumit, tetapi ketika membandingkan alamat
cerita
13

Solusi lain adalah dengan menggunakan alias tabel, seperti:

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

Yang benar-benar Anda lakukan dalam kasus ini adalah mengambil tabel daftar asli , membuat dua tabel p retend - p 1 dan p 2 - dari itu, dan kemudian melakukan gabungan pada kolom alamat (baris 3). Baris ke-4 memastikan bahwa catatan yang sama tidak muncul beberapa kali di set hasil Anda ("duplikat duplikat").

kesederhanaan
sumber
1
Bagus sekali. Jika WHERE memeriksa dengan LIKE maka tanda kutip juga ditemukan. Membuat kueri lebih lambat, tetapi dalam kasus saya ini adalah satu kali.
gossi
10

Tidak akan menjadi sangat efisien, tetapi harus bekerja:

SELECT *
FROM list AS outer
WHERE (SELECT COUNT(*)
        FROM list AS inner
        WHERE inner.address = outer.address) > 1;
Chad Birch
sumber
10

Ini akan memilih duplikat dalam satu pass tabel, tanpa subquery.

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

Permintaan ini secara aktif mengemulasi ROW_NUMBER()hadir dalam OracledanSQL Server

Lihat artikel di blog saya untuk detail:

Quassnoi
sumber
20
Bukan untuk nitpick, tetapi FROM (SELECT ...) aooadalah subquery :-P
Rocket Hazmat
8

Ini juga akan menunjukkan kepada Anda berapa banyak duplikat yang memiliki dan akan memesan hasilnya tanpa bergabung

SELECT  `Language` , id, COUNT( id ) AS how_many
FROM  `languages` 
GROUP BY  `Language` 
HAVING how_many >=2
ORDER BY how_many DESC
Martin Tonev
sumber
sempurna karena masih mengatakan berapa banyak entri yang digandakan
denis
4
 SELECT firstname, lastname, address FROM list
 WHERE 
 Address in 
 (SELECT address FROM list
 GROUP BY address
 HAVING count(*) > 1)
Ryan Roper
sumber
Mencoba yang satu ini juga, tetapi tampaknya hanya menggantung. Percaya bahwa pengembalian dari kueri dalam tidak memenuhi format parameter IN.
doublejosh
Apa yang Anda maksud tidak memenuhi format parameter dalam? Semua yang diperlukan DI adalah bahwa subquery Anda harus mengembalikan satu kolom. Ini sangat sederhana. Lebih besar kemungkinan subquery Anda dihasilkan pada kolom yang tidak diindeks sehingga membutuhkan waktu yang sangat lama untuk dijalankan. Saya menyarankan jika perlu waktu lama untuk memecahnya menjadi dua pertanyaan. Ambil subquery, jalankan terlebih dahulu ke tabel sementara, buat indeks di atasnya kemudian jalankan kueri lengkap melakukan subquery tempat bidang duplikat Anda di tabel sementara.
Ryan Roper
Saya khawatir IN memerlukan daftar yang dipisahkan koma, bukan kolom, yang salah. Inilah permintaan yang berfungsi untuk saya: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

Untuk meja Anda itu akan menjadi sesuatu seperti

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

Kueri ini akan memberi Anda semua entri alamat yang berbeda di tabel daftar Anda ... Saya tidak yakin bagaimana ini akan bekerja jika Anda memiliki nilai kunci utama untuk nama, dll.

Neha Patil
sumber
4

Prosedur kueri penghapusan duplikat tercepat:

/* 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;
venkatesh
sumber
2
Ini jelas hanya menghapus catatan pertama dari setiap kelompok duplikat.
Palec
4

Secara pribadi kueri ini telah menyelesaikan masalah saya:

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

Apa yang dilakukan skrip ini adalah menampilkan semua ID pelanggan yang ada lebih dari satu kali ke dalam tabel dan jumlah duplikat yang ditemukan.

Ini adalah kolom tabel:

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

Semoga bermanfaat bagi Anda juga!

Ionut Petre
sumber
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

Ganti kota dengan Meja Anda. Ganti nama dengan nama bidang Anda

Lalit Patel
sumber
2
    SELECT *
    FROM (SELECT  address, COUNT(id) AS cnt
    FROM list
    GROUP BY address
    HAVING ( COUNT(id) > 1 ))
DJ.
sumber
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 |
+---------------------+------------+---+
Usman Yaqoob
sumber
0

Untuk melihat baris duplikat dengan cepat, Anda dapat menjalankan satu permintaan sederhana

Di sini saya menanyakan tabel dan mencantumkan semua baris duplikat dengan user_id, market_place, dan sku yang sama:

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

Untuk menghapus baris duplikat Anda harus memutuskan baris mana yang ingin Anda hapus. Misalnya yang memiliki id lebih rendah (biasanya lebih tua) atau mungkin beberapa informasi tanggal lainnya. Dalam kasus saya, saya hanya ingin menghapus id yang lebih rendah karena id yang lebih baru adalah informasi terbaru.

Pertama periksa apakah catatan yang tepat akan dihapus. Di sini saya memilih catatan di antara duplikat yang akan dihapus (oleh id unik).

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;

Lalu saya menjalankan permintaan hapus untuk menghapus dupes:

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;

Cadangan, Periksa ulang, verifikasi, verifikasi pencadangan kemudian jalankan.

Ganesh Krishnan
sumber
-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

sub-kueri dalam mengembalikan baris dengan alamat duplikat kemudian sub-kueri luar mengembalikan kolom alamat untuk alamat dengan duplikat. sub-kueri terluar harus mengembalikan hanya satu kolom karena digunakan sebagai operan untuk operator '= apa saja'

aad
sumber
-1

Jawaban Powerlord memang yang terbaik dan saya akan merekomendasikan satu perubahan lagi: gunakan LIMIT untuk memastikan db tidak akan kelebihan beban:

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

Merupakan kebiasaan yang baik untuk menggunakan LIMIT jika tidak ada DIMANA dan saat membuat sambungan. Mulai dengan nilai kecil, periksa seberapa berat kueri, lalu tambah batas.

Michał Maluga
sumber
bagaimana ini berkontribusi apa pun untuk apa pun?
Kennet Celeste