2016-03-09 26 views
8

Birden milyon kayıt güncelleyebilirsiniz bir UPDATE deyimi var. Ben @@ROWCOUNT ile çalıştı 1000 veya 10000 gruplar halinde güncellemek istiyorum ama istenilen sonucu alamıyor. Sadece ben is yaptıklarını test amaçlıdırSQL Server'da milyonlarca satırlık büyük tablo nasıl güncellenir?

, ben 14 kayıtlarıyla tabloyu seçilir ve bu sorgu 5. 5 ve 4'te kayıtlarını güncellemek gerekiyordu 5 bir satır sayısı ayarlanır ama sadece ilk 5 kayıtlarını günceller.

Sorgu - 1:

SET ROWCOUNT 5 

UPDATE TableName 
SET Value = 'abc1' 
WHERE Parameter1 = 'abc' AND Parameter2 = 123 

WHILE @@ROWCOUNT > 0 
BEGIN 
    SET rowcount 5 

    UPDATE TableName 
    SET Value = 'abc1' 
    WHERE Parameter1 = 'abc' AND Parameter2 = 123 

    PRINT (@@ROWCOUNT) 
END 

SET rowcount 0 

Sorgu - 2: Burada ne

SET ROWCOUNT 5 

WHILE (@@ROWCOUNT > 0) 
BEGIN 
    BEGIN TRANSACTION 

    UPDATE TableName 
    SET Value = 'abc1' 
    WHERE Parameter1 = 'abc' AND Parameter2 = 123 

    PRINT (@@ROWCOUNT) 

    IF @@ROWCOUNT = 0 
    BEGIN 
     COMMIT TRANSACTION 

     BREAK 
    END 

    COMMIT TRANSACTION 
END 

SET ROWCOUNT 0 

eksik? o @@ROWCOUNT sıfırlar nedeniyle Sizin print

+0

Sorgu2'de sorun nedir? –

+4

Böyle bir şekilde ROWCOUNT kullanmayın. Bu kullanımdan kaldırılmıştır. https://msdn.microsoft.com/en-us/library/ms188774.aspx –

+0

@JuanCarlosOropeza Komut başarıyla tamamlanmış olarak geliyor ama gerçekte kayıtların hiçbiri güncellenmiyor. – CSharper

cevap

1

şeyler berbat ediyor. Ne zaman @@ROWCOUNT kullanın, benim tavsiyem her zaman hemen bir değişkene ayarlayın. Yani:

DECLARE @RC int; 
WHILE @RC > 0 or @RC IS NULL 
    BEGIN 
     SET rowcount 5; 

     UPDATE TableName 
      SET Value = 'abc1' 
      WHERE Parameter1 = 'abc' AND Parameter2 = 123 AND Value <> 'abc1'; 

     SET @RC = @@ROWCOUNT; 
     PRINT(@@ROWCOUNT) 
    END; 

SET rowcount = 0; 

Ve başka güzel özellik update kodunu tekrarlamak zorunda kalmamasıdır.

+0

Güncelleştirmeler için satırları denetlemek üzere ROWCOUNT kullanımı hakkında yukarıda anlattığım bağlantıya bakın. –

+0

@Gordon Bu mantığı kullanıyorum ve sorgu 2 dakika çalışıyor (sadece 14 kayıt için !!!). Sonsuz döngüye gider. – CSharper

+0

@CSharper. . . Hmmm, eğer satır güncellenmezse, o zaman @@ ROWCOUNT = 0 olmalıdır, NULL değil. Sonsuz bir döngünün sebebi belli değil. "Baskı" üreten nedir? Eğer 'update'' NULL' üretiyorsa, bu '' RC '' i bazı rasgele değerlere ayarlayarak ve ''RC IS NULL' koşulunu' WHILE'den kaldırarak düzeltilebilir. –

11
WHILE EXISTS (SELECT * FROM TableName WHERE Value <> 'abc1') 
BEGIN 
UPDATE TOP (1000) TableName 
SET Value = 'abc1' 
WHERE Parameter1 = 'abc' AND Parameter2 = 123 
END 
+0

eşittir işareti olmadan SET satır sayısı 0 olmalıdır. Bunu 1000'den 4000'e güncelledim ve şimdiye kadar gayet iyi çalışıyor gibi görünüyor. Bir tabloda 5 milyon kayıt güncelleştiriyorum (her 10 dakikada bir 744.000 kayıt güncelleniyor gibi görünüyor). Bunu dev bir sunucuda çalıştırıyorum ve sonraki 26 milyon kayıtlarını güncellemeye çalışıyorum. Şu anda süreci "çok iş parçacığı" ile hızlandırmak için bir yol olup olmadığını görmek için bakıyor. – PHBeagle

0

Geçerli olan ve güncellemeler için kullanımdan kaldırılmış ROWCOUNT kullanılmayan başka bir seçenek aşağıda verilmiştir.

while @@ROWCOUNT > 0 
begin 
    UPDATE top (1000) TableName 
    SET Value = 'abc1' 
    WHERE Parameter1 = 'abc' 
    AND Parameter2 = 123 
end 
+1

, bu aynı kayıtları tekrar tekrar güncellemiyor mu? – FLICKER

+0

@FLICKER Aynı şeyi düşünüyordum. –

+0

@TT. Parameter1 <> 'abc' ve Parameter2 <> '123' – Kramb

-1

Her şeyden önce, girişleriniz için hepinize teşekkür ederiz. Query - 1'u değiştirdim ve istediğim sonucu aldım. Gordon Linoff haklı, PRINT benim sorgu kadar karıştırmasını yüzden aşağıdaki gibi modifiye:

Modifiye Sorgu - 1:

SET ROWCOUNT 5 
WHILE (1 = 1) 
    BEGIN 
    BEGIN TRANSACTION 

     UPDATE TableName 
     SET Value = 'abc1' 
     WHERE Parameter1 = 'abc' AND Parameter2 = 123 

     IF @@ROWCOUNT = 0 
      BEGIN 
       COMMIT TRANSACTION 
       BREAK 
      END 
    COMMIT TRANSACTION 
    END 
SET ROWCOUNT 0 

Çıktı:

(5 row(s) affected) 

(5 row(s) affected) 

(4 row(s) affected) 

(0 row(s) affected) 
9
  1. Güncelliyorsunuz edilmemelidir bir sette 10k satırları (nedeniyle sayfa başına birden çok satır UPDATE operasyonun bir parçası olmak için) işlemi Sayfa Kilitler oluyor emin olmadıkça. sorun (Row veya Sayfanıza gelen Tablo kilitleri için) kilit yükseltme 5000 kilitleri meydana gelmesidir. Bu yüzden, operasyon sadece Satır Kilitleri kullanıyorsa, 5000'in altında tutmak en güvenli yoldur.

  2. Sen değiştirilecek satır sayısını sınırlamak için SET ROWCOUNT kullanarak edilmemelidir.

    1. O (11 yıl önce), SQL Server 2005 serbest bırakıldı zamandan beri kullanılmadığından:

      , SİL INSERT etkilemez SET ROWCOUNT kullanarak ve UPDATE ifadeleri iki Burada konu var SQL Server'ın gelecekteki bir sürümü. Yeni geliştirme çalışmalarında DELETE, INSERT ve UPDATE deyimleriyle SET ROWCOUNT kullanmaktan kaçının ve şu anda kullanan uygulamaları değiştirmeyi planlayın.zaman işlemeyi durdurmak için en Transact-SQL ifadeleri SET ROWCOUNT seçeneğini neden olur Ayar

      : Benzer bir davranış için, TOP sözdizimini kullanın

    2. Size ilgileniyor sadece deyimi daha etkileyebilir belirtilen sayıda satırdan etkilenmişlerdir. Bu tetikleyicileri içerir. ROWCOUNT seçeneği dinamik imleçleri etkilemez, ancak keyset ve duyarsız imleçlerin satır kümesini sınırlar. Bu seçenek dikkatli kullanılmalıdır.

    Bunun yerine, TOP() madde kullanılır. Burada açık bir işlem yapmanın bir amacı yoktur. Bu kodu karmaşıklaştırır ve bir ROLLBACK için herhangi bir işlem yapmazsınız, çünkü her ifade kendi işlemidir (örn. Otomatik taahhüt).

  3. Açık işlemi devam ettirmek için bir neden bulduğunuzu varsayarak, bir TRY/CATCH yapınız yoktur. bu yüzden, ben fıkra Soru örnek kodda gösterilmiyor gerçek şüpheli

    Are we required to handle Transaction in C# Code as well as in Store procedure

: işlemlerini halleden TL/CATCH şablonu için DBA.StackExchange benim cevaba bakınız basitçe gösterilmiştir neyi güvenerek, daha iyi bir model olacaktır: son setin tipik bazı uyuşmuş olduğu için

@BatchSize karşı @Rows test ederek
DECLARE @Rows INT, 
     @BatchSize INT; -- keep below 5000 to be safe 

SET @BatchSize = 2000; 

SET @Rows = @BatchSize; -- initialize just to enter the loop 

BEGIN TRY  
    WHILE (@Rows = @BatchSize) 
    BEGIN 
     UPDATE TOP (@BatchSize) tab 
     SET tab.Value = 'abc1' 
     FROM TableName tab 
     WHERE tab.Parameter1 = 'abc' 
     AND tab.Parameter2 = 123 
     AND tab.Value <> 'abc1' COLLATE Latin1_General_100_BIN2; 
     -- Use a binary Collation (ending in _BIN2, not _BIN) to make sure 
     -- that you don't skip differences that compare the same due to 
     -- insensitivity of case, accent, etc, or linguistic equivalence. 

     SET @Rows = @@ROWCOUNT; 
    END; 
END TRY 
BEGIN CATCH 
    RAISERROR(stuff); 
    RETURN; 
END CATCH; 

, siz (çoğu durumda) bu son GÜNCELLEME sorgusu önleyebilirsiniz satırların sayısı @BatchSize'dan daha azdır, bu durumda işlemin daha fazla olamayacağını biliyoruz (bu, answer ürününüzde gösterilen çıktıda gördüğünüz şeydir). Sadece son satır kümesinin @BatchSize'a eşit olduğu durumlarda, bu kod 0 satırı etkileyen son UPDATE'i çalıştırır.

Ayrıca, daha önce güncellenen satırların yeniden güncellenmesini önlemek için WHERE maddesine bir koşul ekledim.

+0

Tek bir sütunu güncelleştirmiyor, ancak 10 sütun diyelim ne olur? Tüm sütunların değerlerini karşılaştırmalı mıyım? En performanslı yaklaşım ne olurdu? – asemprini87

+0

@ asemprini87 Daha uzun sürdüğü ve günlük dosyasını daha fazla büyüdükçe gereksiz güncellemeleri azaltmak için mümkün olduğunca fazla karşılaştırın.Cevabımı, "Value" filtresi için ikili bir Harmanlamayı zorlamayı içerecek şekilde güncelledim, ancak "tam eşleşen eşleşmeleri ve beklemeyi aradığınızı varsayarak" string dize eşleşmesini hızlandırmak için başka bir dize sütununda "COLLATE Latin1_General_100_BIN2" yi kullanabilirsiniz. Gövde farklılıkları, vb. için izin vermem gerekiyor. Bu işlem için filtrelenmiş bir dizin oluşturmayı tercih etmeyi düşünüyordum, ancak her parti için güncellenmesi gerekiyorsa daha hızlı olmayabilir. Yine de test etmeye değer olabilir. –

3

Deneyimlerimi paylaşmak istiyorum. Birkaç gün önce, 76 milyon kayıtla tabloda 21 milyon kayıt güncellemeliyim. Meslektaşım bir sonraki varyantı önerdi.

Id | FirstName | LastName | Email   | JobTitle 
1 | John  | Doe  | [email protected]  | Software Developer 
2 | John1  | Doe1 | [email protected]  | Software Developer 
3 | John2  | Doe2 | [email protected]  | Web Designer 

Görev: Yeni İş Ünvanı Güncelleme kişileri: -> 'Web Geliştirici' 'Yazılım Geliştirici' Örneğin, bir sonraki tabloyu 'Kişiler' var.

1. Geçici Tablo 'Persons_SoftwareDeveloper_To_WebDeveloper (id INT birincil anahtar)' Yeni İş Başlığı ile güncelleştirmek istediğiniz geçici tablo kişilerin içine

2. Seç oluşturun:

INSERT INTO Persons_SoftwareDeveloper_To_WebDeveloper SELECT Id FROM 
Persons WITH(NOLOCK) --avoid lock 
WHERE JobTitle = 'Software Developer' 
OPTION(MAXDOP 1) -- use only one core 

Satır sayısına bağlı olarak, bu ifadenin geçici tablonuzu doldurması biraz zaman alacaktır, ancak kilitlenmeyi önler. Benim durumumda yaklaşık 5 dakika sürdü (21 milyon satır).

3. ana fikir veritabanını güncellemek için mikro sql ifadeleri üretmektir. Yani, onları baskı atalım:

DECLARE @i INT, @pagesize INT, @totalPersons INT 
    SET @i=0 
    SET @pagesize=2000 
    SELECT @totalPersons = MAX(Id) FROM Persons 

    while @i<= @totalPersons 
    begin 
    Print ' 
    UPDATE persons 
     SET persons.JobTitle = ''ASP.NET Developer'' 
     FROM Persons_SoftwareDeveloper_To_WebDeveloper tmp 
     JOIN Persons persons ON tmp.Id = persons.Id 
     where persons.Id between '+cast(@i as varchar(20)) +' and '+cast(@[email protected] as varchar(20)) +' 
     PRINT ''Page ' + cast((@i/@pageSize) as varchar(20)) + ' of ' + cast(@totalPersons/@pageSize as varchar(20))+' 
    GO 
    ' 
    set @[email protected][email protected] 
    end 

Bu komut dosyasını çalıştırmadan sonra MS SQL Management Studio biri sekmede yürütebileceği toplu yüzlerce alacaktır.

4. Yazdırılan sql deyimlerini çalıştırın ve tablodaki kilitleri kontrol edin. İşlemi her zaman durdurabilir ve güncelleştirmeyi hızlandırmak veya hızlandırmak için @ pageSize ile oynayabilirsiniz (komut dosyasını duraklattıktan sonra @i'u değiştirmeyi unutmayın).

5. Drop Persons_SoftwareDeveloper_To_AspNetDeveloper. Geçici tabloyu kaldırın.

Küçük Not: Bu geçiş, bir süre alabilir ve geçiş sırasında geçersiz veriler içeren yeni satırlar eklenebilir. Yani, öncelikle satırların eklediği yerleri düzeltin. Durumumda UI, 'Yazılım Geliştirici' -> 'Web Geliştiricisi'ni kurdum.

İlgili konular