Solusi untuk INSERT atau UPDATE di SQL Server

599

Asumsikan struktur tabel MyTable(KEY, datafield1, datafield2...).

Seringkali saya ingin memperbarui catatan yang ada, atau menyisipkan catatan baru jika tidak ada.

Pada dasarnya:

IF (key exists)
  run update command
ELSE
  run insert command

Apa cara berkinerja terbaik untuk menulis ini?

Chris Cudmore
sumber
28
Bagi siapa pun yang menemukan pertanyaan ini untuk pertama kalinya - pastikan untuk membaca semua jawaban dan komentar mereka. Usia kadang-kadang dapat mengarah pada informasi yang menyesatkan ...
Aaron Bertrand
1
Pertimbangkan untuk menggunakan operator KECUALI, yang diperkenalkan pada SQL Server 2005.
Tarzan

Jawaban:

370

jangan lupa tentang transaksi. Performa bagus, tetapi pendekatan sederhana (JIKA ADA ..) sangat berbahaya.
Ketika beberapa utas akan mencoba melakukan Sisipkan atau perbarui, Anda dapat dengan mudah mendapatkan pelanggaran kunci utama.

Solusi yang disediakan oleh @Beau Crawford & @Esteban menunjukkan gagasan umum tetapi rawan kesalahan.

Untuk menghindari kebuntuan dan pelanggaran PK, Anda dapat menggunakan sesuatu seperti ini:

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert into table (key, ...)
   values (@key, ...)
end
commit tran

atau

begin tran
   update table with (serializable) set ...
   where key = @key

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran
aku
sumber
1
Pertanyaan yang diajukan untuk sebagian besar solusi berkinerja daripada yang paling aman. Sementara transaksi menambah keamanan pada proses, itu juga menambahkan overhead.
Luke Bennett
31
Kedua metode ini masih bisa gagal. Jika dua utas bersamaan melakukan hal yang sama pada baris yang sama, yang pertama akan berhasil, tetapi sisipan kedua akan gagal karena pelanggaran kunci utama. Transaksi tidak menjamin bahwa sisipan akan berhasil bahkan jika pembaruan gagal karena catatan ada. Untuk menjamin bahwa sejumlah transaksi bersamaan akan berhasil, Anda HARUS menggunakan kunci.
Jean Vincent
7
@ apakah alasan Anda menggunakan petunjuk tabel ("with (xxxx)") sebagai lawan dari "SET TINGKAT ISOLASI TRANSAKSI YANG DAPAT DIPERCERASIKAN" tepat sebelum BEGIN TRAN Anda?
EBarr
4
@CashCow, kemenangan terakhir, inilah yang harus dilakukan INSERT atau UPDATE: yang pertama disisipkan, yang kedua memperbarui catatan. Menambahkan kunci memungkinkan ini terjadi dalam kerangka waktu yang sangat singkat, mencegah kesalahan.
Jean Vincent
1
Saya selalu berpikir menggunakan petunjuk penguncian buruk, dan kita harus membiarkan mesin Microsoft Internal mendikte kunci. Apakah ini pengecualian yang jelas untuk aturan?
382

Lihat jawaban terinci saya untuk pertanyaan sebelumnya yang sangat mirip

@Beau Crawford's adalah cara yang baik dalam SQL 2005 dan di bawah ini, meskipun jika Anda memberikan perwakilan, ia harus pergi ke orang pertama yang SO . Satu-satunya masalah adalah bahwa untuk menyisipkan masih dua operasi IO.

MS Sql2008 memperkenalkan mergedari standar SQL: 2003:

merge tablename with(HOLDLOCK) 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 benar-benar hanya satu operasi IO, tetapi kode mengerikan :-(

Keith
sumber
10
@Ian Boyd - yeah, itulah sintaks standar SQL: 2003, bukan upserthanya tentang semua penyedia DB lainnya yang memutuskan untuk mendukungnya. The upsertsintaks adalah cara yang jauh lebih baik untuk melakukan hal ini, sehingga setidaknya MS harus didukung juga - itu tidak seperti itu satu-satunya kunci non standar di T-SQL
Keith
1
ada komentar pada petunjuk kunci dalam jawaban lain? (akan mengetahuinya segera, tetapi jika itu cara yang disarankan, saya sarankan menambahkannya pada jawaban)
eglasius
25
Lihat di sini weblogs.sqlteam.com/dang/archive/2009/01/31/… untuk jawaban tentang cara mencegah kondisi balapan dari menyebabkan kesalahan yang dapat terjadi bahkan ketika menggunakan MERGEsintaksis.
Seph
5
@ Se, itu kejutan nyata - agak gagal oleh Microsoft di sana: -Saya kira itu berarti Anda memerlukan HOLDLOCKoperasi penggabungan dalam situasi konkurensi tinggi.
Keith
11
Jawaban ini benar-benar perlu diperbarui untuk memperhitungkan komentar Seph tentang hal itu tidak aman tanpa HOLDLOCK. Menurut posting yang ditautkan, MERGE secara implisit mengeluarkan kunci pembaruan, tetapi melepaskannya sebelum memasukkan baris, yang dapat menyebabkan kondisi balapan dan pelanggaran kunci utama pada insert. Dengan menggunakan HOLDLOCK, kunci disimpan sampai setelah memasukkan terjadi.
Triynko
169

Lakukan UPSERT:

UPDATE MyTable SET FieldA = @ FieldA WHERE Key = @ Key

IF @@ ROWCOUNT = 0
   INSERT INTO MyTable (FieldA) VALUES (@FieldA)

http://en.wikipedia.org/wiki/Upsert

Beau Crawford
sumber
7
Pelanggaran kunci primer tidak boleh terjadi jika Anda menerapkan batasan indeks unik yang tepat. Inti dari kendala adalah untuk mencegah duplikat baris dari setiap kejadian. Tidak peduli berapa banyak utas yang mencoba untuk disisipkan, basis data akan membuat serial yang diperlukan untuk menegakkan batasan ... dan jika tidak, maka mesin tidak berharga. Tentu saja, membungkus ini dalam transaksi serial akan membuatnya lebih benar dan kurang rentan terhadap kebuntuan atau gagal memasukkan.
Triynko
19
@Triynko, saya pikir @Sam Saffron berarti bahwa jika dua thread + interleave dalam urutan yang benar maka server sql akan melempar kesalahan yang mengindikasikan pelanggaran kunci primer akan terjadi. Membungkusnya dalam transaksi serializable adalah cara yang benar untuk mencegah kesalahan dalam set pernyataan di atas.
EBarr
1
Bahkan jika Anda memiliki kunci utama yang merupakan peningkatan otomatis, kekhawatiran Anda akan menjadi kendala unik yang mungkin ada di atas meja.
Seph
1
database harus menangani masalah utama. Apa yang Anda katakan adalah bahwa jika pembaruan gagal dan proses lain sampai di sana terlebih dahulu dengan menyisipkan, insert Anda akan gagal. Jika demikian, Anda tetap memiliki kondisi balapan. Mengunci tidak akan mengubah fakta bahwa kondisi setelahnya adalah bahwa salah satu proses yang mencoba menulis akan mendapatkan nilai.
CashCow
93

Banyak orang akan menyarankan Anda menggunakannya MERGE, tetapi saya memperingatkan Anda untuk tidak menggunakannya. Secara default, itu tidak melindungi Anda dari kondisi konkurensi dan ras lebih dari beberapa pernyataan, dan itu memperkenalkan bahaya lain:

http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/

Bahkan dengan sintaks "sederhana" ini yang tersedia, saya masih lebih suka pendekatan ini (penanganan kesalahan dihilangkan untuk singkatnya):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
  INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;

Banyak orang akan menyarankan cara ini:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
  UPDATE ...
END
ELSE
  INSERT ...
END
COMMIT TRANSACTION;

Tetapi semua ini dicapai memastikan Anda mungkin perlu membaca tabel dua kali untuk menemukan baris yang akan diperbarui. Dalam sampel pertama, Anda hanya perlu menemukan baris sekali saja. (Dalam kedua kasus, jika tidak ada baris yang ditemukan dari pembacaan awal, terjadi penyisipan.)

Orang lain akan menyarankan cara ini:

BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

Namun, ini bermasalah jika tidak ada alasan lain selain membiarkan SQL Server menangkap pengecualian yang bisa Anda cegah sejak awal jauh lebih mahal, kecuali dalam skenario langka di mana hampir setiap insert gagal. Saya membuktikan banyak hal di sini:

Aaron Bertrand
sumber
3
Bagaimana dengan memasukkan / memperbarui DARI tabel tem yang menyisipkan / memperbarui banyak catatan?
user960567
@ user960567 Nah,UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Aaron Bertrand
4
nice
reply
12
@ user960567 Maaf, saya tidak selalu menangkap pemberitahuan komentar secara real time.
Aaron Bertrand
60
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

Edit:

Sayangnya, bahkan merugikan saya sendiri, saya harus mengakui solusi yang melakukan ini tanpa memilih tampaknya lebih baik karena mereka menyelesaikan tugas dengan satu langkah lebih sedikit.

Esteban Araya
sumber
6
Saya masih lebih suka yang ini. Upert tampaknya lebih seperti pemrograman dengan efek samping, dan saya belum pernah melihat pencarian indeks kecil berkerut dari seleksi awal yang menyebabkan masalah kinerja dalam database nyata.
Eric Z Beard
38

Jika Anda ingin UPSERT lebih dari satu catatan pada suatu waktu Anda dapat menggunakan ANSI SQL: 2003 DML pernyataan MERGE.

MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])

Lihat Meniru Pernyataan MERGE di SQL Server 2005 .

Eric Weilnau
sumber
1
Di Oracle, mengeluarkan pernyataan MERGE saya pikir mengunci meja. Apakah hal yang sama terjadi pada SQL * Server?
Mike McAllister
13
MERGE rentan terhadap kondisi lomba (lihat weblogs.sqlteam.com/dang/archive/2009/01/31/… ) kecuali Anda membuatnya memegang kunci sertifikasi. Juga, lihat kinerja MERGE di SQL Profiler ... saya menemukan bahwa ini lebih lambat dan menghasilkan lebih banyak bacaan daripada solusi alternatif.
EBarr
@EBarr - Terima kasih atas tautan di kunci. Saya telah memperbarui jawaban saya untuk memasukkan saran penguncian yang disarankan.
Eric Weilnau
Lihat juga mssqltips.com/sqlservertip/3074/…
Aaron Bertrand
10

Meskipun cukup terlambat untuk mengomentari ini, saya ingin menambahkan contoh yang lebih lengkap menggunakan MERGE.

Pernyataan Insert + Update tersebut biasanya disebut pernyataan "Upsert" dan dapat diimplementasikan menggunakan MERGE di SQL Server.

Contoh yang sangat baik diberikan di sini: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

Di atas juga menjelaskan skenario penguncian dan konkurensi.

Saya akan mengutip yang sama untuk referensi:

ALTER PROCEDURE dbo.Merge_Foo2
      @ID int
AS

SET NOCOUNT, XACT_ABORT ON;

MERGE dbo.Foo2 WITH (HOLDLOCK) AS f
USING (SELECT @ID AS ID) AS new_foo
      ON f.ID = new_foo.ID
WHEN MATCHED THEN
    UPDATE
            SET f.UpdateSpid = @@SPID,
            UpdateTime = SYSDATETIME()
WHEN NOT MATCHED THEN
    INSERT
      (
            ID,
            InsertSpid,
            InsertTime
      )
    VALUES
      (
            new_foo.ID,
            @@SPID,
            SYSDATETIME()
      );

RETURN @@ERROR;
pengguna243131
sumber
1
Ada hal-hal lain yang perlu dikhawatirkan dengan MERGE: mssqltips.com/sqlservertip/3074/…
Aaron Bertrand
8
/*
CREATE TABLE ApplicationsDesSocietes (
   id                   INT IDENTITY(0,1)    NOT NULL,
   applicationId        INT                  NOT NULL,
   societeId            INT                  NOT NULL,
   suppression          BIT                  NULL,
   CONSTRAINT PK_APPLICATIONSDESSOCIETES PRIMARY KEY (id)
)
GO
--*/

DECLARE @applicationId INT = 81, @societeId INT = 43, @suppression BIT = 0

MERGE dbo.ApplicationsDesSocietes WITH (HOLDLOCK) AS target
--set the SOURCE table one row
USING (VALUES (@applicationId, @societeId, @suppression))
    AS source (applicationId, societeId, suppression)
    --here goes the ON join condition
    ON target.applicationId = source.applicationId and target.societeId = source.societeId
WHEN MATCHED THEN
    UPDATE
    --place your list of SET here
    SET target.suppression = source.suppression
WHEN NOT MATCHED THEN
    --insert a new line with the SOURCE table one row
    INSERT (applicationId, societeId, suppression)
    VALUES (source.applicationId, source.societeId, source.suppression);
GO

Ganti nama tabel dan bidang dengan apa pun yang Anda butuhkan. Jaga kondisi penggunaan ON . Kemudian atur nilai yang sesuai (dan tipe) untuk variabel-variabel pada baris DECLARE.

Bersulang.

Denver
sumber
7

Anda dapat menggunakan MERGEPernyataan, Pernyataan ini digunakan untuk memasukkan data jika tidak ada atau memperbarui jika memang ada.

MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`
Daniel Acosta
sumber
@RamenChef saya tidak mengerti. Di mana klausa KAPAN TERTUTUP?
likejudo
@likejudo Saya tidak menulis ini; Saya hanya merevisinya. Tanyakan pada pengguna yang menulis posting.
RamenChef
5

Jika melakukan UPDATE jika-tanpa-baris-diperbarui maka rute INSERT, pertimbangkan melakukan INSERT terlebih dahulu untuk mencegah kondisi balapan (dengan asumsi tidak ada HAPUS intervensi)

INSERT INTO MyTable (Key, FieldA)
   SELECT @Key, @FieldA
   WHERE NOT EXISTS
   (
       SELECT *
       FROM  MyTable
       WHERE Key = @Key
   )
IF @@ROWCOUNT = 0
BEGIN
   UPDATE MyTable
   SET FieldA=@FieldA
   WHERE Key=@Key
   IF @@ROWCOUNT = 0
   ... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END

Selain menghindari kondisi balapan, jika dalam kebanyakan kasus catatan sudah ada maka ini akan menyebabkan INSERT gagal, membuang-buang CPU.

Menggunakan MERGE mungkin lebih baik untuk SQL2008 dan seterusnya.

Kristen
sumber
Ide yang menarik, tetapi salah sintaks. SELECT membutuhkan FROM <table_source>, dan TOP 1 (kecuali table_source yang dipilih hanya memiliki 1 baris).
jk7
Terima kasih. Saya telah mengubahnya menjadi TIDAK ADA. Hanya akan ada satu baris yang cocok karena tes untuk "kunci" sesuai O / P (meskipun itu mungkin harus menjadi kunci multi-bagian :))
Kristen
4

Itu tergantung pada pola penggunaan. Kita harus melihat penggunaan gambaran besar tanpa tersesat dalam detailnya. Misalnya, jika pola penggunaannya adalah pembaruan 99% setelah catatan dibuat, maka 'UPSERT' adalah solusi terbaik.

Setelah penyisipan pertama (klik), itu akan menjadi semua pembaruan pernyataan tunggal, tidak ada ifs atau buts. Kondisi 'di mana' pada sisipan diperlukan jika tidak akan memasukkan duplikat, dan Anda tidak ingin berurusan dengan penguncian.

UPDATE <tableName> SET <field>=@field WHERE key=@key;

IF @@ROWCOUNT = 0
BEGIN
   INSERT INTO <tableName> (field)
   SELECT @field
   WHERE NOT EXISTS (select * from tableName where key = @key);
END
Saleh Najar
sumber
2

MS SQL Server 2008 memperkenalkan pernyataan MERGE, yang saya percaya merupakan bagian dari standar SQL: 2003. Seperti yang telah ditunjukkan oleh banyak orang, ini bukan masalah besar untuk menangani kasus satu baris, tetapi ketika berhadapan dengan dataset besar, kita membutuhkan kursor, dengan semua masalah kinerja yang muncul. Pernyataan MERGE akan disambut lebih baik ketika berhadapan dengan dataset besar.

bjorsig
sumber
1
Saya tidak pernah perlu menggunakan kursor untuk melakukan ini dengan kumpulan data besar. Anda hanya perlu pembaruan yang memperbarui catatan yang cocok dan sisipan dengan pilih bukannya klausa nilai yang tersisa bergabung ke tabel.
HLGEM
1

Sebelum semua orang melompat ke HOLDLOCK-s karena takut dari para pengguna yang susah payah ini menjalankan sprocs Anda secara langsung :-) izinkan saya menunjukkan bahwa Anda harus menjamin keunikan PK-s baru dengan desain (kunci identitas, generator urutan di Oracle, indeks unik untuk ID-s eksternal, kueri yang dicakup oleh indeks). Itulah alfa dan omega dari masalah ini. Jika Anda tidak memilikinya, tidak ada HOLDLOCK-s dari jagat raya yang akan menyelamatkan Anda dan jika Anda memilikinya, maka Anda tidak memerlukan apapun selain UPDLOCK pada pilihan pertama (atau untuk menggunakan pembaruan terlebih dahulu).

Sprocs biasanya berjalan dalam kondisi yang sangat terkendali dan dengan asumsi penelepon tepercaya (mid tier). Berarti bahwa jika pola sederhana sederhana (perbarui + sisipkan atau gabungkan) pernah melihat duplikat PK yang berarti bug pada desain tingkat menengah atau tabel Anda dan ada baiknya SQL akan berteriak kesalahan dalam kasus tersebut dan menolak catatan. Menempatkan HOLDLOCK dalam kasus ini sama dengan pengecualian makan dan mengambil data yang berpotensi salah, selain mengurangi kinerja Anda.

Karena itu, Menggunakan MERGE, atau UPDATE maka INSERT lebih mudah di server Anda dan lebih sedikit kesalahan karena Anda tidak harus ingat untuk menambahkan (UPDLOCK) untuk memilih pertama. Juga, jika Anda melakukan sisipan / pembaruan dalam batch kecil Anda perlu mengetahui data Anda untuk memutuskan apakah suatu transaksi sesuai atau tidak. Itu hanya kumpulan catatan yang tidak terkait maka transaksi "membungkus" tambahan akan merugikan.

ZXX
sumber
1
Jika Anda hanya melakukan pembaruan lalu menyisipkan tanpa penguncian atau isolasi yang ditinggikan, maka dua pengguna dapat mencoba untuk mengirimkan kembali data yang sama (Saya tidak akan menganggapnya sebagai bug di tingkat menengah jika dua pengguna mencoba mengirimkan informasi yang sama persis di saat yang sama - sangat tergantung pada konteks, bukan?). Keduanya masuk ke pembaruan, yang mengembalikan 0 baris untuk keduanya, kemudian keduanya mencoba untuk menyisipkan. Satu menang, yang lain mendapat pengecualian. Inilah yang biasanya orang coba hindari.
Aaron Bertrand
1

Apakah kondisi perlombaan benar-benar penting jika Anda pertama kali mencoba pembaruan diikuti dengan sisipan? Katakanlah Anda memiliki dua utas yang ingin menetapkan nilai untuk kunci kunci :

Thread 1: value = 1
Thread 2: value = 2

Contoh skenario kondisi balapan

  1. kunci tidak ditentukan
  2. Thread 1 gagal dengan pembaruan
  3. Thread 2 gagal dengan pembaruan
  4. Salah satu utas 1 atau utas 2 berhasil dengan sisipan. Misal utas 1
  5. Utas lainnya gagal dengan sisipan (dengan kunci duplikat kesalahan) - utas 2.

    • Hasil: "pertama" dari dua tapak yang akan disisipkan, menentukan nilai.
    • Hasil yang diinginkan: Yang terakhir dari 2 utas untuk menulis data (perbarui atau masukkan) harus menentukan nilai

Tapi; dalam lingkungan multithreaded, scheduler OS memutuskan urutan eksekusi thread - dalam skenario di atas, di mana kita memiliki kondisi balapan ini, itu adalah OS yang memutuskan urutan eksekusi. Yaitu: Adalah salah untuk mengatakan bahwa "utas 1" atau "utas 2" adalah "pertama" dari sudut pandang sistem.

Ketika waktu eksekusi sangat dekat untuk utas 1 dan utas 2, hasil dari kondisi balapan tidak masalah. Satu-satunya persyaratan adalah salah satu utas harus menentukan nilai yang dihasilkan.

Untuk implementasi: Jika pembaruan diikuti dengan memasukkan hasil kesalahan "kunci duplikat", ini harus dianggap berhasil.

Juga, orang tentu saja tidak boleh berasumsi bahwa nilai dalam database sama dengan nilai yang Anda tulis terakhir.

runec
sumber
1

Di SQL Server 2008 Anda bisa menggunakan pernyataan MERGE

Bart
sumber
11
ini adalah komentar. dengan tidak adanya kode contoh aktual ini sama seperti banyak komentar lain di situs.
swasheck
Sangat tua, tetapi sebuah contoh akan menyenangkan.
Matt McCabe
0

Saya telah mencoba solusi di bawah ini dan berfungsi untuk saya, ketika permintaan bersamaan untuk memasukkan pernyataan terjadi.

begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
   update table set ...
   where key = @key
end
else
begin
   insert table (key, ...)
   values (@key, ...)
end
commit tran
Dev
sumber
0

Anda dapat menggunakan kueri ini. Bekerja di semua edisi SQL Server. Sederhana, dan jelas. Tetapi Anda perlu menggunakan 2 pertanyaan. Anda dapat menggunakan jika Anda tidak dapat menggunakan MERGE

    BEGIN TRAN

    UPDATE table
    SET Id = @ID, Description = @Description
    WHERE Id = @Id

    INSERT INTO table(Id, Description)
    SELECT @Id, @Description
    WHERE NOT EXISTS (SELECT NULL FROM table WHERE Id = @Id)

    COMMIT TRAN

CATATAN: Tolong jelaskan jawaban negatif

Victor Sanchez
sumber
Saya kira kurang mengunci?
Zeek2
Tidak ada kekurangan mengunci ... Saya menggunakan "TRAN". Transaksi sql-server default telah dikunci.
Victor Sanchez
-2

Jika Anda menggunakan ADO.NET, DataAdapter menangani ini.

Jika Anda ingin menanganinya sendiri, inilah caranya:

Pastikan ada batasan kunci utama pada kolom kunci Anda.

Maka kamu:

  1. Lakukan pembaruan
  2. Jika pembaruan gagal karena catatan dengan kunci sudah ada, lakukan memasukkan. Jika pembaruan tidak gagal, Anda selesai.

Anda juga dapat melakukannya dengan cara lain, yaitu melakukan penyisipan terlebih dahulu, dan melakukan pembaruan jika penyisipan gagal. Biasanya cara pertama lebih baik, karena pembaruan dilakukan lebih sering daripada menyisipkan.

nruessmann
sumber
... dan melakukan penyisipan terlebih dahulu (mengetahui bahwa kadang-kadang gagal) mahal untuk SQL Server. sqlperformance.com/2012/08/t-sql-queries/error-handling
Aaron Bertrand
-3

Melakukan jika ada ... yang lain ... melibatkan melakukan dua permintaan minimum (satu untuk memeriksa, satu untuk mengambil tindakan). Pendekatan berikut ini hanya membutuhkan satu di mana catatan itu ada, dua jika memasukkan diperlukan:

DECLARE @RowExists bit
SET @RowExists = 0
UPDATE MyTable SET DataField1 = 'xxx', @RowExists = 1 WHERE Key = 123
IF @RowExists = 0
  INSERT INTO MyTable (Key, DataField1) VALUES (123, 'xxx')
Luke Bennett
sumber
-3

Saya biasanya melakukan apa yang dikatakan oleh beberapa poster lain sehubungan dengan memeriksa keberadaannya terlebih dahulu dan kemudian melakukan apa pun jalur yang benar. Satu hal yang harus Anda ingat ketika melakukan ini adalah bahwa rencana eksekusi yang di-cache oleh sql bisa menjadi tidak optimal untuk satu jalur atau yang lainnya. Saya percaya cara terbaik untuk melakukan ini adalah memanggil dua prosedur tersimpan yang berbeda.

FirstSP:
Jika ada
   Panggil SecondSP (UpdateProc)
Lain
   Panggil ThirdSP (InsertProc)

Sekarang, saya tidak terlalu sering mengikuti saran saya sendiri, jadi ambillah dengan sebutir garam.

Micky McQuade
sumber
Ini mungkin relevan dalam versi kuno SQL Server, tetapi versi modern memiliki kompilasi tingkat pernyataan. Garpu dll bukanlah masalah, dan menggunakan prosedur terpisah untuk hal-hal ini tidak menyelesaikan masalah yang melekat dalam membuat pilihan antara pembaruan dan sisipan ...
Aaron Bertrand
-10

Lakukan pilih, jika Anda mendapatkan hasil, perbarui, jika tidak, buatlah.

Clint Ecker
sumber
3
Itu dua panggilan ke database.
Chris Cudmore
3
Saya tidak melihat masalah dengan itu.
Clint Ecker
10
Dua panggilan ke DB itulah masalahnya, Anda berakhir dengan menggandakan jumlah pulang pergi ke DB. Jika aplikasi menyentuh db dengan banyak sisipan / pembaruan, itu akan merusak kinerja. UPSERT adalah strategi yang lebih baik.
Kev
5
itu juga menciptakan kondisi balapan bukan?
niico