PostgreSQL'de yinelenen kayıtlar nasıl bulunur


190

Şu anda aşağıdaki yinelenen alanlara izin veren "user_links" adlı bir PostgreSQL veritabanı tablo var:

year, user_id, sid, cid

Benzersiz kısıt ancak şimdi emin olmak için bir kısıtlama eklemek için arıyorum, şu anda "id" denilen ilk alandır year, user_id, sidve cidtüm benzersiz ama yinelenen değerler zaten bu kısıtlamayı ihlal hangi mevcut olduğundan ben kısıtlamasını uygulayamazsınız.

Tüm kopyaları bulmanın bir yolu var mı?


Yanıtlar:


335

Temel fikir, sayı toplama ile iç içe bir sorgu kullanmak olacaktır:

select * from yourTable ou
where (select count(*) from yourTable inr
where inr.sid = ou.sid) > 1

Aramayı daraltmak için iç sorgudaki where yan tümcesini ayarlayabilirsiniz.


Yorumlarda bahsedilenler için iyi bir çözüm var (ancak herkes bunları okumuyor):

select Column1, Column2, count(*)
from yourTable
group by Column1, Column2
HAVING count(*) > 1

Veya daha kısa:

SELECT (yourTable.*)::text, count(*)
FROM yourTable
GROUP BY yourTable.*
HAVING count(*) > 1

65
select co1, col2, count(*) from tbl group by col1, col2 HAVING count(*)>1
HAVING'i

1
@Alexkovelsky sayesinde bu ifadeyi değiştirmek benim için daha kolay oldu ve daha hızlı koştu. Daha yüksek görünürlük için onunla bir cevap öneriyorum.
Vesanto

Bu seçenekler bana çalıştı, diğerleri sonuçları gruplandırır ve bu seçenekler bana sadece çoğaltılan kayıt yerine tüm çoğaltılan kayıtları verdi, teşekkürler!
rome3ro

1
Bu cevabınız biraz yavaş olmalı. Bir masada 10
bin

1
işte reçel burası kardeşim. kahretsin evet. Teşekkürler. 💯
dps

91

" PostgreSQL ile yinelenen satırları bulun " dan akıllı çözüm:

select * from (
  SELECT id,
  ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id asc) AS Row
  FROM tbl
) dups
where 
dups.Row > 1

11
Bu hızlı! Milyonlarca satırın ikinci bir bölümünde çalıştı. Diğer cevaplar sadece orada asılı ...
dmvianna

5
Gördüğüm kadarıyla, bu sorgu bir gruptaki tüm satırları dikkate almaz. Sadece bir şeyin kopyalarını gösterir, kopyaların bir kısmı rownum = 1 ile olacaktır. Beni düzeltin
Vladimir Filipchenko

9
@vladimir Filipchenko Tüm çizgilere sahip olmak için Alexkovelsky çözümüne bir seviye ekleyin:SELECT * FROM ( SELECT *, LEAD(row,1) OVER () AS nextrow FROM ( SELECT *, ROW_NUMBER() OVER(w) AS row FROM tbl WINDOW w AS (PARTITION BY col1, col2 ORDER BY col3) ) x ) y WHERE row > 1 OR nextrow > 1;
Le Droid

4
@VladimirFilipchenko Hemen yerini ROW_NUMBER()ile COUNT(*)ve ekleme rows between unbounded preceding and unbounded followingsonrasıORDER BY id asc
alexkovelsky

2
bulduğum diğer çözümlerden çok daha iyi. ayrıca çiftleri DELETE ...USINGve bazı küçük düzeltmeleri silmek için de eşit derecede iyi çalışır
Brandon

6

Çoğaltılacak alanlarda aynı tabloya katılabilir ve ardından id alanında anti-join yapabilirsiniz. İlk tablo diğer adından (tn1) id alanını seçin ve ardından ikinci tablo diğer adının id alanındaki array_agg işlevini kullanın. Son olarak, array_agg işlevinin düzgün çalışması için sonuçları tn1.id alanına göre gruplandırırsınız. Bu, bir kaydın kimliğini ve birleştirme koşullarına uyan tüm kimliklerin bir dizisini içeren bir sonuç kümesi oluşturur.

select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id;

Açıkçası, bir kimlik için duplicate_entries dizisinde yer alacak id'lerin sonuç kümesinde kendi girişleri de olacaktır. Hangi sonucun 'gerçeğin' kaynağı olmasını istediğinize karar vermek için bu sonuç kümesini kullanmanız gerekecektir. Silinmemesi gereken tek kayıt. Belki böyle bir şey yapabilirsiniz:

with dupe_set as (
select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id
order by tn1.id asc)
select ds.id from dupe_set ds where not exists 
 (select de from unnest(ds.duplicate_entries) as de where de < ds.id)

Kopyaları olan en düşük sayı ID'lerini seçer (ID'nin PK'da arttığı varsayılarak). Bunlar etrafta saklayacağınız kimliklerdir.


3

Bunu kolaylaştırmak için, yalnızca sütun yılı için benzersiz bir kısıtlama uygulamak istediğinizi ve birincil anahtarın id adında bir sütun olduğunu varsayıyorum.

Yinelenen değerleri bulmak için çalıştırmanız gerekir,

SELECT year, COUNT(id)
FROM YOUR_TABLE
GROUP BY year
HAVING COUNT(id) > 1
ORDER BY COUNT(id);

Yukarıdaki sql deyimini kullanarak tablonuzdaki tüm yinelenen yılları içeren bir tablo elde edersiniz. Amacıyla son yinelenen giriş dışındaki tüm çiftleri silmek sql deyimi yukarıda kullanmalıdır.

DELETE
FROM YOUR_TABLE A USING YOUR_TABLE_AGAIN B
WHERE A.year=B.year AND A.id<B.id;
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.