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

Bagaimana cara memasukkan baris yang dikecualikan di RETURNING from INSERT ... ON CONFLICT

Kesalahan yang Anda dapatkan:

Perintah ON CONFLICT DO UPDATE tidak dapat mempengaruhi baris untuk kedua kalinya

menunjukkan Anda mencoba memasukkan baris yang sama lebih dari sekali dalam satu perintah. Dengan kata lain:Anda memiliki penipuan di (name, url, email) di VALUES . Anda daftar. Lipat duplikat (jika itu opsi) dan itu akan berfungsi. Tetapi Anda harus memutuskan baris mana yang akan dipilih dari setiap kumpulan penipuan.

INSERT INTO feeds_person (created, modified, name, url, email)
SELECT DISTINCT ON (name, url, email) *
FROM  (
   VALUES
   ('blah', 'blah', 'blah', 'blah', 'blah')
   -- ... more
   ) v(created, modified, name, url, email)  -- match column list
ON     CONFLICT (name, url, email) DO UPDATE
SET    url = feeds_person.url
RETURNING id;

Karena kami menggunakan VALUES yang berdiri sendiri ekspresi sekarang, Anda harus menambahkan gips tipe eksplisit untuk tipe non-default. Seperti:

VALUES
    (timestamptz '2016-03-12 02:47:56+01'
   , timestamptz '2016-03-12 02:47:56+01'
   , 'n3', 'u3', 'e3')
   ...

timestamptz . Anda kolom membutuhkan pemeran tipe eksplisit, sedangkan tipe string dapat beroperasi dengan text default . (Anda masih dapat melakukan transmisi ke varchar(n) segera.)

Ada beberapa cara untuk menentukan baris mana yang harus dipilih dari setiap kumpulan penipuan:

  • Pilih baris pertama di setiap grup GROUP BY?

Anda benar, (saat ini) tidak ada cara untuk dikecualikan baris di RETURNING ayat. Saya mengutip Postgres Wiki:

Perhatikan bahwa RETURNING tidak menampilkan "EXCLUDED.* " aliasdari UPDATE (hanya "TARGET.*" generik " alias terlihat di sana). Melakukan hal itu dianggap menciptakan ambiguitas yang mengganggu untuk kasus-kasus umum yang sederhana [30] dengan sedikit atau tanpa manfaat. Pada suatu saat di masa mendatang, kami mungkin mencari cara untuk mengekspos ifRETURNING - tupel yang diproyeksikan dimasukkan dan diperbarui, tetapi ini mungkin tidak perlu membuatnya menjadi iterasi berkomitmen pertama dari fitur [31].

Namun , Anda tidak boleh memperbarui baris yang tidak seharusnya diperbarui. Pembaruan kosong hampir semahal pembaruan biasa - dan mungkin memiliki efek samping yang tidak diinginkan. Anda tidak benar-benar membutuhkan UPSERT untuk memulai, kasing Anda lebih mirip "PILIH atau MASUKKAN". Terkait:

  • Apakah SELECT atau INSERT dalam fungsi rentan terhadap kondisi balapan?

Satu cara yang lebih bersih untuk menyisipkan satu set baris adalah dengan CTE yang memodifikasi data:

WITH val AS (
   SELECT DISTINCT ON (name, url, email) *
   FROM  (
      VALUES 
      (timestamptz '2016-1-1 0:0+1', timestamptz '2016-1-1 0:0+1', 'n', 'u', 'e')
    , ('2016-03-12 02:47:56+01', '2016-03-12 02:47:56+01', 'n1', 'u3', 'e3')
      -- more (type cast only needed in 1st row)
      ) v(created, modified, name, url, email)
   )
, ins AS (
   INSERT INTO feeds_person (created, modified, name, url, email)
   SELECT created, modified, name, url, email FROM val
   ON     CONFLICT (name, url, email) DO NOTHING
   RETURNING id, name, url, email
   )
SELECT 'inserted' AS how, id FROM ins  -- inserted
UNION  ALL
SELECT 'selected' AS how, f.id         -- not inserted
FROM   val v
JOIN   feeds_person f USING (name, url, email);

Kompleksitas tambahan harus membayar untuk tabel besar di mana INSERT adalah aturan dan SELECT pengecualian.

Awalnya, saya telah menambahkan NOT EXISTS predikat pada SELECT terakhir untuk mencegah duplikat dalam hasil. Tapi itu berlebihan. Semua CTE dari satu kueri melihat cuplikan tabel yang sama. Set dikembalikan dengan ON CONFLICT (name, url, email) DO NOTHING saling eksklusif untuk set yang dikembalikan setelah INNER JOIN pada kolom yang sama.

Sayangnya ini juga membuka jendela kecil untuk kondisi balapan . Jika ...

  • transaksi bersamaan menyisipkan baris yang bertentangan
  • belum berkomitmen
  • tetapi akhirnya berhasil

... beberapa baris mungkin hilang.

Anda mungkin hanya INSERT .. ON CONFLICT DO NOTHING , diikuti oleh SELECT separate yang terpisah permintaan untuk semua baris - dalam transaksi yang sama untuk mengatasi ini. Yang pada gilirannya membuka jendela kecil lainnya untuk kondisi balapan jika transaksi bersamaan dapat melakukan penulisan ke tabel antara INSERT dan SELECT (secara default READ COMMITTED tingkat isolasi). Dapat dihindari dengan REPEATABLE READ isolasi transaksi (atau lebih ketat). Atau dengan kunci tulis (mungkin mahal atau bahkan tidak dapat diterima) di seluruh meja. Anda bisa mendapatkan perilaku apa pun yang Anda butuhkan, tetapi mungkin ada harga yang harus dibayar.

Terkait:

  • Bagaimana cara menggunakan RETURNING dengan ON CONFLICT di PostgreSQL?
  • Kembalikan baris dari INSERT dengan ON CONFLICT tanpa perlu memperbarui



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana Acos() Bekerja di PostgreSQL

  2. Nyali PostgreSQL:Apa itu "resjunk"?

  3. “PERINGATAN:Ditemukan ketidakcocokan antara sl_table dan pg_class.” di Slony-I

  4. Tampilan Terwujud PostgreSQL

  5. Klausul Care To Know:Semua Tentang SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, dan LIMIT