SQL Server'da depolanan proc güncellemesini ekleyin


104

Bir kayıt varsa güncelleme yapacak depolanmış bir işlem yazdım, aksi takdirde ekleme yapacak. Şuna benzer:

update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)

Bunu bu şekilde yazmanın arkasındaki mantığım, güncellemenin where cümlesini kullanarak örtük bir seçim gerçekleştireceği ve eğer 0 döndürürse, eklemenin gerçekleşeceği.

Bunu bu şekilde yapmanın alternatifi, bir seçim yapmak ve ardından bir güncelleme yapmak veya eklemek için döndürülen satırların sayısına bağlı olarak olabilir. Bunun verimsiz olduğunu düşündüm çünkü bir güncelleme yapacaksanız, 2 seçime neden olacaktır (ilk açık seçim çağrısı ve güncellemenin bulunduğu yerde ikinci örtük). İşlemci bir ekleme yapacak olsaydı, verimlilikte hiçbir fark olmazdı.

Mantığım burada mı? Bir eklenti ve güncellemeyi depolanan bir işlemde böyle mi birleştirirsiniz?

Yanıtlar:


61

Varsayımınız doğru, bunu yapmanın en uygun yolu bu ve buna yükseltme / birleştirme deniyor .

UPSERT'nin önemi - sqlservercentral.com'dan :

Yukarıda belirtilen durumdaki her güncelleme için, EXISTS yerine UPSERT kullanırsak tablodan ek bir okumayı kaldırıyoruz. Maalesef bir Ek için hem UPSERT hem de IF EXISTS yöntemleri tablodaki aynı sayıda okuma kullanır. Bu nedenle, mevcudiyet kontrolü yalnızca ek G / Ç'yi gerekçelendirmek için çok geçerli bir neden olduğunda yapılmalıdır. İşleri yapmanın optimize edilmiş yolu, DB'de mümkün olduğunca az okumaya sahip olduğunuzdan emin olmaktır.

En iyi strateji güncellemeyi denemektir. Güncellemeden etkilenen satır yoksa, ekleyin. Çoğu durumda, satır zaten mevcut olacak ve yalnızca bir G / Ç gerekli olacaktır.

Düzenleme : Bu modelle ilgili sorunlar ve nasıl güvenli bir şekilde çalışacağı hakkında bilgi edinmek için lütfen bu yanıta ve bağlantılı blog gönderisine bakın.


1
En azından bir soruya cevap verdi sanırım. Kod eklemedim çünkü sorudaki kod benim için zaten doğru görünüyordu. Bir işleme koyacak olsam da, güncelleme için izolasyon seviyesini hesaba katmadım. Cevabınızda bunu belirttiğiniz için teşekkürler!
binOr

54

Lütfen okuyun bloguma yazı bir iyiliği için güvenli desen kullanabilirsiniz. Dikkate alınması gereken çok şey var ve bu sorunun kabul edilen cevabı güvenli olmaktan uzak.

Hızlı bir yanıt için aşağıdaki modeli deneyin. SQL 2000 ve üzeri sürümlerde sorunsuz çalışacaktır. SQL 2005 size diğer seçenekleri açan hata işlemeyi verir ve SQL 2008 size MERGE komutu verir.

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

1
Blog gönderinizde, varoluş kontrolünde WITH (updlock, serializable) ipucu kullanmakla bitirdiniz. Ancak, MSDN okunduğunda şu durum vardır: "UPDLOCK - Güncelleme kilitlerinin alınacağını ve işlem tamamlanana kadar tutulacağını belirtir." Bu, işlemin geri kalanı için güncelleme kilidi tutulacağından serileştirilebilir ipucunun gereksiz olduğu anlamına mı geliyor yoksa bir şeyi yanlış mı anladım?
Dan Def

10

SQL Server 2000/2005 ile kullanılacaksa, verilerin eşzamanlı senaryoda tutarlı kalmasını sağlamak için orijinal kodun işleme dahil edilmesi gerekir.

BEGIN TRANSACTION Upsert
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
COMMIT TRANSACTION Upsert

Bu, ek performans maliyetine neden olacak, ancak veri bütünlüğünü sağlayacaktır.

Daha önce önerildiği gibi, varsa MERGE kullanılmalıdır.



6

Sadece işlem sırasında çalıştırmanız gerekmez, aynı zamanda yüksek izolasyon seviyesine de ihtiyaç duyar. Aslında varsayılan izolasyon seviyesi Read Commited ve bu kodun Serializable olması gerekiyor.

SET transaction isolation level SERIALIZABLE
BEGIN TRANSACTION Upsert
UPDATE myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
  begin
    INSERT into myTable (ID, Col1, Col2) values (@ID @col1, @col2)
  end
COMMIT TRANSACTION Upsert

Belki @@ hata denetimi ve geri alma eklemek de iyi bir fikir olabilir.


@Munish Goyal Çünkü veri tabanında birden fazla komut ve emsal paralel çalışıyor. Diğer iş parçacığı, güncelleme çalıştırıldıktan hemen sonra ve ekleme çalıştırılmadan önce bir satır ekleyebilir.
Tomas Tintera

5

SQL 2008'de bir birleştirme yapmıyorsanız, bunu şu şekilde değiştirmelisiniz:

@@ rowcount = 0 ve @@ error = 0 ise

aksi takdirde güncelleme herhangi bir nedenle başarısız olursa, başarısız bir ifadedeki satır sayısı 0 olduğundan daha sonra eklemeyi deneyecektir.


3

UPSERT'nin büyük hayranı, yönetilmesi gereken kodu gerçekten azaltır. İşte bunu yapmamın başka bir yolu: Giriş parametrelerinden biri ID'dir, eğer ID NULL veya 0 ise, bunun bir INSERT olduğunu biliyorsunuz, aksi takdirde bir güncelleme. Uygulamanın bir kimlik olup olmadığını bildiğini varsayar, bu nedenle her durumda çalışmayacaktır, ancak yaparsanız yürütmeleri yarıya indirecektir.


2

Değiştirilmiş Dima Malenko gönderisi:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRANSACTION UPSERT 

UPDATE MYTABLE 
SET    COL1 = @col1, 
       COL2 = @col2 
WHERE  ID = @ID 

IF @@rowcount = 0 
  BEGIN 
      INSERT INTO MYTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

IF @@Error > 0 
  BEGIN 
      INSERT INTO MYERRORTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

COMMIT TRANSACTION UPSERT 

Hatayı yakalayabilir ve kaydı başarısız bir ekleme tablosuna gönderebilirsiniz.
Bunu yapmam gerekiyordu çünkü WSDL ile gönderilen her türlü veriyi alıyoruz ve mümkünse dahili olarak düzeltiyoruz.


1

Mantığınız sağlam görünüyor, ancak belirli bir birincil anahtardan geçtiyseniz, eklemeyi önlemek için bazı kodlar eklemeyi düşünebilirsiniz.

Aksi takdirde, güncelleme herhangi bir kaydı etkilemediyse her zaman bir ekleme yapıyorsanız, "UPSERT" çalışmadan önce birisi kaydı sildiğinde ne olur? Şimdi güncellemeye çalıştığınız kayıt mevcut değil, bu nedenle onun yerine bir kayıt oluşturacak. Muhtemelen aradığınız davranış bu değildi.

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.