SQL Server'da INSERT VEYA UPDATE için çözümler


598

Bir tablo yapısını varsayalım MyTable(KEY, datafield1, datafield2...).

Genellikle varolan bir kaydı güncellemek veya yoksa yeni bir kayıt eklemek istiyorum.

esasen:

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

Bunu yazmanın en iyi yolu nedir?



27
Bu soruya ilk kez gelen herkes için - lütfen tüm cevapları ve yorumlarını okuduğunuzdan emin olun. Yaş bazen yanıltıcı bilgilere yol açabilir ...
Aaron Bertrand

1
SQL Server 2005'te tanıtılan EXCEPT operatörünü kullanmayı düşünün.
Tarzan

Yanıtlar:


370

işlemleri unutma. Performans iyidir, ancak basit (EĞER VARSA) yaklaşım çok tehlikelidir.
Birden çok iş parçacığı Ekle veya güncellemeyi gerçekleştirmeye çalıştığında kolayca birincil anahtar ihlali alabilirsiniz.

@Beau Crawford & @Ebeban tarafından sağlanan çözümler genel fikir gösterir, ancak hataya açıktır.

Kilitlenmeleri ve PK ihlallerini önlemek için şöyle bir şey kullanabilirsiniz:

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

veya

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

   if @@rowcount = 0
   begin
      insert into table (key, ...) values (@key,..)
   end
commit tran

1
Soru, en güvenli yerine en performanslı çözümü istedi. Bir işlem sürece güvenlik eklerken, ek bir yük de ekler.
Luke Bennett

31
Her iki yöntem de başarısız olabilir. İki eşzamanlı iş parçacığı aynı satırda aynısını yaparsa, birincisi başarılı olur, ancak birincil anahtar ihlali nedeniyle ikinci ekleme başarısız olur. Bir işlem, kayıt olduğu için güncelleme başarısız olsa bile eklemenin başarılı olacağını garanti etmez. Herhangi bir sayıda eşzamanlı işlemin başarılı olacağını garanti etmek için bir kilit kullanmanız GEREKİR.
Jean Vincent

7
@aku, "BAŞLANGIÇ İZOLASYON DÜZEYİNİ SERİLEŞTİRMENİ BAŞLAT" ın tersine tablo ipuçlarını ("ile (xxxx)") kullanmanızın herhangi bir nedeni var mı?
EBarr

4
@CashCow, son kazanır, INSERT veya UPDATE'in yapması gereken budur: birincisi ekler, ikincisi kaydı günceller. Bir kilit eklemek, bunun çok kısa bir zaman dilimi içinde olmasına izin vererek bir hatayı önler.
Jean Vincent

1
Her zaman kilitleme ipuçlarının kullanımının kötü olduğunu düşündüm ve Microsoft Internal engine'in kilitleri dikte etmesine izin vermeliyiz. Bu kuralın bariz istisnası mı?

381

Çok benzer bir önceki soruya verilen ayrıntılı cevabımı görün

@Beau Crawford's , SQL 2005 ve altında iyi bir yoldur, ancak rep veriyorsanız SO'ya ilk adama gitmelidir . Tek sorun, kesici uçlar için hala iki IO işlemi olmasıdır.

MS Sql2008 merge, SQL: 2003 standardından tanıtır :

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, ... )

Şimdi sadece bir IO operasyonu, ama korkunç bir kod :-(


10
@Ian Boyd - evet, bu SQL: 2003 standardının sözdizimi, upsertdiğer tüm DB sağlayıcılarının desteklemeye karar vermesi değil. upsertO T-SQL olmayan tek standart anahtar kelime gibi o değil - sözdizimi yüzden çok az MS anda çok destekledi gerekirdi, bunu yapmanın bir çok güzel yoludur
Keith

1
diğer cevaplarda kilit ipucu hakkında herhangi bir yorum? (yakında öğrenecek, ancak önerilen
yolsa

25
Sözdizimi kullanılırken bile, yarış koşullarının hatalara neden olmasını nasıl önleyeceğiniz konusunda yanıt için weblogs.sqlteam.com/dang/archive/2009/01/31/… adresine bakın MERGE.
Seph

5
@Seph bu gerçek bir sürpriz - biraz Microsoft tarafından başarısız oldu: -SI tahmin bu HOLDLOCKyüksek eşzamanlılık durumlarda birleştirme işlemleri için bir ihtiyaç anlamına gelir .
Keith

11
Bu cevabın Seph tarafından HOLDLOCK olmadan iş parçacığı açısından güvenli olmadığına ilişkin yorumu dikkate almak için gerçekten güncellenmesi gerekiyor. Bağlantılı gönderiye göre, MERGE örtülü olarak bir güncelleme kilidi çıkarır, ancak satır eklemeden önce serbest bırakır, bu da bir yarış durumuna ve ekleme sırasında birincil anahtar ihlallerine neden olabilir. HOLDLOCK kullanılarak kilitler kesici uç oluşana kadar tutulur.
Triynko

169

Bir UPSERT yapın:

UPTATE MyTable SET FieldA = @ FieldA NEREDE Anahtar = @ Anahtar

@@ ROWCOUNT = 0 olursa
   MyTable (FieldA) DEĞERLERİNE EKLE (@FieldA)

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


7
Uygun benzersiz dizin kısıtlamaları uygulandıysa birincil anahtar ihlalleri oluşmamalıdır. Kısıtlamanın bütün amacı, yinelenen satırların her olmasını engellemektir. Kaç tane iş parçacığı eklemeye çalıştığı önemli değil, veritabanı kısıtlamayı uygulamak için gerektiği şekilde serileştirecek ... ve eğer değilse, motor değersizdir. Elbette, bunu serileştirilmiş bir işlemde sarmak, bunu daha doğru ve kilitlenmelere veya başarısız eklere karşı daha az duyarlı hale getirecektir.
Triynko

19
@Triynko, ben @Sam Safran iki + ipler doğru sırayla serpiştirilmesi o zaman sql server olacak anlamına geliyordu düşünüyorum atmak birincil anahtar ihlali belirten bir hata olurdu oluştu. Serileştirilebilir bir işlemde sarmak, yukarıdaki ifade kümesindeki hataları önlemenin doğru yoludur.
EBarr

1
Otomatik artışlı bir birincil anahtarınız olsa bile, endişeniz tablodaki benzersiz kısıtlamalar olacaktır.
Seph

1
veritabanı birincil anahtar konulara dikkat etmelidir. Söylediğiniz şey, güncelleme başarısız olursa ve başka bir işlem ilk önce bir insert ile oraya girerse, insertinizin başarısız olacağıdır. Bu durumda zaten bir yarış durumunuz var. Kilitleme, post-koşulun yazmaya çalışan süreçlerden birinin değeri alacağı gerçeğini değiştirmez.
CashCow

93

Birçok kişi kullanmanızı önerir MERGE, ama ben buna karşı uyarıyorum. Varsayılan olarak, sizi birden fazla ifadeden daha fazla eşzamanlılık ve yarış koşullarından korumaz ve diğer tehlikeleri ortaya çıkarır:

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

Bu "daha basit" sözdizimi ile bile, hala bu yaklaşımı tercih ediyorum (kısalık için hata işleme atlandı):

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;

Birçok kişi şu şekilde önerecektir:

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;

Ancak tüm bunlar, güncellenecek satır (lar) ı bulmak için tabloyu iki kez okumanız gerekebileceğinden emin olmaktır. İlk örnekte, satır (lar) ı yalnızca bir kez bulmanız gerekecektir. (Her iki durumda da, ilk okumada hiç satır bulunmazsa, bir ekleme yapılır.)

Diğerleri şöyle önerecektir:

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

Ancak, SQL Server'ın ilk etapta önleyebileceğiniz istisnaları yakalamasına izin vermekten başka bir sebep yoksa, neredeyse her ekin başarısız olduğu nadir senaryo dışında çok daha pahalıysa bu sorunludur. Burada kanıtladığım kadarıyla:


3
Çok sayıda kayıt ekleyen / güncelleyen bir tem tablodan ekleme / güncelleme ne olacak?
user960567

@ user960567 Şey,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
2 yıldan fazla sonra güzel cevap :)
user960567

12
@ user960567 Üzgünüz, her zaman gerçek zamanlı olarak yorum bildirimleri yakalamıyorum.
Aaron Bertrand

60
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)

Düzenle:

Ne yazık ki, kendi zararım için bile, bir seçim yapmadan bunu yapan çözümleri daha iyi gibi görmeliyim, çünkü görevi daha az bir adımla gerçekleştirirler.


6
Bunu daha çok seviyorum. Upsert daha yan etki ile programlama gibi görünüyor ve adres asla piddly biraz kümelenmiş dizin gerçek veritabanında performans sorunlara neden o ilk seçme arayışı içindedirler görüldü.
Eric Z Beard

38

Aynı anda birden fazla kayıt UPSERT yapmak istiyorsanız, ANSI SQL: 2003 DML deyimini MERGE kullanabilirsiniz.

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 ...])

Check out SQL Server 2005'te Taklit MERGE Bildirimi .


1
Oracle'da, bir MERGE bildirimi yayınlama I tabloyu kilitlediğini düşünüyorum . Aynı şey SQL * Server'da da olur mu?
Mike McAllister

13
MERGE, certian kilitlerini tutmadıkça yarış koşullarına karşı hassastır (bkz. Weblogs.sqlteam.com/dang/archive/2009/01/31/… ). Ayrıca, MERGE'in SQL Profiler'deki performansına bir göz atın ... Tipik olarak daha yavaş olduğunu ve alternatif çözümlerden daha fazla okuma ürettiğini görüyorum.
EBarr

@EBarr - Kilitlerdeki bağlantı için teşekkürler. Cevabımı kilitleme ipucunu içerecek şekilde güncelledim.
Eric Weilnau


10

Bu konuda yorum yapmak için oldukça geç olmasına rağmen, MERGE kullanarak daha eksiksiz bir örnek eklemek istiyorum.

Bu Insert + Update deyimlerine genellikle "Upsert" deyimi denir ve SQL Server'da MERGE kullanılarak uygulanabilir.

Burada çok iyi bir örnek verilmiştir: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx

Yukarıda kilitleme ve eşzamanlılık senaryoları da açıklanmaktadır.

Ben referans için aynı alıntı olacak:

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;

1
MERGE ile ilgili endişelenmeniz gereken başka şeyler var: 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

Tablo ve alan adlarını ihtiyacınız olan şeylerle değiştirin. İlgilen kullanarak AÇIK durumda. Ardından DECLARE satırındaki değişkenler için uygun değeri (ve türü) ayarlayın.

Şerefe.


7

MERGEDeyimi kullanabilirsiniz , Bu ifade yoksa veri eklemek veya varsa güncellemek için kullanılır.

MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`

@RamenChef Anlamıyorum. WHEN MATCHED cümleleri nerede?
likejudo

@likejudo Bunu yazmadım; Sadece revize ettim. Gönderiyi yazan kullanıcıya sorun.
RamenChef

5

UPDATE-no-nows güncellendikten sonra INSERT yoluna gidiyorsanız, bir yarış koşulunu önlemek için önce INSERT yapmayı düşünün (araya giren DELETE olmadığını varsayarak)

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

Bir yarış koşulundan kaçınmanın yanı sıra, çoğu durumda kayıt zaten mevcutsa, bu INSERT'in başarısız olmasına ve CPU israfına neden olacaktır.

SQL2008 ve sonrasında MERGE kullanılması tercih edilebilir.


İlginç bir fikir ama yanlış sözdizimi. SELECT, bir FROM <table_source> ve bir TOP 1 (seçilen table_source içinde sadece 1 satır yoksa) gerekir.
jk7

Teşekkürler. Ben bir DEĞİL DEĞİL olarak değiştirdim. O / P'ye göre "anahtar" testi nedeniyle sadece tek bir eşleşen satır olacaktır (bunun çok parçalı bir anahtar olması gerekebilir :))
Kristen

4

Bu kullanım şekline bağlıdır. Bir detaylarda kaybolmadan büyük resme kullanım bakmak gerekir. Örneğin, kayıt oluşturulduktan sonra kullanım şekli% 99 güncellemeyse, 'UPSERT' en iyi çözümdür.

İlk eklemeden (isabet) sonra, hepsi tek deyim güncellemeleri olacak, ifs veya buts yok. Kesici uçtaki 'nerede' koşulu gereklidir, aksi takdirde kopyalar eklenir ve kilitleme ile uğraşmak istemezsiniz.

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

2

MS SQL Server 2008, SQL: 2003 standardının bir parçası olduğunu düşündüğüm MERGE deyimini sunar. Birçoğunun gösterdiği gibi, bir sıradaki vakaları ele almak büyük bir şey değildir, ancak büyük veri kümeleriyle uğraşırken, ortaya çıkan tüm performans sorunlarıyla birlikte bir imleç gerekir. Büyük veri kümeleriyle uğraşırken MERGE ifadesi memnuniyetle eklenecektir.


1
Büyük veri kümeleriyle bunu yapmak için hiç bir imleç kullanmam gerekmedi. Yalnızca eşleşen kayıtları güncelleyen bir güncelleştirmeye ve tabloya katılan bir değer yan tümcesi yerine bir seçim içeren bir eke ihtiyacınız vardır.
HLGEM

1

Herkes sprocs'larınızı doğrudan çalıştıran bu nafarious kullanıcılardan korkmadan HOLDLOCK'lara atlamadan önce :-) Yeni PK'lerin benzersizliğini tasarımla garanti etmek zorunda olduğunuzu belirtmeme izin verin (kimlik anahtarları, Oracle'daki dizi jeneratörleri, harici kimlikler, dizinler tarafından kapsanan sorgular). Sorunun alfa ve omega'sı bu. Eğer buna sahip değilseniz, evrenin HOLDLOCK'ları sizi kurtaramaz ve eğer buna sahipseniz, ilk seçimde (veya ilk önce güncellemeyi kullanmak için) UPDLOCK'un ötesinde bir şeye ihtiyacınız yoktur.

Sprocs normalde çok kontrollü koşullar altında ve güvenilir bir arayan (orta kademe) varsayımı ile çalışır. Basit bir üst kalıp deseni (güncelleme + ekleme veya birleştirme), yinelenen PK'yi görürse, orta katman veya tablo tasarımınızda bir hata anlamına gelir ve SQL'in bu durumda bir hata vermesi ve kaydı reddetmesi iyi olur. Bu durumda bir HOLDLOCK yerleştirmek yeme istisnalarına ve potansiyel olarak hatalı verileri almaya eşittir.

Bunu söyledikten sonra, ilk seçime (UPDLOCK) eklemeyi hatırlamanız gerekmediğinden, MERGE veya UPDATE kullanmak INSERT'i kullanmak sunucunuzda daha kolay ve daha az hataya açıktır. Ayrıca, küçük gruplar halinde ekler / güncellemeler yapıyorsanız, bir işlemin uygun olup olmadığına karar vermek için verilerinizi bilmeniz gerekir. Bu sadece ilgisiz kayıtların bir koleksiyonudur, o zaman ek "zarflama" işlemi zararlı olacaktır.


1
Sadece bir güncelleme yaparsanız, daha sonra herhangi bir kilitleme veya yükseltilmiş yalıtım olmadan yerleştirirseniz, iki kullanıcı aynı verileri geri aktarmayı deneyebilir (iki kullanıcı aynı bilgileri Aynı zamanda - büyük ölçüde bağlama bağlıdır, değil mi?). İkisi de her ikisi için 0 satır döndüren güncellemeye girer, sonra her ikisi de eklemeye çalışır. Biri kazanır, diğeri bir istisna alır. İnsanlar genellikle bundan kaçınmaya çalışıyor.
Aaron Bertrand

1

Önce bir güncellemeyi ve ardından bir eki denemeniz durumunda yarış koşulları gerçekten önemli mi? Diyelim ki anahtar anahtarı için bir değer ayarlamak isteyen iki iş parçanız var :

Konu 1: değer = 1
Konu 2: değer = 2

Örnek yarış koşulu senaryosu

  1. anahtar tanımlanmadı
  2. Konu 1 güncelleme ile başarısız oluyor
  3. Konu 2 güncelleme ile başarısız oluyor
  4. İplik 1 veya iplik 2'den biri kesici uç ile başarılı olur. Örn. İplik 1
  5. Diğer iş parçacığı ekleme ile başarısız oluyor (hata yinelenen anahtarla) - iş parçacığı 2.

    • Sonuç: Eklenecek iki basamaktan "ilk" değeri değerini belirler.
    • İstenen sonuç: Veri yazacak son 2 iş parçacığının sonuncusu (güncelleme veya ekleme) değere karar vermelidir

Fakat; çok iş parçacıklı bir ortamda, OS zamanlayıcı iş parçacığı yürütme sırasına karar verir - bu senaryo koşuluna sahip olduğumuz yukarıdaki senaryoda, yürütme sırasına karar veren işletim sistemiydi. Yani: "iş parçacığı 1" veya "iş parçacığı 2" nin sistem açısından "ilk" olduğunu söylemek yanlıştır.

Uygulama zamanı diş 1 ve diş 2 için çok yakın olduğunda, yarış koşulunun sonucu önemli değildir. Tek gereklilik, iş parçacıklarından birinin ortaya çıkan değeri tanımlamasıdır.

Uygulama için: Güncelleme ve ardından "yinelenen anahtar" hatasıyla sonuçlanırsa, bu başarı olarak değerlendirilmelidir.

Ayrıca, elbette ki veritabanındaki değerin en son yazdığınız değerle aynı olduğunu varsaymamalıdır.


1

SQL Server 2008'de MERGE deyimini kullanabilirsiniz


11
bu bir yorum. gerçek bir örnek kodun yokluğunda bu, sitedeki diğer pek çok yorum gibidir.
swasheck

Çok eski, ama bir örnek güzel olurdu.
Matt McCabe

0

Aşağıdaki çözümü denedim ve insert deyimi için eşzamanlı istek oluştuğunda benim için çalışıyor.

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

0

Bu sorguyu kullanabilirsiniz. Tüm SQL Server sürümlerinde çalışın. Çok basit ve açık. Ancak 2 sorgu kullanmanız gerekir. MERGE kullanamıyorsanız kullanabilirsiniz

    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

NOT: Lütfen cevap negatiflerini açıklayınız


Kilitlenme eksikliğini tahmin ediyorum?
Zeek2

Kilitleme yok ... "TRAN" kullanıyorum. Varsayılan sql-server işlemleri kilitleme özelliğine sahiptir.
Victor Sanchez

-2

ADO.NET kullanıyorsanız, DataAdapter bunu işler.

Eğer kendiniz halletmek istiyorsanız, bu yol:

Anahtar sütununuzda birincil anahtar kısıtlaması bulunduğundan emin olun.

O zaman sen:

  1. Güncellemeyi yap
  2. Anahtarlı bir kayıt zaten mevcut olduğundan güncelleme başarısız olursa, ekleme işlemini yapın. Güncelleme başarısız olmazsa, işiniz tamamlanmış demektir.

Bunu başka bir şekilde de yapabilirsiniz, yani önce eki yapın ve ek başarısız olursa güncellemeyi yapın. Normalde ilk yol daha iyidir, çünkü güncellemeler eklerden daha sık yapılır.


... ve önce ekleme yapmak (bazen başarısız olacağını bilmek) SQL Server için pahalıdır. sqlperformance.com/2012/08/t-sql-queries/error-handling
Aaron Bertrand

-3

Varsa ... başka yapmak ... minimum iki istek yapmayı içerir (biri kontrol etmek, diğeri harekete geçmek için). Aşağıdaki yaklaşım, kaydın var olduğu yerde yalnızca bir tane, bir ekleme gerekliyse iki tane gerektirir:

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')

-3

Genellikle diğer posterlerin birkaçının söylediklerini önce var olup olmadığını kontrol etmek ve daha sonra doğru yol ne olursa olsun yapmakla yaparım. Bunu yaparken hatırlamanız gereken bir şey, sql tarafından önbelleğe alınan yürütme planının bir yol veya diğeri için optimal olmaması olabilir. Bunu yapmanın en iyi yolunun iki farklı saklı yordam çağırmak olduğuna inanıyorum.

FirstSP:
Varsa
   SecondSP'yi arayın (UpdateProc)
Başka
   Call ThirdSP (InsertProc)

Şimdi, kendi tavsiyemi çok sık takip etmiyorum, bu yüzden bir tuz tanesi ile alın.


Bu, SQL Server'ın eski sürümlerinde ilgili olabilir, ancak modern sürümlerde deyim düzeyinde derleme vardır. Çatallar vb bir sorun değildir ve bunlar için ayrı prosedürleri kullanarak ... Neyse bir güncelleme ve bir ek arasındaki seçim yapmak doğasında sorunlardan herhangi çözmez
Aaron Bertrand

-10

Bir seçim yapın, sonuç alırsanız, güncelleyin, eğer değilse, oluşturun.


3
Bu veritabanına iki çağrı.
Chris Cudmore

3
Bununla ilgili bir sorun görmüyorum.
Clint Ecker

10
Sorun DB DB iki çağrı, DB gidiş dönüş sayısını iki katına çıkar. Uygulama çok sayıda ekler / güncellemeler ile db'ye vurursa, performansa zarar verir. UPSERT daha iyi bir stratejidir.
Kev

5
aynı zamanda bir yarış durumu yaratıyor hayır?
niico
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.