PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Bagaimana cara UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) di PostgreSQL?

9.5 dan yang lebih baru:

PostgreSQL 9.5 dan yang lebih baru mendukung INSERT ... ON CONFLICT (key) DO UPDATE (dan ON CONFLICT (key) DO NOTHING ), yaitu upsert.

Perbandingan dengan ON DUPLICATE KEY UPDATE .

Penjelasan cepat.

Untuk penggunaan lihat manual - khususnya conflict_action klausa dalam diagram sintaks, dan teks penjelasan.

Tidak seperti solusi untuk 9.4 dan yang lebih lama yang diberikan di bawah ini, fitur ini bekerja dengan beberapa baris yang bertentangan dan tidak memerlukan penguncian eksklusif atau pengulangan percobaan.

Komitmen untuk menambahkan fitur ada di sini dan diskusi seputar pengembangannya ada di sini.

Jika Anda menggunakan 9.5 dan tidak perlu kompatibel dengan versi sebelumnya, Anda dapat berhenti membaca sekarang .

9.4 dan yang lebih lama:

PostgreSQL tidak memiliki UPSERT bawaan (atau MERGE ) fasilitas, dan melakukannya secara efisien dalam menghadapi penggunaan bersamaan sangat sulit.

Artikel ini membahas masalah dengan detail yang berguna.

Secara umum Anda harus memilih di antara dua opsi:

  • Operasi penyisipan/pembaruan individu dalam pengulangan percobaan; atau
  • Mengunci tabel dan melakukan penggabungan batch

Loop percobaan ulang baris individu

Menggunakan upsert baris individual dalam loop percobaan ulang adalah opsi yang masuk akal jika Anda ingin banyak koneksi secara bersamaan mencoba melakukan penyisipan.

Dokumentasi PostgreSQL berisi prosedur berguna yang memungkinkan Anda melakukan ini dalam satu lingkaran di dalam database. Ini menjaga dari pembaruan yang hilang dan memasukkan balapan, tidak seperti kebanyakan solusi naif. Ini hanya akan berfungsi di READ COMMITTED mode dan hanya aman jika itu 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 praktis, Anda harus mengantre pekerjaan dan melakukan upser massal seperti yang dijelaskan di bawah ini.

Banyak solusi yang dicoba untuk masalah ini gagal mempertimbangkan rollback, sehingga menghasilkan pembaruan yang tidak lengkap. Dua transaksi berpacu satu sama lain; salah satunya berhasil INSERT s; yang lain mendapatkan kesalahan kunci duplikat dan melakukan UPDATE sebagai gantinya. UPDATE blok menunggu INSERT untuk mengembalikan atau melakukan. Saat diputar kembali, UPDATE pemeriksaan ulang kondisi cocok dengan nol baris, jadi meskipun UPDATE melakukan itu belum benar-benar melakukan upsert yang Anda harapkan. Anda harus memeriksa jumlah baris hasil dan mencoba lagi jika perlu.

Beberapa solusi yang dicoba juga gagal mempertimbangkan balapan 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;

kemudian ketika dua dijalankan sekaligus ada beberapa mode kegagalan. Salah satunya adalah masalah yang sudah dibahas dengan pemeriksaan ulang pembaruan. Lain adalah di mana keduanya UPDATE pada saat yang sama, mencocokkan baris nol dan melanjutkan. Kemudian mereka berdua melakukan EXISTS tes, yang terjadi sebelum INSERT . Keduanya mendapatkan baris nol, jadi keduanya melakukan INSERT . Satu gagal dengan kesalahan kunci duplikat.

Inilah sebabnya mengapa Anda memerlukan pengulangan percobaan. 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 (bergantung pada pendekatan yang dipilih) dan mencoba kembali.

Tolong jangan menggulung solusi Anda sendiri untuk ini. Seperti dengan antrian pesan, itu mungkin salah.

Upser massal dengan kunci

Terkadang Anda ingin melakukan upsert massal, di mana Anda memiliki kumpulan data baru yang ingin Anda gabungkan ke dalam kumpulan data lama yang sudah ada. Ini sangat lebih efisien daripada upser baris individual dan harus dipilih kapan pun praktis.

Dalam hal ini, Anda biasanya mengikuti proses berikut:

  • CREATE sebuah TEMPORARY tabel

  • COPY atau masukkan data baru secara massal ke tabel temp

  • LOCK tabel target IN EXCLUSIVE MODE . Ini mengizinkan transaksi lain untuk SELECT , tetapi tidak membuat perubahan apa pun pada tabel.

  • Lakukan UPDATE ... FROM catatan yang ada menggunakan 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

  • UPSERT halaman wiki
  • UPSERTisme di Postgres
  • Sisipkan, pada pembaruan duplikat di PostgreSQL?
  • http://petereisentraut.blogspot.com/2010/05/merge-syntax.html
  • Upsert dengan transaksi
  • Apakah SELECT atau INSERT dalam fungsi rentan terhadap kondisi balapan?
  • SQL MERGE di wiki PostgreSQL
  • Cara paling idiomatis untuk mengimplementasikan UPSERT di Postgresql saat ini

Bagaimana dengan MERGE ?

MERGE standar SQL sebenarnya memiliki semantik konkurensi yang tidak terdefinisi dengan baik dan tidak cocok untuk upserting tanpa mengunci tabel terlebih dahulu.

Ini adalah pernyataan OLAP yang sangat berguna untuk penggabungan data, tetapi sebenarnya bukan solusi yang berguna untuk upsert yang aman secara konkurensi. Ada banyak saran untuk orang yang menggunakan DBMS lain untuk menggunakan MERGE untuk upser, tapi sebenarnya salah.

DB lain:

  • INSERT ... ON DUPLICATE KEY UPDATE di MySQL
  • MERGE dari MS SQL Server (tapi lihat di atas tentang MERGE masalah)
  • MERGE dari Oracle (tapi lihat di atas tentang MERGE masalah)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara Mengonversi Kasus String di PostgreSQL

  2. Bagaimana saya bisa menggunakan UUID di SQLAlchemy?

  3. Bagaimana make_time() Bekerja di PostgreSQL

  4. Pilih jumlah baris di tabel lain dalam pernyataan SELECT Postgres

  5. Apa yang Baru di PgBouncer 1.6