Implementasi SQL Server 2005 dari MySQL REPLACE INTO?

87

MySQL memiliki REPLACE INTOPerintah SQL yang sangat berguna namun eksklusif ini .

Dapatkah ini dengan mudah ditiru di SQL Server 2005?

Memulai Transaksi baru, melakukan Select()dan kemudian salah satu UPDATEatau INSERTdan COMMITselalu sedikit merepotkan, terutama saat melakukannya dalam aplikasi dan karena itu selalu menyimpan 2 versi pernyataan.

Saya ingin tahu apakah ada cara yang mudah dan universal untuk mengimplementasikan fungsi seperti itu ke dalam SQL Server 2005?

Michael Stum
sumber

Jawaban:

60

Ini adalah sesuatu yang mengganggu saya tentang MSSQL ( kata-kata kasar di blog saya ). Saya berharap MSSQL didukung upsert.

Kode @ Dillie-O adalah cara yang baik dalam versi SQL yang lebih lama (+1 suara), tetapi pada dasarnya masih dua operasi IO ( existsdan kemudian updateatau insert)

Ada cara yang sedikit lebih baik di posting ini , pada dasarnya:

--try an update
update tablename 
set field1 = 'new value',
    field2 = 'different value',
    ...
where idfield = 7

--insert if failed
if @@rowcount = 0 and @@error = 0
    insert into tablename 
           ( idfield, field1, field2, ... )
    values ( 7, 'value one', 'another value', ... )

Ini menguranginya menjadi satu operasi IO jika itu pembaruan, atau dua jika disisipkan.

MS Sql2008 memperkenalkan mergedari standar SQL: 2003:

merge tablename as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

Sekarang ini benar-benar hanya satu operasi IO, tetapi kode yang mengerikan :-(

Keith
sumber
Terima kasih banyak! Menyimpan Select dan seringkali bahkan tidak memerlukan tindakan dalam situasi di mana saya dapat yakin bahwa antara Pembaruan dan sisipan "saya", tidak ada sisipan lain untuk kunci itu.
Michael Stum
2
@Michael Anda sebaiknya memiliki indeks unik di tabel ini dan menangani kesalahan kunci duplikat jika Anda akan menggunakan solusi ini.
Sam Saffron
3
@Ke Pernyataan penggabungan Anda tidak berfungsi. MERGEtidak mendukung WHEREklausa tersebut, Anda harus menulis ulang dengan menggunakan USINGdan ON. Selain itu, kecuali Anda menambahkan WITH (HOLDLOCK), ada balapan dan INSERTmungkin terjadi bersamaan , dengan salah satu dari mereka gagal karena benturan kunci.
Evgeniy Berezovsky
Ya, seperti yang ditunjukkan di sini: weblogs.sqlteam.com/dang/archive/2009/01/31/… MERGE tidak bersifat atomik. Ini mengambil kunci pembaruan implisit, tetapi melepaskannya sebelum melakukan penyisipan, yang menyebabkan kondisi balapan yang dapat mengakibatkan pelanggaran kunci utama. Anda harus menggunakan HOLDLOCK eksplisit selain UPDLOCK implisit agar operasi menjadi atomic. Seperti berdiri, itu tidak atom, meskipun tampak seperti satu pernyataan.
Triynko
1
Sintaks MERGE salah dan diperbaiki dalam jawaban yang lebih baru dari penulis yang sama: stackoverflow.com/a/243670/24472
Larry
21

Fungsionalitas yang Anda cari biasanya disebut UPSERT. Setidaknya mengetahui apa namanya dapat membantu Anda menemukan apa yang Anda cari.

Saya tidak berpikir SQL Server 2005 memiliki cara yang bagus untuk melakukan ini. 2008 memperkenalkan pernyataan MERGE yang dapat digunakan untuk melakukannya seperti yang ditunjukkan di: http://www.databasejournal.com/features/mssql/article.php/3739131 atau http://blogs.conchango.com/davidportas/archive/ 2007/11/14 / SQL-Server-2008-MERGE.aspx

Merge tersedia dalam versi beta tahun 2005, tetapi mereka menghapusnya di rilis final.

Karl Seguin
sumber
18

Apa yang dilakukan upsert / merge adalah sesuatu yang mempengaruhi ...

IF EXISTS (SELECT * FROM [Table] WHERE Id = X)
   UPDATE [Table] SET...
ELSE
   INSERT INTO [Table]

Jadi semoga kombinasi artikel-artikel itu dan pseudo code ini bisa menggerakkan semuanya.

Dillie-O
sumber
10

Saya menulis posting blog tentang masalah ini.

Intinya adalah jika Anda menginginkan pembaruan murah dan ingin aman untuk penggunaan bersamaan, coba:

update t
set hitCount = hitCount + 1
where pk = @id

if @@rowcount < 1 
begin 
   begin tran
      update t with (serializable)
      set hitCount = hitCount + 1
      where pk = @id
      if @@rowcount = 0
      begin
         insert t (pk, hitCount)
         values (@id,1)
      end
   commit tran
end

Dengan cara ini Anda memiliki 1 operasi untuk pembaruan dan maksimal 3 operasi untuk penyisipan. Jadi, jika Anda biasanya memperbarui, ini adalah opsi murah yang aman.

Saya juga akan sangat berhati-hati untuk tidak menggunakan apa pun yang tidak aman untuk penggunaan bersamaan. Sangat mudah untuk mendapatkan pelanggaran kunci primer atau baris duplikat dalam produksi.

Sam Saffron
sumber