9.5 dan yang lebih baru:
PostgreSQL 9.5 dan dukungan yang lebih baru INSERT ... ON CONFLICT UPDATE
(dan ON CONFLICT DO NOTHING
), yaitu upert.
Perbandingan denganON DUPLICATE KEY UPDATE
.
Penjelasan cepat .
Untuk penggunaan melihat manual - khususnya conflict_action klausul dalam diagram sintaks, dan teks jelas .
Berbeda dengan solusi untuk 9.4 dan lebih lama yang diberikan di bawah ini, fitur ini bekerja dengan beberapa baris yang saling bertentangan dan tidak memerlukan penguncian eksklusif atau coba lagi loop.
Komit yang menambahkan fitur ada di sini dan diskusi seputar pengembangannya ada di sini .
Jika Anda menggunakan 9.5 dan tidak perlu kompatibel-mundur Anda dapat berhenti membaca sekarang .
9.4 dan lebih lama:
PostgreSQL tidak memiliki fasilitas built-in UPSERT
(atau MERGE
), dan melakukannya secara efisien dalam menghadapi penggunaan bersamaan sangat sulit.
Artikel ini membahas masalah dengan detail yang bermanfaat .
Secara umum Anda harus memilih antara dua opsi:
- Operasi memasukkan / memperbarui secara individu dalam loop coba lagi; atau
- Mengunci tabel dan melakukan penggabungan batch
Pengulangan coba baris individual
Menggunakan masing-masing baris upert dalam loop coba adalah pilihan yang masuk akal jika Anda ingin banyak koneksi secara bersamaan mencoba melakukan sisipan.
Dokumentasi PostgreSQL berisi prosedur bermanfaat yang memungkinkan Anda melakukan ini dalam satu lingkaran di dalam basis data . Ini melindungi terhadap pembaruan yang hilang dan menyisipkan balapan, tidak seperti kebanyakan solusi naif. Ini hanya akan bekerja dalam READ COMMITTED
mode dan hanya aman jika itu adalah satu-satunya hal yang Anda lakukan dalam transaksi. Fungsi tidak akan berfungsi dengan benar jika pemicu atau kunci unik sekunder menyebabkan pelanggaran unik.
Strategi ini sangat tidak efisien. Kapan pun Anda praktis, Anda harus mengantri kerja dan melakukan upert massal seperti yang dijelaskan di bawah ini.
Banyak upaya solusi untuk masalah ini gagal untuk mempertimbangkan rollback, sehingga menghasilkan pembaruan yang tidak lengkap. Dua transaksi saling bersaing; salah satunya berhasil INSERT
; yang lain mendapat kesalahan kunci duplikat dan melakukan UPDATE
sebaliknya. The UPDATE
blok menunggu INSERT
untuk rollback atau melakukan. Ketika bergulir kembali, UPDATE
kondisi pemeriksaan ulang cocok dengan baris nol, jadi meskipun UPDATE
komit tidak benar-benar melakukan upert yang Anda harapkan. Anda harus memeriksa jumlah baris hasil dan mencoba kembali jika perlu.
Beberapa solusi yang dicoba juga gagal untuk mempertimbangkan ras SELECT. Jika Anda mencoba yang jelas dan sederhana:
-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.
BEGIN;
UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;
-- Remember, this is WRONG. Do NOT COPY IT.
INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);
COMMIT;
maka ketika dua dijalankan sekaligus ada beberapa mode kegagalan. Salah satunya adalah masalah yang sudah dibahas dengan pembaruan ulang. Lain adalah di mana keduanya UPDATE
pada saat yang sama, mencocokkan nol baris dan melanjutkan. Kemudian mereka berdua melakukan EXISTS
tes, yang terjadi sebelum itu INSERT
. Keduanya mendapatkan nol baris, jadi keduanya melakukan INSERT
. Satu gagal dengan kesalahan kunci duplikat.
Inilah sebabnya mengapa Anda perlu mencoba ulang lingkaran. Anda mungkin berpikir bahwa Anda dapat mencegah kesalahan kunci duplikat atau kehilangan pembaruan dengan SQL pintar, tetapi Anda tidak bisa. Anda perlu memeriksa jumlah baris atau menangani kesalahan kunci duplikat (tergantung pada pendekatan yang dipilih) dan coba lagi.
Tolong jangan roll solusi Anda sendiri untuk ini. Seperti halnya antrian pesan, itu mungkin salah.
Upert massal dengan kunci
Kadang-kadang Anda ingin melakukan bulk upsert, di mana Anda memiliki kumpulan data baru yang ingin Anda gabungkan ke dalam kumpulan data lama yang sudah ada. Ini jauh lebih efisien daripada baris individual dan harus lebih disukai jika praktis.
Dalam hal ini, Anda biasanya mengikuti proses berikut:
CREATE
sebuah TEMPORARY
meja
COPY
atau massal-memasukkan data baru ke tabel temp
LOCK
tabel target IN EXCLUSIVE MODE
. Ini memungkinkan transaksi lain untuk SELECT
, tetapi tidak membuat perubahan apa pun pada tabel.
Lakukan UPDATE ... FROM
catatan yang ada menggunakan nilai-nilai dalam tabel temp;
Lakukan INSERT
baris yang belum ada di tabel target;
COMMIT
, melepaskan kunci.
Misalnya, untuk contoh yang diberikan dalam pertanyaan, menggunakan multi-nilai INSERT
untuk mengisi tabel temp:
BEGIN;
CREATE TEMPORARY TABLE newvals(id integer, somedata text);
INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');
LOCK TABLE testtable IN EXCLUSIVE MODE;
UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;
INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;
COMMIT;
Bacaan terkait
Bagaimana dengan MERGE
?
SQL-standar MERGE
sebenarnya memiliki semantik konkurensi yang tidak jelas dan tidak cocok untuk memasang tanpa mengunci meja terlebih dahulu.
Ini adalah pernyataan OLAP yang sangat berguna untuk penggabungan data, tetapi sebenarnya bukan solusi yang berguna untuk uperturrency yang aman. Ada banyak saran untuk orang-orang yang menggunakan DBMS lain untuk digunakan MERGE
untuk upert, tetapi sebenarnya itu salah.
DB lain: