Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

UPSERT Atom di SQL Server 2005

INSERT INTO <table>
SELECT <natural keys>, <other stuff...>
FROM <table>
WHERE NOT EXISTS
   -- race condition risk here?
   ( SELECT 1 FROM <table> WHERE <natural keys> )

UPDATE ...
WHERE <natural keys>
  • ada kondisi balapan di INSERT pertama. Kunci mungkin tidak ada selama kueri dalam SELECT, tetapi ada pada waktu INSERT yang mengakibatkan pelanggaran kunci.
  • ada kondisi balapan antara INSERT dan UPDATE. Kunci mungkin ada saat diperiksa di kueri dalam INSERT tetapi hilang saat UPDATE berjalan.

Untuk kondisi balapan kedua, orang dapat berargumen bahwa kunci akan tetap dihapus oleh utas bersamaan, jadi itu bukan pembaruan yang benar-benar hilang.

Solusi optimal biasanya mencoba kasus yang paling mungkin, dan menangani kesalahan jika gagal (tentu saja di dalam transaksi):

  • jika kunci kemungkinan hilang, selalu masukkan terlebih dahulu. Tangani pelanggaran batasan unik, mundur untuk memperbarui.
  • jika kuncinya mungkin ada, selalu perbarui terlebih dahulu. Sisipkan jika tidak ada baris yang ditemukan. Tangani kemungkinan pelanggaran batasan unik, mundur untuk memperbarui.

Selain benar, pola ini juga optimal untuk kecepatan:lebih efisien untuk mencoba memasukkan dan menangani pengecualian daripada melakukan penguncian palsu. Penguncian berarti pembacaan halaman logis (yang mungkin berarti pembacaan halaman fisik), dan IO (bahkan logis) lebih mahal daripada SEH.

Perbarui @Peter

Mengapa tidak ada satu pernyataan 'atom'? Katakanlah kita memiliki tabel trivial:

create table Test (id int primary key);

Sekarang jika saya menjalankan pernyataan tunggal ini dari dua utas, dalam satu lingkaran, itu akan menjadi 'atom', seperti yang Anda katakan, tidak ada kondisi balapan:

  insert into Test (id)
    select top (1) id
    from Numbers n
    where not exists (select id from Test where id = n.id); 

Namun hanya dalam beberapa detik, pelanggaran kunci utama terjadi:

Msg 2627, Level 14, State 1, Line 4
Pelanggaran batasan PRIMARY KEY 'PK__Test__24927208'. Tidak dapat menyisipkan kunci duplikat di objek 'dbo.Test'.

Mengapa demikian? Anda benar bahwa rencana kueri SQL akan melakukan 'hal yang benar' pada DELETE ... FROM ... JOIN , pada WITH cte AS (SELECT...FROM ) DELETE FROM cte dan dalam banyak kasus lainnya. Namun ada perbedaan penting dalam kasus ini:'subquery' mengacu pada target dari pembaruan atau hapus operasi. Untuk kasus seperti itu, rencana kueri memang akan menggunakan kunci yang sesuai, sebenarnya saya perilaku ini sangat penting pada kasus tertentu, seperti saat mengimplementasikan antrian Menggunakan tabel sebagai Antrian.

Tetapi dalam pertanyaan awal, serta dalam contoh saya, subquery dilihat oleh pengoptimal kueri hanya sebagai subkueri dalam kueri, bukan sebagai kueri jenis 'pindai untuk pembaruan' khusus yang memerlukan perlindungan kunci khusus. Hasilnya adalah eksekusi pencarian subquery dapat diamati sebagai operasi yang berbeda oleh pengamat yang bersamaan , sehingga melanggar perilaku 'atomik' dari pernyataan tersebut. Kecuali jika tindakan pencegahan khusus diambil, beberapa utas dapat mencoba memasukkan nilai yang sama, keduanya yakin bahwa mereka telah memeriksa dan nilainya belum ada. Hanya satu yang bisa berhasil, yang lain akan terkena pelanggaran PK. QED.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bekerja dengan Data Salesforce.com di Layanan Pelaporan SQL Server

  2. Bagaimana cara memeriksa apakah ada Kendala di server Sql?

  3. SQL Server memeriksa sensitivitas huruf besar-kecil?

  4. SQL Server 2016:Buat Prosedur Tersimpan

  5. T-SQL trim   (dan karakter non-alfanumerik lainnya)