Bagaimana menemukan catatan duplikat di PostgreSQL

190

Saya memiliki tabel database PostgreSQL yang disebut "user_links" yang saat ini memungkinkan bidang duplikat berikut:

year, user_id, sid, cid

Kendala yang unik saat field pertama disebut "id", namun saya sekarang mencari untuk menambahkan kendala untuk memastikan year, user_id, siddan cidsemua unik tapi saya tidak dapat menerapkan kendala karena nilai-nilai duplikat sudah ada yang melanggar kendala ini.

Apakah ada cara untuk menemukan semua duplikat?

John
sumber
2
kemungkinan duplikat Temukan baris duplikat dengan PostgreSQL
drs

Jawaban:

335

Gagasan dasar akan menggunakan kueri bersarang dengan agregasi jumlah:

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

Anda dapat menyesuaikan klausa di mana dalam permintaan dalam untuk mempersempit pencarian.


Ada solusi lain yang bagus untuk yang disebutkan dalam komentar, (tetapi tidak semua orang membacanya):

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

Atau lebih pendek:

SELECT (yourTable.*)::text, count(*)
FROM yourTable
GROUP BY yourTable.*
HAVING count(*) > 1
Marcin Zablocki
sumber
65
Anda juga bisa menggunakan HAVING:select co1, col2, count(*) from tbl group by col1, col2 HAVING count(*)>1
alexkovelsky
1
Terima kasih @alexkovelsky pernyataan memiliki lebih mudah untuk memodifikasi untuk saya dan berlari lebih cepat. Saya akan menyarankan jawaban untuk visibilitas yang lebih tinggi.
Vesanto
opsi ini bekerja untuk saya, yang lain mengelompokkan hasil, dan opsi ini memberi saya semua rekaman duplikat, bukan hanya catatan yang digandakan, terima kasih!
rome3ro
1
Saya minta jawaban Anda agak lambat. Di atas meja 10k baris * 18 kolom, kueri membutuhkan waktu 8 detik
aydow
1
itulah kemacetan di sana kawan. sih ya. Terima kasih. 💯
dps
91

Dari " Temukan baris duplikat dengan PostgreSQL " di sini adalah solusi cerdas:

select * from (
  SELECT id,
  ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id asc) AS Row
  FROM tbl
) dups
where 
dups.Row > 1
alexkovelsky
sumber
11
Ini cepat! Bekerja lebih dari jutaan baris dalam sepersekian detik. Jawaban lain hanya digantung di sana ...
dmvianna
5
Seperti yang saya lihat, permintaan ini tidak mempertimbangkan semua baris dalam grup. Ini hanya menunjukkan duplikat ke sesuatu, bagian dari duplikat akan dengan rownum = 1. Koreksi saya Jika saya salah
Vladimir Filipchenko
9
@vladimir Filipchenko Untuk memilikinya dengan semua lini, tambahkan level ke solusi Alexkovelsky: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 Cukup ganti ROW_NUMBER()dengan COUNT(*), dan tambahkan rows between unbounded preceding and unbounded followingsetelahORDER BY id asc
alexkovelsky
2
jauh lebih baik daripada solusi lain yang saya temukan. juga berfungsi sama baiknya untuk menghapus dupes dengan DELETE ...USINGdan beberapa penyesuaian kecil
Brandon
6

Anda bisa bergabung ke tabel yang sama di bidang yang akan digandakan dan kemudian anti-bergabung di bidang id. Pilih bidang id dari alias tabel pertama (tn1) dan kemudian gunakan fungsi array_agg pada bidang id dari alias tabel kedua. Terakhir, agar fungsi array_agg berfungsi dengan baik, Anda akan mengelompokkan hasilnya berdasarkan bidang tn1.id. Ini akan menghasilkan set hasil yang berisi id dari catatan dan array dari semua id yang sesuai dengan kondisi gabungan.

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;

Jelas, id yang akan berada dalam array duplikat untuk satu id, juga akan memiliki entri mereka sendiri di set hasil. Anda harus menggunakan set hasil ini untuk memutuskan id mana yang Anda inginkan untuk menjadi sumber 'kebenaran'. Satu catatan yang tidak boleh dihapus. Mungkin Anda bisa melakukan sesuatu seperti ini:

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)

Memilih nomor ID terendah yang memiliki duplikat (dengan asumsi ID meningkat ke PK). Ini akan menjadi ID yang akan Anda simpan.

pwnyexpress
sumber
3

Untuk membuatnya lebih mudah, saya berasumsi bahwa Anda ingin menerapkan batasan unik hanya untuk tahun kolom dan kunci utama adalah kolom bernama id.

Untuk menemukan nilai duplikat yang harus Anda jalankan,

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

Menggunakan pernyataan sql di atas Anda mendapatkan tabel yang berisi semua tahun duplikat di tabel Anda. Untuk menghapus semua duplikat kecuali entri duplikat terbaru, Anda harus menggunakan pernyataan sql di atas.

DELETE
FROM YOUR_TABLE A USING YOUR_TABLE_AGAIN B
WHERE A.year=B.year AND A.id<B.id;
George Siggouroglou
sumber