Saya harus menambahkan batasan unik ke tabel yang ada. Ini bagus kecuali bahwa tabel sudah memiliki jutaan baris, dan banyak baris yang melanggar batasan unik yang perlu saya tambahkan.
Apa pendekatan tercepat untuk menghilangkan baris yang melanggar? Saya memiliki pernyataan SQL yang menemukan duplikat dan menghapusnya, tetapi butuh waktu lama untuk dijalankan. Apakah ada cara lain untuk mengatasi masalah ini? Mungkin mencadangkan tabel, lalu memulihkan setelah batasan ditambahkan?
sql
postgresql
duplicate-removal
unique-constraint
sql-delete
gjrwebber.dll
sumber
sumber
CREATE TABLE tmp AS SELECT ...;
. Maka Anda bahkan tidak perlu mencari tahu apa tatatmp
letaknya. :)Beberapa dari pendekatan ini tampak sedikit rumit, dan saya biasanya melakukan ini sebagai:
Tabel yang diberikan
table
, ingin membuatnya unik di (bidang1, bidang2) menjaga baris dengan bidang3 maks:Misalnya, saya memiliki tabel,
user_accounts
dan saya ingin menambahkan batasan unik pada email, tetapi saya memiliki beberapa duplikat. Katakan juga bahwa saya ingin menyimpan yang paling baru dibuat (id maks di antara duplikat).USING
bukan SQL standar, ini adalah ekstensi PostgreSQL (tetapi sangat berguna), tetapi pertanyaan asli secara khusus menyebutkan PostgreSQL.sumber
USING
dilakukan di postgresql?WHERE table1.ctid<table2.ctid
- tidak perlu menambahkan kolom serialAlih-alih membuat tabel baru, Anda juga dapat memasukkan kembali baris unik ke dalam tabel yang sama setelah memotongnya. Lakukan semuanya dalam satu transaksi . Secara opsional, Anda dapat menghapus tabel sementara di akhir transaksi secara otomatis dengan
ON COMMIT DROP
. Lihat di bawah.Pendekatan ini hanya berguna jika ada banyak baris yang harus dihapus dari seluruh tabel. Untuk beberapa duplikat, gunakan polos
DELETE
.Anda menyebutkan jutaan baris. Untuk membuat operasi cepat, Anda ingin mengalokasikan buffer sementara yang cukup untuk sesi tersebut. Pengaturan harus disesuaikan sebelum buffer sementara apa pun digunakan dalam sesi Anda saat ini. Cari tahu ukuran meja Anda:
Atur
temp_buffers
sesuai. Kumpulkan dengan murah hati karena representasi dalam memori membutuhkan lebih banyak RAM.Metode ini bisa lebih baik daripada membuat tabel baru jika tergantung objek yang ada. Tampilan, indeks, kunci asing, atau objek lain yang mereferensikan tabel.
TRUNCATE
membuat Anda tetap memulai dengan papan tulis yang bersih (file baru di latar belakang) dan jauh lebih cepat dibandingkanDELETE FROM tbl
dengan tabel besar (DELETE
sebenarnya bisa lebih cepat dengan tabel kecil).Untuk tabel besar, biasanya lebih cepat untuk menghapus indeks dan kunci asing, mengisi ulang tabel, dan membuat ulang objek ini. Sejauh menyangkut kendala fk, Anda harus yakin bahwa data baru tentu saja valid atau Anda akan mengalami pengecualian saat mencoba membuat fk.
Perhatikan bahwa
TRUNCATE
membutuhkan penguncian yang lebih agresif daripadaDELETE
. Ini mungkin menjadi masalah untuk tabel dengan beban yang berat dan bersamaan.Jika
TRUNCATE
bukan merupakan pilihan atau umumnya untuk tabel kecil hingga menengah, ada teknik serupa dengan CTE pemodifikasi data (Postgres 9.1 +):Lebih lambat untuk tabel besar, karena
TRUNCATE
lebih cepat ke sana. Tetapi mungkin lebih cepat (dan lebih sederhana!) Untuk tabel kecil.Jika Anda tidak memiliki objek bergantung sama sekali, Anda dapat membuat tabel baru dan menghapus yang lama, tetapi Anda hampir tidak mendapatkan apa-apa dari pendekatan universal ini.
Untuk tabel yang sangat besar yang tidak sesuai dengan RAM yang tersedia , membuat tabel baru akan jauh lebih cepat. Anda harus mempertimbangkan ini terhadap kemungkinan masalah / overhead dengan objek yang bergantung.
sumber
TRUNCATE
. Seperti yang dikatakan Erwin, pastikan untuk memastikannya ada sebelum memotong meja Anda. Lihat jawaban @ codebyatON COMMIT DROP
, sehingga orang yang melewatkan bagian di mana saya menulis "dalam satu transaksi" tidak kehilangan data. Dan saya menambahkan BEGIN / COMMIT untuk memperjelas "satu transaksi".Anda dapat menggunakan oid atau ctid, yang biasanya merupakan kolom "tidak terlihat" di tabel:
sumber
NOT EXISTS
harus jauh lebih cepat :DELETE FROM tbl t WHERE EXISTS (SELECT 1 FROM tbl t1 WHERE t1.dist_col = t.dist_col AND t1.ctid > t.ctid)
- atau gunakan kolom atau kumpulan kolom lain untuk menyortir untuk memilih orang yang selamat.NOT EXISTS
?EXISTS
sini. Bacalah seperti ini: "Hapus semua baris di mana ada baris lain dengan nilai yang samadist_col
tetapi lebih besarctid
". Satu-satunya orang yang selamat dari setiap kelompok korban penipuan akan menjadi yang terbesarctid
.LIMIT
jika Anda mengetahui jumlah duplikatnya.Fungsi jendela PostgreSQL berguna untuk masalah ini.
Lihat Menghapus duplikat .
sumber
Dari milis postgresql.org lama :
Nilai-nilai unik
Nilai duplikat
Satu lagi duplikat ganda
Pilih baris duplikat
Hapus baris duplikat
Catatan: PostgreSQL tidak mendukung alias di tabel yang disebutkan di
from
klausul penghapusan.sumber
Kueri umum untuk menghapus duplikat:
Kolom
ctid
adalah kolom khusus yang tersedia untuk setiap tabel tetapi tidak terlihat kecuali disebutkan secara khusus. Nilaictid
kolom dianggap unik untuk setiap baris dalam tabel.sumber
GROUP BY
klausa dengan benar - ini harus menjadi 'kriteria keunikan' yang dilanggar sekarang atau jika Anda ingin kunci untuk mendeteksi duplikat. Jika ditentukan salah, itu tidak akan berfungsi dengan benarSaya baru saja menggunakan jawaban Erwin Brandstetter berhasil menghapus duplikat dalam tabel gabungan (tabel yang tidak memiliki ID utamanya sendiri), tetapi menemukan bahwa ada satu peringatan penting.
Termasuk
ON COMMIT DROP
berarti tabel sementara akan dijatuhkan di akhir transaksi. Bagi saya, itu berarti tabel sementara tidak lagi tersedia pada saat saya memasukkannya!Saya baru saja melakukannya
CREATE TEMPORARY TABLE t_tmp AS SELECT DISTINCT * FROM tbl;
dan semuanya bekerja dengan baik.Tabel sementara tidak dihilangkan di akhir sesi.
sumber
Fungsi ini menghapus duplikat tanpa menghapus indeks dan melakukannya ke tabel mana pun.
Pemakaian:
select remove_duplicates('mytable');
sumber
sumber
Jika Anda hanya memiliki satu atau beberapa entri duplikat, dan mereka memang duplikat (yaitu, muncul dua kali), Anda dapat menggunakan kolom "tersembunyi"
ctid
, seperti yang diusulkan di atas, bersama denganLIMIT
:Ini hanya akan menghapus baris pertama yang dipilih.
sumber
Pertama, Anda perlu memutuskan "duplikat" mana yang akan Anda simpan. Jika semua kolom sama, OK, Anda dapat menghapus salah satu dari mereka ... Tetapi mungkin Anda hanya ingin menyimpan yang terbaru, atau beberapa kriteria lain?
Cara tercepat bergantung pada jawaban Anda atas pertanyaan di atas, dan juga pada% duplikat di tabel. Jika Anda membuang 50% baris Anda, lebih baik Anda melakukannya
CREATE TABLE ... AS SELECT DISTINCT ... FROM ... ;
, dan jika Anda menghapus 1% baris, menggunakan DELETE lebih baik.Juga untuk operasi pemeliharaan seperti ini, umumnya baik untuk mengatur
work_mem
sebagian besar RAM Anda: jalankan JELASKAN, periksa nomor N jenis / hash, dan setel work_mem ke RAM / 2 / N. Gunakan banyak RAM; itu bagus untuk kecepatan. Selama Anda hanya memiliki satu koneksi bersamaan ...sumber
Saya bekerja dengan PostgreSQL 8.4. Ketika saya menjalankan kode yang diusulkan, saya menemukan bahwa itu tidak benar-benar menghapus duplikat. Dalam menjalankan beberapa tes, saya menemukan bahwa menambahkan "DISTINCT ON (duplikat_kolom_namaku)" dan "ORDER BY duplikat_kolom_namaku" berhasil. Saya bukan ahli SQL, saya menemukan ini di dokumen PostgreSQL 8.4 SELECT ... DISTINCT.
sumber
Ini bekerja dengan sangat baik dan sangat cepat:
sumber
Hapus duplikat menurut kolom dan pertahankan baris dengan id terendah. Polanya diambil dari wiki postgres
Dengan menggunakan CTE Anda dapat mencapai versi yang lebih mudah dibaca di atas melalui ini
sumber
sumber