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

Kembalikan nilai kolom pra-PERBARUI menggunakan SQL saja

Masalah

Manual menjelaskan:

RETURNING opsional klausa menyebabkan UPDATE untuk menghitung dan mengembalikan nilai berdasarkan setiap baris yang benar-benar diperbarui. Ekspresi apa pun yang menggunakan kolom tabel, dan/atau kolom tabel lain yang disebutkan dalam FROM , dapat dihitung. Nilai baru (pasca-pembaruan) dari kolom tabel digunakan . Sintaks dari RETURNING list identik dengan daftar keluaran SELECT .

Penekanan saya yang berani. Tidak ada cara untuk mengakses baris lama dalam RETURNING ayat. Anda dapat mengatasi pembatasan ini dengan pemicu atau SELECT separate yang terpisah sebelum UPDATE dibungkus dalam transaksi atau dibungkus dengan CTE seperti yang dikomentari.

Namun, apa yang Anda coba capai berfungsi dengan baik jika Anda bergabung ke instance lain dari tabel di FROM klausa:

Solusi tanpa penulisan bersamaan

UPDATE tbl x
SET    tbl_id = 23
     , name = 'New Guy'
FROM   tbl y                -- using the FROM clause
WHERE  x.tbl_id = y.tbl_id  -- must be UNIQUE NOT NULL
AND    x.tbl_id = 3
RETURNING y.tbl_id AS old_id, y.name AS old_name
        , x.tbl_id          , x.name;

Pengembalian:

 old_id | old_name | tbl_id |  name
--------+----------+--------+---------
  3     | Old Guy  | 23     | New Guy

Kolom yang digunakan untuk bergabung sendiri harus UNIQUE NOT NULL . Dalam contoh sederhana, WHERE kondisi pada kolom yang sama tbl_id , tapi itu hanya kebetulan. Bekerja untuk apa saja kondisi.

Saya menguji ini dengan versi PostgreSQL dari 8.4 hingga 13.

Ini berbeda untuk INSERT :

  • MASUKKAN KE ... DARI PILIH ... MENGEMBALIKAN pemetaan id

Solusi dengan beban tulis bersamaan

Ada berbagai cara untuk menghindari kondisi balapan dengan operasi penulisan bersamaan pada baris yang sama. (Perhatikan bahwa operasi penulisan bersamaan pada baris yang tidak terkait sama sekali tidak masalah.) Metode sederhana, lambat dan pasti (tapi mahal) adalah menjalankan transaksi dengan SERIALIZABLE tingkat isolasi:

BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE ... ;
COMMIT;

Tapi itu mungkin berlebihan. Dan Anda harus bersiap untuk mengulangi operasi jika terjadi kegagalan serialisasi.

Lebih sederhana dan lebih cepat (dan sama andalnya dengan beban tulis bersamaan) adalah kunci eksplisit pada satu baris yang akan diperbarui:

UPDATE tbl x
SET    tbl_id = 24
     , name = 'New Gal'
FROM  (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y 
WHERE  x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name
        , x.tbl_id          , x.name;

Perhatikan bagaimana WHERE kondisi dipindahkan ke subquery (sekali lagi, bisa apa saja ), dan hanya self-join (pada UNIQUE NOT NULL column(s)) tetap berada di kueri luar. Ini menjamin bahwa hanya baris yang dikunci oleh SELECT inner bagian dalam diproses. WHERE kondisi mungkin berubah menjadi kumpulan baris yang berbeda beberapa saat kemudian.

Lihat:

  • PEMBARUAN Atom .. PILIH di Postgres

db<>main biola di sini
sqlfiddle lama



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. INITCAP() – Konversi ke Huruf Awal di PostgreSQL

  2. Pemantauan PostgreSQL Penting - Bagian 2

  3. tidak dapat menghapus objek karena batasan kunci asing

  4. Setara dengan PostgreSQL untuk TOP n WITH TIES:LIMIT with ties?

  5. Apakah mungkin untuk menyediakan parameter untuk nama tabel atau kolom di Pernyataan yang Disiapkan atau QueryRunner.update()?