Cara paling efisien untuk menghapus baris secara massal dari postgres

23

Saya bertanya-tanya apa cara paling efisien untuk menghapus sejumlah besar baris dari PostgreSQL, proses ini akan menjadi bagian dari tugas berulang setiap hari untuk mengimpor data massal (delta penyisipan + penghapusan) ke dalam tabel. Mungkin ada ribuan, berpotensi jutaan baris untuk dihapus.

Saya punya file kunci utama, satu per baris. Dua opsi yang saya pikirkan ada di sepanjang baris di bawah ini, tapi saya tidak tahu / cukup memahami internal PostgreSQL untuk membuat keputusan yang tepat.

  • Jalankan DELETEkueri untuk setiap baris dalam file, dengan sederhana WHEREpada kunci utama (atau kelompokkan penghapusan dalam batch nmenggunakan IN()klausa)
  • Impor kunci utama ke tabel sementara menggunakan COPYperintah dan kemudian hapus dari tabel utama menggunakan gabungan

Setiap saran akan sangat dihargai!

tarnfeld
sumber
1
Pertanyaan yang sama telah dijawab secara lebih rinci di sini: stackoverflow.com/a/8290958
Simon

Jawaban:

25

Pilihan kedua Anda jauh lebih bersih dan berkinerja cukup baik untuk membuatnya layak. Alternatif Anda adalah membuat pertanyaan raksasa yang akan cukup menyakitkan untuk direncanakan dan dijalankan. Secara umum Anda akan lebih baik membiarkan PostgreSQL melakukan pekerjaan di sini. Secara umum, saya telah menemukan pembaruan pada puluhan ribu baris dengan cara yang Anda jelaskan berkinerja memadai, tetapi ada satu hal penting yang harus dihindari.

Cara melakukannya adalah dengan menggunakan pilih dan bergabung dalam penghapusan Anda.

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

Dalam situasi apa pun Anda harus mengikuti tabel besar:

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

Ini biasanya akan menyebabkan antijoin loop bersarang yang akan membuat kinerja agak bermasalah. Jika Anda akhirnya harus menempuh rute itu, lakukan ini sebagai gantinya:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL biasanya cukup baik dalam menghindari rencana yang buruk tetapi masih ada kasus yang melibatkan gabungan luar yang dapat membuat perbedaan besar antara rencana yang baik dan yang buruk.

Ini mengembara sedikit lebih jauh, tapi saya pikir itu layak disebutkan karena betapa mudahnya untuk beralih dari IN ke NOT IN dan menonton tangki kinerja permintaan.

Chris Travers
sumber
Itu banyak membantu, terima kasih! Namun saya menemukan bahwa menggunakan "permintaan gabungan" lebih efisien dalam kasus khusus ini. Misalnya, IN ( select id from foo except select id from rows_to_keep ) lihat postgresql.org/docs/9.4/static/queries-union.html
Ufos
1

Saya menemukan pertanyaan ini karena saya memiliki masalah yang sama. Saya membersihkan database yang memiliki 300M + baris, database akhir hanya akan memiliki sekitar 30% dari data asli. Jika Anda menghadapi skenario yang sama, sebenarnya lebih mudah untuk memasukkan tabel baru dan mengindeks ulang daripada menghapus.

Lakukan sesuatu seperti

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

Dengan pengindeksan yang tepat di foo dan bar, Anda dapat menghindari pemindaian Seq.

Maka Anda harus mengindeks ulang dan mengganti nama tabel.

Niro
sumber