Aslında herhangi bir veriyi değiştirmeyen bir UPDATE deyim varsa (veriler zaten güncellenmiş durumda olduğundan), güncelleştirmeyi engellemek için yan tümce maddesini kontrol etmenin herhangi bir performans yararı var mı?
GÜNCELLEME 1 nedeniyle hafif bir performans farkı olduğu için kesinlikle olabilir :
- aslında hiçbir satırı güncellemiyor (dolayısıyla diske yazacak hiçbir şey yok, hatta minimum günlük etkinliği bile yok) ve
- Gerçek güncellemeyi yapmak için gerekenden daha az kısıtlayıcı kilitler almak (bu nedenle eşzamanlılık için daha iyidir) ( Lütfen sona doğru Güncelleme bölümüne bakın )
Ancak, sisteminizde sizin tarafınızdan şema, veri ve sistem yükü ile ne kadar farkın ölçülmesi gerekir. Güncelleme yapılmayan bir UPDATE'in ne kadar etkili olduğunu belirleyen çeşitli faktörler vardır:
- Güncellenen masada çekişme miktarı
- güncellenen satır sayısı
- güncellenen masadaki GÜNCELLEME Tetikleyicileri varsa (Soru ile ilgili bir yorumda Mark tarafından belirtildiği gibi). Yürütürseniz
UPDATE TableName SET Field1 = Field1
, daha sonra bir Güncelleme Tetik ateş edeceği ve (ya kullanarak kontrol ederseniz alan güncellendi belirtmek UPDATE () veya COLUMNS_UPDATED işlevlerini) ve her iki alan olduğunu INSERTED
ve DELETED
tablolar aynı değerdir.
Ayrıca, aşağıdaki özet bölüm Paul White'ın Güncelleme Yapılmayan Güncellemelerin Etkisi ( @spaghettidba tarafından yanıtına yaptığı bir yorumda belirtildiği gibi) adlı makalesinde bulunmaktadır:
SQL Server, kalıcı bir veritabanında herhangi bir değişikliğe neden olmayacak bir UPDATE işlemini işlerken gereksiz günlük kaydı veya sayfa temizlemesini önlemek için çeşitli optimizasyonlar içerir.
- Kümelenmiş bir tablodaki güncelleme olmayan güncellemeler, genellikle, küme anahtarını oluşturan bir sütun (güncelleme işlemi) etkilenmiyorsa, fazladan günlük kaydı yapmaktan ve sayfa temizlemekten kaçınır.
- Küme tuşunun herhangi bir kısmı aynı değere 'güncellenmiş' ise, işlem sanki veriler değiştirilmiş gibi kaydedilir ve etkilenen sayfalar arabellek havuzunda kirli olarak işaretlenir. Bu, GÜNCELLEME'nin bir silme-sonra-silme işlemine dönüştürülmesinin bir sonucudur.
- Öbek tabloları, fazladan bir günlüğe kaydetmeye veya sayfa temizlemesine neden olacak bir küme anahtarına sahip olmadıklarından, kümelenmiş tablolarla aynı şekilde davranır. Bu, kümelenmemiş bir birincil anahtarın öbek üzerinde olduğu durumlarda bile geçerlidir. Bir yığında güncellenmeyen güncellemeler, bu nedenle genellikle fazladan kayıt ve yıkamadan kaçınır (ancak aşağıya bakınız).
- Hem yığınlar hem de kümelenmiş tablolar, 8000 bayttan fazla veri içeren bir LOB sütununun 'SET column_name = column_name' dışındaki herhangi bir sözdizimi kullanılarak aynı değere güncellendiği herhangi bir satır için fazladan günlüğe kaydetme ve temizleme işlemlerine maruz kalır.
- Basitçe, bir veritabanında herhangi bir tür satır versiyonlama yalıtım seviyesinin etkinleştirilmesi her zaman ekstra günlüklemeye ve yıkamaya neden olur. Bu, güncelleme işlemi için geçerli olan yalıtım seviyesinden bağımsız olarak gerçekleşir.
Lütfen aklınızda bulundurun (özellikle Paul'ün makalesinin tamamını görmek için bağlantıyı takip etmezseniz), aşağıdaki iki madde:
Güncelleme yapılmayan güncellemeler hala bir işlemin başladığını ve bittiğini gösteren bazı günlük faaliyetlerine sahiptir. Sadece hiçbir veri değişikliği gerçekleşmez (bu hala iyi bir tasarruftur).
Yukarıda belirttiğim gibi, sisteminizi test etmeniz gerekiyor. Paul'ün kullandığı araştırma sorgularını kullanın ve aynı sonuçları alıp almadığınızı görün. Sistemimde, makalede gösterilenden biraz farklı sonuçlar görüyorum. Hala yazdırılacak kirli sayfalar yok, ancak biraz daha fazla günlük etkinliği.
... Değişmeyen satırı eklemek için satır sayımına ihtiyacım var, böylece kimlik yoksa ek ekleme yapılıp yapılmayacağını biliyorum. ... bir şekilde ihtiyacım olan satır sayısını almak mümkün mü?
Basitçe, sadece tek bir satır ile ilgileniyorsanız, aşağıdakileri yapabilirsiniz:
UPDATE MyTable
SET Value = 2
WHERE ID = 2
AND Value <> 2;
IF (@@ROWCOUNT = 0)
BEGIN
IF (NOT EXISTS(
SELECT *
FROM MyTable
WHERE ID = 2 -- or Value = 2 depending on the scenario
)
)
BEGIN
INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
VALUES (2, 2);
END;
END;
Birden fazla satır için, OUTPUT
maddeyi kullanarak bu kararı vermek için gereken bilgileri alabilirsiniz . Tam olarak hangi satırların güncellendiğini yakalayarak, mevcut satırları güncellememek yerine var olan güncellemelere ihtiyaç duymamak yerine var olmayan güncelleme satırları değil arasındaki farkı öğrenmek için öğeleri daraltabilirsiniz.
Temel uygulamayı aşağıdaki cevapta gösteriyorum:
Xml parametresini kullanarak birden fazla veriyi yükseltirken Birleştir sorgusunu kullanmaktan nasıl kaçınılır?
Bu cevapta gösterilen yöntem henüz var olan satırları filtrelemez, ancak güncellenmesi gerekmez. Bu kısım eklenebilir, ancak önce birleştiğiniz veri kümenizi tam olarak nereden aldığınızı göstermeniz gerekir MyTable
. Geçici bir masadan mı geliyorlar? Tablo değerli bir parametre (TVP)?
GÜNCELLEME 1:
Sonunda bazı testler yapabildim ve işte işlem günlüğü ve kilitleme ile ilgili bulduklarım. İlk olarak, tablonun şeması:
CREATE TABLE [dbo].[Test]
(
[ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
[StringField] [varchar](500) NULL
);
Ardından, alanı zaten sahip olduğu değerle güncelleyen test:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
Sonuçlar:
-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
8 - IX 6 - PAGE
5 - X 7 - KEY
Son olarak, değer değişmemesi nedeniyle güncellemeyi filtreleyen test:
UPDATE rt
SET rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM dbo.Test rt
WHERE rt.ID = 4082117
AND rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';
Sonuçlar:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (3 Lock:Acquired events):
Mode Type
--------------------------------------
8 - IX 5 - OBJECT
7 - IU 6 - PAGE
4 - U 7 - KEY
Gördüğünüz gibi, İşlemin başlangıcını ve bitişini gösteren iki girişin aksine, satırı filtreleyen İşlem Günlüğü'ne hiçbir şey yazılmaz. Ve bu iki girişin neredeyse hiçbir şey olmadığı doğru olsa da, hala bir şeydir.
Ayrıca, PAGE ve KEY kaynaklarının kilitlenmesi değişmemiş satırları filtrelerken daha az kısıtlayıcıdır. Başka hiçbir işlem bu tabloyla etkileşime girmiyorsa, o zaman sorun olmaz (ama bu gerçekten ne kadar muhtemeldir?). Bağlantılı bloglardan herhangi birinde (ve hatta testlerimde) gösterilen testlerin, hiçbir zaman testlerin bir parçası olmadığı için masada hiçbir çekişme olmadığını varsaydığını unutmayın. Güncelleme yapılmayan güncellemelerin o kadar hafif olduğunu söyleyerek, filtrelemeyi yapmak için para ödemeyin, testin bir vakumda az ya da çok yapıldığı için bir miktar tuzla alınması gerekir. Ancak Üretim'de bu tablo büyük olasılıkla yalıtılmış değildir. Tabii ki, küçük kütük kaydı ve daha kısıtlayıcı kilitlerin daha az verimlilikle sonuçlanmaması çok iyi olabilir. Yani bu soruyu cevaplamak için en güvenilir bilgi kaynağı? SQL Server. özellikle:sizin SQL Server. Sisteminiz için hangi yöntemin daha iyi olduğunu size gösterecektir :-).
GÜNCELLEME 2:
Yeni değerin mevcut değerle aynı olduğu işlemler (yani güncelleme yok), yeni değerin farklı olduğu ve güncellemenin gerekli olduğu işlemleri sayıyorsa, aşağıdaki örnek daha iyi olabilir. Masada çok çekişme var. Fikir, SELECT
geçerli değeri elde etmek için önce basit bir işlem yapmaktır . Eğer bir değer alamazsanız, ilgili cevabınız sizde INSERT
. Eğer bir değeriniz varsa, basit bir işlem yapabilir IF
ve UPDATE
sadece gerekli olması durumunda düzenleyebilirsiniz .
DECLARE @CurrentValue VARCHAR(500) = NULL,
@NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
@ID INT = 4082117;
SELECT @CurrentValue = rt.StringField
FROM dbo.Test rt
WHERE rt.ID = @ID;
IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
-- row does not exist
INSERT INTO dbo.Test (ID, StringField)
VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
-- row exists, so check value to see if it is different
IF (@CurrentValue <> @NewValue)
BEGIN
-- value is different, so do the update
UPDATE rt
SET rt.StringField = @NewValue
FROM dbo.Test rt
WHERE rt.ID = @ID;
END;
END;
Sonuçlar:
-- Transaction Log (0 entries):
Operation
----------------------------
-- SQL Profiler (2 Lock:Acquired events):
Mode Type
--------------------------------------
6 - IS 5 - OBJECT
6 - IS 6 - PAGE
Bu yüzden, 3 yerine yalnızca 2 kilit alınmış ve bu kilitlerin her ikisi de Amaçlı Olarak Belirlenmiş veya Amaçlı Güncelleme ( Kilit Uyumluluğu ) değil Amaç Paylaşımlı . Elde edilen her bir kilidin de serbest bırakılacağını unutmayın; her bir kilit gerçekten 2 işlemdir, bu nedenle bu yeni yöntem, önerilen yöntemdeki 6 işlem yerine toplam 4 işlemdir. Bu işlemin her 15 ms'de bir (yaklaşık olarak OP tarafından belirtildiği gibi) bir saniyede 66 kez çalıştığını düşünün. Bu nedenle, orijinal teklif saniyede 396 kilitleme / kilit açma işlemi tutarken, bu yeni yöntem daha hafif kilitlerin bile saniye başına yalnızca 264 kilitleme / kilit açma işlemi tutarındadır. Bu, mükemmel performansın garantisi değil, kesinlikle test etmeye değerdir :-).