MySQL'de yinelenen kayıtları bulma


650

MySQL Veritabanındaki yinelenen kayıtları çıkarmak istiyorum. Bu, aşağıdakilerle yapılabilir:

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

Sonuç:

100 MAIN ST    2

Yinelenen her satırı gösterir şekilde çekmek istiyorum. Gibi bir şey:

JIM    JONES    100 MAIN ST
JOHN   SMITH    100 MAIN ST

Bunun nasıl yapılabileceğine dair düşünceleriniz var mı? İlkini yapmaktan sonra kodda ikinci bir sorgu ile kopyaları ararken kaçınmaya çalışıyorum.

Yanıtlar:


684

Anahtar, bu sorguyu alt sorgu olarak kullanılabilmesi için yeniden yazmaktır.

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
Alt sorgular konusunda dikkatli olun. Alt sorgular performans endişeleri için gülünç derecede kötü olabilir. Bunun sık sık ve / veya çok sayıda yinelenen kayıt olması gerekiyorsa, işlemeyi veritabanının dışına ve bir veri kümesine taşımayı düşünürüm.
bdwakefield

11
İlişkisiz bir alt sorgu olduğundan, her iki sorgunun da kötü tasarlanmadığını varsayarak çok kötü olmamalıyız.
Mayıu

Güzel. Sanırım bu "ERROR 1248 (42000): Türetilmiş her tablonun kendi takma adı olmalıdır"
etrafındaki sytax

3
Bu doğru fikir, ama yine, aşağıdaki gibi, bu sadece adreslerin standartlaştırılması garanti edilirse işe yarar ...
Matt

30
+ 1 bu sorguyla aynı kopyaları bulabilirsiniz, aynı zamanda üçlü, dörtlü ..... ve benzeri
albanx

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

5
Laravel ile kullanmak için en kolay çalışma sorgusuydu. Sadece ->having(DB::raw('count(*)'), '>', 2)sorguya eklemek zorunda kaldım . Çok teşekkürler!
Kovah

1
10 milyon satırlık tablo ile iyi çalışır. Bu en iyi cevap olmalı
Terry Lin

13
Bu yanıta dikkat edin. Yinelenenlerden yalnızca birini döndürür. Aynı kaydın 2'den fazla kopyası varsa, hepsini görmezsiniz ve döndürülen kaydı sildikten sonra tablonuzda yine de kopyalar olur.
Mikiko Jane

7
Neden >=2? Sadece kullanınHAVING COUNT(*) > 1
BadHorsie

2
@TerryLin Aslında aslında belirtilen sorunu çözmediğini düşünerek (bu tüm kopyaları nasıl geri dönecekti) katılmıyorum.
Michael

198

Neden sadece INNER masaya kendisi katılmıyor?

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

Adres iki kereden fazla mevcut olabiliyorsa bir DISTINCT gereklidir.


20
Bunu da test ettim ve durumumdaki kabul edilen çözümle karşılaştırıldığında neredeyse 6 kat daha yavaştı (en son MySQL, 120.000 satırlık tablo). Bunun nedeni geçici bir tablo gerektirmesi olabilir, farklılıkları görmek için her ikisinde de bir EXPLAIN çalıştırın.

4
Sorgunun son bölümünü WHERE a.id > b.idyalnızca yeni kopyaları filtrelemek için değiştirdim, bu şekilde DELETEdoğrudan sonuç üzerinde yapabilirim . Eski kopyaları listelemek için karşılaştırmayı değiştirin.
Stoffe

1
Bu, çalıştırmak için 50 saniye sürdü, @ doublejosh'ın yanıtı .13 saniye sürdü.
antonagestam

Bu cevabın, bir adresin üç katına çıkması durumunda çıkış satırlarının iki katına çıkmasına rağmen NEREDE rağmen yinelenen cevaplar verdiğini eklemeliyim. Dörtlü ise, cevabın üç katına çıkacağına inanıyorum.
Wli

Bunu leetcode " leetcode.com/problems/duplicate-emails " adresinde test ettim . Alt sorguya göre daha hızlıydı.
dalgalı

56

Bu soru için seçilen en iyi yanıtı denedim, ama bu beni biraz karıştırdı. Aslında bunu masamdan tek bir alana ihtiyacım vardı. Bu bağlantıdan aşağıdaki örnek benim için çok iyi çalıştı:

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

Tıkır tıkır çalışıyor!
Vinícius

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

Bu sormak istediğiniz benzer sorgu ve onun% 200 çalışma ve çok kolay. Zevk almak!!!


37

Bu daha kolay değil mi:

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

?


1
benim için çalıştı sadece 600.000 satır yük daha çok daha hızlı, benzersiz yapmak için ~ 10.000 yinelenen satırları işlemek zorunda kaldı.
adrianTNT

1
çok daha kolay
Shwet

35

Bu sorgu ile e-posta adresine göre yinelenen kullanıcıları bulun ...

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
Gerçek kopyayı bulmak için sadece dahili sorguyu kullanmanız gerekir. Bu diğer cevaplardan çok daha hızlı.
antonagestam

20

yinelemelerin birden fazla alana bağlı olduğunu da bulabiliriz.Bu durumlar için aşağıdaki biçimi kullanabilirsiniz.

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

16

Yinelenen adresleri bulmak , özellikle doğruluk istiyorsanız, göründüğünden daha karmaşıktır. Bu durumda MySQL sorgusu yeterli değil ...

Çalıştığım SmartyStreets biz adres doğrulama ve tekilleştirme ve diğer şeyler yapmak, ve ben benzer sorunları olan çeşitli sorunlar çok gördüm.

Sizin için bir listede kopyaları işaretleyecek birkaç üçüncü taraf hizmeti vardır. Bunu yalnızca bir MySQL alt sorgusu ile yapmak, adres formatları ve standartlarındaki farklılıkları hesaba katmaz. USPS (ABD adresi için) bu standardı yapmak için belirli kurallara sahiptir, ancak bu işlemleri gerçekleştirmek için yalnızca birkaç satıcı onaylanmıştır.

Bu nedenle, sizin için en iyi yanıtı, örneğin bir CSV dosyasına dışa aktarmak ve bunu yetenekli bir liste işlemcisine göndermeniz önerilir. Bunlardan biri, birkaç saniye ila birkaç dakika içinde sizin için otomatik olarak yapacak olan SmartyStreets Toplu Adres Doğrulama Aracıdır . Yinelenen satırları, "Çoğalt" adlı yeni bir alan ve içindeki bir değerle işaretler Y.


6
Adres dizelerini eşleştirme zorluğunu görmek için +1, ancak OP'nin "yinelenen kayıtlar" sorununun kendi başına karmaşık olmadığını belirtmek isteyebilirsiniz, ancak adresleri karşılaştırırken
hikaye

13

Başka bir çözüm, tablo takma adlarını kullanmaktır, şöyle:

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

Bu durumda gerçekten yaptığınız tek şey orijinal liste tablosunu almak, bundan iki p retend tablosu oluşturmak - p 1 ve p 2 - ve sonra adres sütununda birleştirme yapmak (satır 3). 4. satır, aynı kaydın sonuç kümenizde birden fazla gösterilmemesini sağlar ("yinelenen kopyalar").


1
Güzel çalışıyor. NEREDE GİBİ kontrol ediyorsa, kesme işaretleri de bulunur. Sorguyu yavaşlatır, ancak benim durumumda bir zamanlayıcıdır.
gossi

10

Çok verimli olmayacak, ancak işe yarayacak:

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

10

Bu, bir tablo geçişinde yinelenenleri seçecektir, alt sorgu içermez.

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

Bu sorgu aktif ROW_NUMBER()olarak OracleveSQL Server

Ayrıntılar için blogumdaki makaleye bakın:


20
Nitpick için değil, ama FROM (SELECT ...) aoobir alt sorgu :-P
Rocket Hazmat

8

Bu aynı zamanda kaç kopyaya sahip olduğunu gösterir ve sonuçları birleştirmeden sipariş eder

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

mükemmel çünkü hala kaç girişin kopyalandığını söylüyor
denis

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

Bunu da denedim, ama sadece asılmış gibi görünüyor. İç sorgudan döndürmenin IN parametresi biçimini karşılamadığına inan.
doublejosh

Ne demek parametre biçiminde tatmin değil? Tüm IN gereksinimleri, alt sorgunuzun tek bir sütun döndürmesi gerektiğidir. Gerçekten çok basit. Alt sorgunuzun dizine eklenmemiş bir sütunda oluşturulma olasılığı daha yüksektir, bu nedenle çalıştırılması çok uzun zaman alır. Bunu iki sorguya ayırmak uzun zaman alıyorsa öneririm. Alt sorguyu alın, önce geçici bir tabloya çalıştırın, bir dizin oluşturun, sonra tam sorguyu geçici tablodaki yinelenen alanınızın bulunduğu alt sorguyu çalıştırın.
Ryan Roper

Ben sadece yanlış bir sütun yerine virgülle ayrılmış bir liste gerekli endişeliydi. İşte benim için çalışan sorgu: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

Masanız için böyle bir şey olurdu

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

Bu sorgu, liste tablonuzdaki tüm farklı adres girdilerini verecektir ... Ad, vb. İçin herhangi bir birincil anahtar değerleriniz varsa, bunun nasıl çalışacağından emin değilim.


4

En hızlı yinelenen kaldırma sorguları prosedürü:

/* 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
Bu açıkça her kopya grubundan sadece ilk kaydı siler.
Palec

4

Şahsen bu sorgu benim sorunumu çözdü:

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

Bu komut dosyasının yaptığı, tabloya bir kereden fazla var olan tüm abone kimliklerini ve bulunan kopya sayısını gösterir.

Tablo sütunları şunlardır:

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

Umarım sizin için de yararlı olacaktır!


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

Şehri Masanızla değiştirin . Adı alan adınızla değiştirin


2
    SELECT *
    FROM (SELECT  address, COUNT(id) AS cnt
    FROM list
    GROUP BY address
    HAVING ( COUNT(id) > 1 ))

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

Yinelenen satırları hızlı bir şekilde görmek için tek bir basit sorgu çalıştırabilirsiniz

Burada tablo sorgulama ve aynı user_id, market_place ve sku ile tüm yinelenen satırları listeleme:

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

Yinelenen satırı silmek için hangi satırı silmek istediğinize karar vermeniz gerekir. Örneğin, daha düşük kimliğe (genellikle daha eski) veya başka bir tarih bilgisine sahip olan. Benim durumumda, daha yeni kimlik son bilgiler olduğu için sadece alt kimliği silmek istiyorum.

Önce doğru kayıtların silinip silinmeyeceğini iki kez kontrol edin. Burada silinecek yinelenen kayıtları arasında (benzersiz kimliği ile) seçiyorum.

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;

Sonra dupes silmek için silme sorgusu çalıştırın:

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;

Yedekleme, İki kez kontrol edin, doğrulayın, yedeklemeyi doğrulayın ve çalıştırın.


-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

iç alt sorgu yinelenen adres içeren satırları, ardından dış alt sorgu yinelenen adresler için adres sütununu döndürür. dış alt sorgu yalnızca bir sütun döndürmelidir, çünkü '= any' işleci için işlenen olarak kullanılır


-1

Powerlord cevap gerçekten en iyisidir ve bir değişiklik daha tavsiye ederim: db'nin aşırı yüklenmeyeceğinden emin olmak için LIMIT kullanın:

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

NEREDE yoksa ve birleştirme yaparken LIMIT kullanmak iyi bir alışkanlıktır. Küçük bir değerle başlayın, sorgunun ne kadar ağır olduğunu kontrol edin ve ardından sınırı artırın.


bu her şeye nasıl bir katkıda bulunur?
Kennet Celeste
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.