Saya pikir saya mungkin bermaksud menambahkan komentar itu pada jawaban sebelumnya, tentang dua pernyataan terpisah. Sudah lebih dari setahun yang lalu, jadi saya tidak sepenuhnya yakin lagi.
Kueri berbasis wCTE tidak benar-benar menyelesaikan masalah yang seharusnya, tetapi setelah meninjaunya kembali lebih dari setahun kemudian saya tidak melihat kemungkinan pembaruan yang hilang dalam versi wCTE.
(Perhatikan bahwa semua solusi ini hanya akan berfungsi dengan baik jika Anda mencoba mengubah tepat satu baris pada setiap transaksi. Segera setelah Anda mencoba untuk melakukan beberapa perubahan dalam satu transaksi, semuanya menjadi berantakan karena kebutuhan untuk mencoba lagi loop pada rollback. Minimal Anda harus menggunakan savepoint di antara setiap perubahan.)
Versi dua pernyataan tunduk pada pembaruan yang hilang.
Versi yang menggunakan dua pernyataan terpisah dapat mengalami pembaruan yang hilang kecuali aplikasi memeriksa jumlah baris yang terpengaruh dari UPDATE
pernyataan dan INSERT
pernyataan dan mencoba lagi jika keduanya nol.
Bayangkan apa yang terjadi jika Anda memiliki dua transaksi secara READ COMMITTED
terpisah.
- TX1 menjalankan
UPDATE
(tidak ada efek)
- TX1 menjalankan
INSERT
(menyisipkan baris)
- TX2 menjalankan
UPDATE
(tidak ada efek, baris yang dimasukkan oleh TX1 belum terlihat)
- TX1
COMMIT
s.
- TX2 menjalankan
INSERT
, * yang mendapat snapshot baru yang dapat melihat baris yang dilakukan oleh TX1. The EXISTS
klausul pengembalian benar, karena TX2 sekarang dapat melihat baris dimasukkan oleh TX1.
Jadi TX2 tidak berpengaruh. Kecuali jika aplikasi memeriksa jumlah baris dari pembaruan dan sisipan dan coba lagi jika keduanya melaporkan nol baris, ia tidak akan tahu bahwa transaksi tidak berpengaruh dan akan melanjutkan dengan gembira.
Satu-satunya cara ia dapat memeriksa jumlah baris yang terpengaruh adalah menjalankannya sebagai dua pernyataan terpisah dan bukan multi-pernyataan, atau menggunakan prosedur.
Anda dapat menggunakan SERIALIZABLE
isolasi, tetapi Anda masih perlu mengulangi loop untuk menghadapi kegagalan serialisasi.
Versi wCTE melindungi terhadap masalah pembaruan yang hilang karena INSERT
tergantung pada apakah UPDATE
mempengaruhi setiap baris, bukan pada permintaan yang terpisah.
WCTE tidak menghilangkan pelanggaran unik
Versi CTE yang dapat ditulisi masih belum bisa diandalkan.
Pertimbangkan dua transaksi yang menjalankan ini secara bersamaan.
Keduanya menjalankan klausa VALUES.
Sekarang keduanya menjalankan UPDATE
porsi. Karena tidak ada baris yang cocok dengan UPDATE
klausa s where, keduanya mengembalikan resultset kosong dari pembaruan dan tidak membuat perubahan.
Sekarang keduanya menjalankan INSERT
porsinya. Karena UPDATE
baris nol yang dikembalikan untuk kedua kueri, keduanya mencoba ke INSERT
baris.
Satu berhasil. Satu melemparkan pelanggaran unik dan batal.
Ini bukan alasan untuk khawatir tentang kehilangan data selama aplikasi memeriksa hasil kesalahan dari kueri-nya (yaitu aplikasi apa pun yang ditulis dengan sopan) dan mencoba ulang, tetapi itu membuat solusinya tidak lebih baik daripada versi dua pernyataan yang ada. Itu tidak menghilangkan kebutuhan untuk mengulangi loop.
Keuntungan yang ditawarkan wCTE dibandingkan versi dua pernyataan yang ada adalah bahwa ia menggunakan output dari UPDATE
untuk memutuskan apakah akan INSERT
, daripada menggunakan kueri terpisah terhadap tabel. Itu sebagian optimasi, tetapi sebagian melindungi terhadap masalah dengan versi dua pernyataan yang menyebabkan pembaruan hilang; Lihat di bawah.
Anda dapat menjalankan wCTE secara SERIALIZABLE
terpisah, tetapi kemudian Anda hanya akan mendapatkan kegagalan serialisasi alih-alih pelanggaran unik. Itu tidak akan mengubah kebutuhan untuk mengulangi loop.
WCTE tampaknya tidak rentan terhadap pembaruan yang hilang
Komentar saya menyarankan bahwa solusi ini dapat mengakibatkan pembaruan yang hilang, tetapi setelah meninjau bahwa saya pikir saya mungkin salah.
Sudah lebih dari setahun yang lalu, dan saya tidak dapat mengingat keadaan yang tepat, tetapi saya pikir saya mungkin melewatkan fakta bahwa indeks unik memiliki sebagian pengecualian dari aturan visibilitas transaksi untuk memungkinkan satu transaksi memasukkan menunggu yang lain untuk memasukkan atau memutar kembali sebelum melanjutkan.
Atau mungkin saya melewatkan fakta bahwa INSERT
dalam WCTE tergantung pada apakah UPDATE
baris yang terpengaruh, bukan pada apakah baris kandidat ada di tabel.
Konflik INSERT
pada indeks unik menunggu komit / kembalikan
Katakan bahwa satu salinan kueri berjalan, menyisipkan baris. Perubahan belum dilakukan. Tupel baru ada di heap dan indeks unik, tetapi belum terlihat oleh transaksi lain, terlepas dari tingkat isolasi.
Sekarang salinan kueri lain berjalan. Baris yang dimasukkan belum terlihat karena salinan pertama belum dilakukan, sehingga pembaruan tidak cocok dengan apa pun. Kueri akan melanjutkan untuk mencoba menyisipkan, yang akan melihat bahwa transaksi lain yang sedang berlangsung sedang memasukkan kunci yang sama dan akan memblokir menunggu transaksi itu dilakukan atau dibatalkan .
Jika transaksi pertama dilakukan, yang kedua akan gagal dengan pelanggaran unik, per di atas. Jika transaksi pertama gulung kembali maka yang kedua akan melanjutkan dengan memasukkannya sebagai gantinya.
The INSERT
tergantung pada UPDATE
rowcount melindungi terhadap update yang hilang
Tidak seperti dalam kasus dua pernyataan, saya tidak berpikir WCTE rentan terhadap pembaruan yang hilang.
Jika UPDATE
tidak memiliki efek, itu INSERT
akan selalu berjalan, karena itu sangat tergantung pada apakah UPDATE
melakukan sesuatu, bukan pada keadaan tabel eksternal. Jadi masih bisa gagal dengan pelanggaran unik, tetapi tidak bisa diam-diam gagal untuk memiliki efek dan kehilangan pembaruan sepenuhnya.