Menemukan nilai duplikat di MySQL

769

Saya memiliki tabel dengan kolom varchar, dan saya ingin menemukan semua catatan yang memiliki nilai duplikat di kolom ini. Apa permintaan terbaik yang bisa saya gunakan untuk menemukan duplikat?

Jon Tackabury
sumber
1
Karena Anda menyebutkan menemukan semua catatan, saya berasumsi Anda perlu mengetahui tombol-tombol dan juga nilai-nilai duplikat di kolom varchar itu.
TechTravelThink
Saya dapat menemukan kunci cukup mudah setelah saya mendapatkan nilai-nilai, saya benar-benar hanya ingin daftar semua nilai duplikat.
Jon Tackabury

Jawaban:

1522

Lakukan SELECTdengan GROUP BYklausa. Katakanlah nama adalah kolom yang ingin Anda temukan duplikatnya:

SELECT name, COUNT(*) c FROM table GROUP BY name HAVING c > 1;

Ini akan mengembalikan hasil dengan nilai nama di kolom pertama, dan hitungan berapa kali nilai itu muncul di kolom kedua.

levik
sumber
27
Tetapi bagaimana ini berguna jika Anda tidak bisa mendapatkan ID dari baris dengan nilai duplikat? Ya, Anda dapat melakukan pencocokan kueri baru untuk setiap nilai duplikat, tetapi apakah mungkin untuk mendaftar duplikat saja?
NobleUplift
23
@NobleUplift Anda dapat melakukan GROUP_CONCAT(id)dan itu akan mencantumkan ID. Lihat jawaban saya sebagai contoh.
Matt Rardon
5
Apa artinya jika dikatakan ERROR: column "c" does not exist LINE 1?
Pengguna
15
Saya bingung mengapa ini adalah jawaban yang diterima dan mengapa ada begitu banyak upvotes. OP bertanya, "Saya ingin menemukan semua catatan yang memiliki nilai duplikat di kolom ini." Jawaban ini mengembalikan daftar jumlah. -1
Monica Heddneck
4
Bagi mereka yang tidak mengerti bagaimana HAVING bekerja - itu hanyalah sebuah filter pada set hasil, jadi terjadi setelah permintaan utama.
John Hunt
236
SELECT varchar_col
FROM table
GROUP BY varchar_col
HAVING COUNT(*) > 1;
maxyfc
sumber
10
Lebih unggul daripada jawaban @ levik karena tidak menambahkan kolom tambahan. Berguna untuk digunakan dengan IN()/ NOT IN().
wmassingham
172
SELECT  *
FROM    mytable mto
WHERE   EXISTS
        (
        SELECT  1
        FROM    mytable mti
        WHERE   mti.varchar_column = mto.varchar_column
        LIMIT 1, 1
        )

Kueri ini mengembalikan catatan lengkap, bukan hanya berbeda varchar_column.

Kueri ini tidak digunakan COUNT(*). Jika ada banyak duplikat, COUNT(*)mahal, dan Anda tidak perlu keseluruhan COUNT(*), Anda hanya perlu tahu apakah ada dua baris dengan nilai yang sama.

Memiliki indeks atas varchar_columnkehendak, tentu saja, sangat mempercepat permintaan ini.

Quassnoi
sumber
3
Baik sekali. Saya menambahkan ORDER BY varchar_column DESCke akhir permintaan.
trante
8
Ini harus menjadi jawaban yang diterima, karena GROUP BYdan HAVINGhanya mengembalikan satu dari kemungkinan duplikat. Juga, kinerja dengan bidang yang diindeks alih-alih COUNT(*), dan kemungkinan ORDER BYuntuk mengelompokkan rekaman duplikat.
Rémi Breton
1
Seperti yang dinyatakan dalam komentar di atas, kueri ini memungkinkan Anda untuk membuat daftar semua baris yang digandakan. Sangat berguna.
TryHarder
4
Melihat ini, saya tidak mengerti bagaimana cara kerjanya sama sekali. Tidakkah kondisi dalam selalu benar karena setiap baris di tabel luar juga akan tersedia di tabel dalam dan jadi setiap baris akan selalu setidaknya cocok dengan dirinya sendiri? Saya mencoba kueri dan mendapatkan hasil yang saya duga - setiap baris dikembalikan. Tetapi dengan begitu banyak upvotes saya meragukan diri saya sendiri. Bukankah permintaan dalam kehilangan sesuatu seperti "AND mto.id <> mti.id"? Ini bekerja untuk saya ketika saya menambahkannya.
Clox
2
@ Quassnoi Baiklah. Saya sudah mencoba meletakkannya di sqlfiddle tapi saya sudah menyerah karena setiap permintaan yang saya coba jalankan, selain membuat skema, waktunya akan habis. Saya mengetahui bahwa menghapus "EXISTS" juga membuat kueri berfungsi dengan benar untuk saya.
Clox
144

Membangun jawaban levik untuk mendapatkan ID dari duplikat baris yang dapat Anda lakukan GROUP_CONCATjika server Anda mendukungnya (ini akan mengembalikan daftar id yang dipisahkan koma).

SELECT GROUP_CONCAT(id), name, COUNT(*) c FROM documents GROUP BY name HAVING c > 1;
Matt Rardon
sumber
12
Selama ini tanpa mengetahui tentang GROUP_CONCAT ()! sangat bermanfaat sekali.
Aesede
Benar-benar menghargai Matt. Ini sangat membantu! Bagi mereka yang mencoba memperbarui di phpmyadmin jika Anda membiarkan id bersama dengan fungsi seperti ini: SELECT id, GROUP_CONCAT(id), name, COUNT(*) c [...]ini memungkinkan pengeditan sisip dan harus memperbarui semua baris yang terlibat (atau setidaknya yang pertama cocok), tetapi sayangnya hasil edit menghasilkan kesalahan Javascript. ..
Armfoot
Lalu bagaimana Anda menghitung berapa id yang dapat digandakan?
CMCDragonkai
2
Bagaimana saya tidak mendapatkan semua ID yang dikelompokkan, tetapi terdaftar dari pertama hingga terakhir; dengan semua nilai masing-masing di kolom di sebelahnya? Jadi alih-alih mengelompokkannya, itu hanya menunjukkan ID 1 dan nilainya, ID 2 dan nilainya. BAHKAN jika nilai untuk ID adalah sama.
MailBlade
1
Jawaban yang sangat membantu, ini harus atas sehingga lebih banyak orang melihatnya. Saya ingat betapa sakitnya saya membuat daftar seperti itu, dan itu tersedia sepanjang waktu sebagai perintah ..
John
13

Dengan asumsi tabel Anda bernama TableABC dan kolom yang Anda inginkan adalah Kol dan kunci utama untuk T1 adalah Kunci.

SELECT a.Key, b.Key, a.Col 
FROM TableABC a, TableABC b
WHERE a.Col = b.Col 
AND a.Key <> b.Key

Keuntungan dari pendekatan ini daripada jawaban di atas adalah memberikan Kunci.

TechTravelThink
sumber
4
+1 Karena berguna. Meskipun, ironisnya, hasilnya sendiri mengandung duplikat (daftar a dan b, lalu b dan a.)
Fabien Snauwaert
2
@FabienSnauwaert Anda dapat menyingkirkan beberapa duplikat dengan membandingkan kurang dari (atau lebih besar dari)
Michael
@ TechTravel. Pikirkan jawaban Anda sangat jelas, terima kasih untuk itu, tetapi pada tabel besar dibutuhkan beberapa saat (sekitar 2 juta pada lebih dari 20.000 entri tabel) dan setelah menunjukkan 25 hasil pertama, jika saya klik untuk menampilkan yang berikutnya, kesalahan acara phpmyadmin "# 1052 - Kolom 'id' dalam klausa agar tidak jelas "
bcag2
12
SELECT * 
FROM `dps` 
WHERE pid IN (SELECT pid FROM `dps` GROUP BY pid HAVING COUNT(pid)>1)
strustam
sumber
1
Tidak, karena ini sangat mungkin yang paling lambat. Subselect terkenal lambat, karena dieksekusi untuk setiap baris yang dikembalikan.
Oddman
10

Untuk menemukan berapa banyak rekaman duplikat di kolom nama di Karyawan, pertanyaan di bawah ini sangat membantu;

Select name from employee group by name having count(*)>1;
pengguna5599549
sumber
10

untuk mendapatkan semua data yang mengandung duplikasi saya menggunakan ini:

SELECT * FROM TableName INNER JOIN(
  SELECT DupliactedData FROM TableName GROUP BY DupliactedData HAVING COUNT(DupliactedData) > 1 order by DupliactedData)
  temp ON TableName.DupliactedData = temp.DupliactedData;

TableName = tabel yang Anda kerjakan.

DupliactedData = data duplikat yang Anda cari.

udi
sumber
Yang ini menunjukkan setiap duplikat di baris itu sendiri. Itu yang saya butuhkan. Terima kasih.
warmwhisky
8

Kueri terakhir saya memasukkan beberapa jawaban di sini yang membantu - menggabungkan grup dengan, menghitung & GROUP_CONCAT.

SELECT GROUP_CONCAT(id), `magento_simple`, COUNT(*) c 
FROM product_variant 
GROUP BY `magento_simple` HAVING c > 1;

Ini memberikan id dari kedua contoh (dipisahkan koma), barcode yang saya butuhkan, dan berapa banyak duplikat.

Ubah tabel dan kolom sesuai kebutuhan.

Jonathan
sumber
8

Saya tidak melihat pendekatan GABUNG, yang memiliki banyak kegunaan dalam hal duplikat.

Pendekatan ini memberi Anda hasil berlipat ganda yang sebenarnya.

SELECT t1.* FROM my_table as t1 
LEFT JOIN my_table as t2 
ON t1.name=t2.name and t1.id!=t2.id 
WHERE t2.id IS NOT NULL 
ORDER BY t1.name
Adam Fischer
sumber
2
FYI - Anda akan ingin 'memilih somecol berbeda ..' jika ada potensi untuk lebih dari 1 rekaman duplikat jika tidak, hasilnya akan berisi duplikat dari baris duplikat yang ditemukan.
Drew
7
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
7

Mengambil jawaban @ maxyfc lebih lanjut, saya perlu menemukan semua baris yang dikembalikan dengan nilai duplikat, sehingga saya bisa mengeditnya di MySQL Workbench :

SELECT * FROM table
   WHERE field IN (
     SELECT field FROM table GROUP BY field HAVING count(*) > 1
   ) ORDER BY field
AbsoluteƵERØ
sumber
6

Saya melihat hasil di atas dan permintaan akan berfungsi dengan baik jika Anda perlu memeriksa nilai kolom tunggal yang merupakan duplikat. Misalnya email.

Tetapi jika Anda perlu memeriksa dengan lebih banyak kolom dan ingin memeriksa kombinasi hasilnya, maka kueri ini akan berfungsi dengan baik:

SELECT COUNT(CONCAT(name,email)) AS tot,
       name,
       email
FROM users
GROUP BY CONCAT(name,email)
HAVING tot>1 (This query will SHOW the USER list which ARE greater THAN 1
              AND also COUNT)
davejal
sumber
Persis apa yang dibutuhkan! Di sini pertanyaan saya, memeriksa 3 bidang untuk duplikat:SELECT COUNT(CONCAT(userid,event,datetime)) AS total, userid, event, datetime FROM mytable GROUP BY CONCAT(userid, event, datetime ) HAVING total>1
Kai Noack
4

Saya lebih suka menggunakan fungsi berjendela (MySQL 8.0+) untuk menemukan duplikat karena saya bisa melihat seluruh baris:

WITH cte AS (
  SELECT *
    ,COUNT(*) OVER(PARTITION BY col_name) AS num_of_duplicates_group
    ,ROW_NUMBER() OVER(PARTITION BY col_name ORDER BY col_name2) AS pos_in_group
  FROM table
)
SELECT *
FROM cte
WHERE num_of_duplicates_group > 1;

DB Fiddle Demo

Lukasz Szozda
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
magesh
sumber
1
Melakukan subquery yang sama dua kali tampaknya tidak efisien.
NobleUplift
2
SELECT DISTINCT a.email FROM `users` a LEFT JOIN `users` b ON a.email = b.email WHERE a.id != b.id;
Pawel Furmaniak
sumber
1
Patut dicatat bahwa ini lambat sekali atau bahkan mungkin tidak selesai jika kolom yang ditanyakan tidak diindeks. Kalau tidak, saya bisa mengubah a.emailke a.*dan mendapatkan semua ID dari baris dengan duplikat.
NobleUplift
@NobleUplift Apa yang kamu bicarakan?
Michael
@Michael Yah karena ini adalah tiga tahun saya tidak dapat menguji pada versi MySQL apa pun yang saya gunakan, tapi saya mencoba permintaan yang sama pada database di mana kolom yang saya pilih tidak memiliki indeks di atasnya, jadi butuh cukup banyak beberapa detik untuk menyelesaikan. Mengubahnya untuk SELECT DISTINCT a.*diselesaikan hampir secara instan.
NobleUplift
@NobleUplift Ah ok. Saya dapat memahaminya karena lambat ... bagian yang saya khawatirkan adalah "bahkan mungkin tidak selesai".
Michael
@Michael Saya tidak ingat tabel mana dalam sistem kami, saya harus menjalankan kueri ini, tetapi untuk yang dengan beberapa juta catatan, mereka mungkin sudah selesai, tetapi dalam waktu yang begitu lama sehingga saya menyerah untuk melihat kapan sebenarnya akan selesai.
NobleUplift
1

Untuk menghapus duplikat baris dengan beberapa bidang, pertama-tama batal mereka ke kunci unik baru yang ditentukan untuk satu-satunya baris yang berbeda, kemudian gunakan perintah "grup dengan" untuk menghapus baris duplikat dengan kunci unik baru yang sama:

Create TEMPORARY table tmp select concat(f1,f2) as cfs,t1.* from mytable as t1;
Create index x_tmp_cfs on tmp(cfs);
Create table unduptable select f1,f2,... from tmp group by cfs;
pertama
sumber
dapatkah Anda menambahkan penjelasan?
Robert
Kenapa tidak digunakan CREATE TEMPORARY TABLE ...? Sedikit penjelasan tentang solusi Anda akan sangat bagus.
maxhb
1

Satu kontribusi yang sangat terlambat ... kalau-kalau ada orang yang membantu ... Saya punya tugas untuk menemukan pasangan transaksi yang cocok (sebenarnya kedua sisi transfer antar-akun) dalam aplikasi perbankan, untuk mengidentifikasi mana yang adalah 'dari' dan 'ke' untuk setiap transaksi antar rekening, jadi kami berakhir dengan ini:

SELECT 
    LEAST(primaryid, secondaryid) AS transactionid1,
    GREATEST(primaryid, secondaryid) AS transactionid2
FROM (
    SELECT table1.transactionid AS primaryid, 
        table2.transactionid AS secondaryid
    FROM financial_transactions table1
    INNER JOIN financial_transactions table2 
    ON table1.accountid = table2.accountid
    AND table1.transactionid <> table2.transactionid 
    AND table1.transactiondate = table2.transactiondate
    AND table1.sourceref = table2.destinationref
    AND table1.amount = (0 - table2.amount)
) AS DuplicateResultsTable
GROUP BY transactionid1
ORDER BY transactionid1;

Hasilnya adalah bahwa DuplicateResultsTablemenyediakan baris yang berisi transaksi yang cocok (yaitu duplikat), tetapi juga memberikan id transaksi yang sama secara terbalik saat kedua cocok dengan pasangan yang sama, sehingga bagian luar SELECTada untuk dikelompokkan berdasarkan ID transaksi pertama, yang dilakukan dengan menggunakan LEASTdan GREATESTmemastikan kedua transaksi itu selalu dalam urutan yang sama dalam hasil, yang membuatnya aman untuk GROUPyang pertama, sehingga menghilangkan semua kecocokan duplikat. Telusuri hampir satu juta catatan dan identifikasi 12.000+ pertandingan hanya dalam waktu kurang dari 2 detik. Tentu saja transactionid adalah indeks utama, yang sangat membantu.

fortyninthnet
sumber
1
Select column_name, column_name1,column_name2, count(1) as temp from table_name group by column_name having temp > 1
Vipin Jain
sumber
1
SELECT ColumnA, COUNT( * )
FROM Table
GROUP BY ColumnA
HAVING COUNT( * ) > 1
Scott Ferguson
sumber
3
Ini tidak benar karena juga menemukan kejadian unik. 0 harus 1.
Kafoso
1

Jika Anda ingin menghapus penggunaan duplikat DISTINCT

Kalau tidak gunakan pertanyaan ini:

SELECT users.*,COUNT(user_ID) as user FROM users GROUP BY user_name HAVING user > 1;

Hassan Latif Butt
sumber
0

Coba gunakan permintaan ini:

SELECT name, COUNT(*) value_count FROM company_master GROUP BY name HAVING value_count > 1;
Atul Akabari
sumber