Hiçbir cümlecik olmadan <table> 'den büyük bir DELETE' i hızlandırma yöntemleri


37

SQL Server 2005'i kullanma.

Hiçbir cümlecik olmadan büyük bir DELETE FROM yapıyorum. Temel olarak bir TRUNCATE TABLE ifadesine eşdeğerdir - TRUNCATE kullanmama izin verilmiyor hariç. Sorun tablo büyük - 10 milyon satır ve tamamlanması bir saatten fazla sürüyor. Olmadan daha hızlı yapmanın bir yolu var mı:

  • Kısaltmayı Kullanma
  • Dizinleri devre dışı bırakma veya bırakma?

T-log zaten ayrı bir diskte.

Herhangi bir öneriniz hoş geldiniz!


2
Bunu çok yapacaksanız, tabloyu bölümlere
ayırmayı

1
TRUNCATE'i kullanamazsınız, çünkü tabloya atıfta bulunan FK kısıtlamaları var mı?
Nick Chammas,

Yanıtlar:


39

Ne yapabilirsiniz toplu iş böyle siler:

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable

Xxx'in 50000 olduğu yerde

Bunun bir modifikasyonu, çok yüksek bir satır yüzdesini kaldırmak istiyorsanız ...

SELECT col1, col2, ... INTO #Holdingtable
           FROM MyTable WHERE ..some condition..

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable WHERE ...

INSERT MyTable (col1, col2, ...)
           SELECT col1, col2, ... FROM #Holdingtable

3
@tuseau: her silme, hata durumunda geri alma için bir miktar günlük boşluk gerektirir. Bir 50k satır silme, 10m satır silme işleminden daha az kaynak / alan alır. Tabii ki, log yedekleri hala çalışıyor ve yer kaplıyor, ancak sunucuda büyük partileri zorlamaktan çok küçük partilere çok daha kolay.
gbn

1
Teşekkürler, toplu silme biraz yardımcı olur, sanırım en iyi seçenek bu.
tuseau

2
@Phil Helmer: toplu silme bir işlemde ise o zaman kazancı yoktur. Aksi halde, her bir günlük yazma daha küçüktür, basitçe, daha kolay bir yüktür
gbn

1
Bir başka yorum: toplu silme işlemi büyük ölçüde yardımcı olur ve 1 saat 42 dakikadan 3 dakikaya kadar 20 milyon satırı silmeyi alır - AMA tablonun kümelenmiş bir dizine sahip olduğundan emin olun! Eğer bu bir yığınsa, TOP cümlesi, yürütme planında herhangi bir gelişmeyi engelleyen bir sıralama oluşturur. Sonrasında bariz görünüyor.
tuseau

2
@Noumenon: @@ ROWCOUNT’in 1
gbn

21

Bunu kolayca yapmak için TOP yan tümcesini kullanabilirsiniz:

WHILE (1=1)
BEGIN
    DELETE TOP(1000) FROM table
    IF @@ROWCOUNT < 1 BREAK
END


@gbn Bu SO. burada hala 101
010.

7

TRUNCATE kullanamıyorsanız, silme işlemlerinizi yönetilebilir parçalara ayırma önerileriyle aynı fikirdeyim ve orijinalliği için bırakma / oluşturma önerisini seviyorum, ancak sorunuzdaki şu yorumu merak ediyorum:

Temel olarak bir TRUNCATE TABLE ifadesine eşdeğerdir - TRUNCATE kullanmama izin verilmemesi dışında

Bu kısıtlamanın nedeninin bir masayı doğrudan kesmek için verilmesi gereken güvenlikle ve ilgilendiğinizden başka masaları kesmenize izin vereceği gerçeğiyle ilgili olduğunu tahmin ediyorum.

Durumun böyle olduğunu varsayarak, TRUNCATE TABLE kullanarak ve "EXECUTE AS" kullanan bir saklı yordamın oluşturulup yaratılmadığını merak ediyorum, masayı doğrudan kesmek için gerekli güvenlik haklarını vermenin uygun bir alternatif olarak kabul edilip edilmeyeceğini düşünüyorum.

Umarım, bu size ihtiyacınız olan hızı verirken, şirketinizin db_ddladmin rolüne hesabınızı eklerken sahip olabileceği güvenlik kaygılarını da ele alır.

Saklı yordamın bu şekilde kullanılmasının bir başka avantajı da saklı yordamın kilitlenebilmesi ve böylece yalnızca belirli hesapların kullanmasına izin verilmesidir.

Herhangi bir nedenden ötürü bu kabul edilebilir bir çözüm değilse ve bu tablodaki verilerin kaldırılması gerekliliği günde / saatte bir kez yapılması gereken bir şeyse, tabloyu kesmek için bir SQL Agent işi oluşturulmasını isterim. Her gün planlanan bir zamanda.

Bu yardımcı olur umarım!


5

Kesik hariç .. sadece gruplar halinde silmek size yardımcı olabilir.

Masayı bırakıp tüm kısıtlamalar ve indekslerle kursun dışı bırakabilirsiniz. Management Studio'da düşünüp oluşturulacak bir tabloyu kodlama seçeneğiniz vardır, bu nedenle önemsiz bir seçenek olmalıdır. Ancak bu yalnızca DDL işlemleri yapmanıza izin veriliyorsa, ki bunun gerçekten bir seçenek olmadığını görüyorum.


Uygulama eşzamanlı işlemler için tasarlandığından yapıyı (DDL) değiştirmek ve kesikli kullanmak seçenek değildir ... Sanırım toplu silme en iyisidir. Yine de teşekkürler.
tuseau

1

Bu soru çok önemli bir referans olduğu için bu kodu gönderip gerçekten döngüleri silmeyi ve ilerlemeyi izlemek için bir döngü içinde mesajlaşmayı anlamama yardımcı oldu.

Sorgu bu yinelenen sorudan değiştirildi . Sorgu tabanı için @ RRL'ye kredi verin .

CREATE TABLE #DelTest (ID INT IDENTITY, name NVARCHAR(128)); -- Build the test table
INSERT INTO #DelTest (name) SELECT name FROM sys.objects;  -- fill from system DB
SELECT COUNT(*) TableNamesContainingSys FROM #deltest WHERE name LIKE '%sys%'; -- check rowcount
go
DECLARE @HowMany INT;
DECLARE @RowsTouched INT;
DECLARE @TotalRowCount INT;
DECLARE @msg VARCHAR(100);
DECLARE @starttime DATETIME 
DECLARE @currenttime DATETIME 

SET @RowsTouched = 1; -- Needs to be >0 for loop to start
SET @TotalRowCount=0  -- Total rows deleted so far is 0
SET @HowMany = 5;     -- Variable to choose how many rows to delete per loop
SET @starttime=GETDATE()

WHILE @RowsTouched > 0
BEGIN
   DELETE TOP (@HowMany)
   FROM #DelTest 
   WHERE name LIKE '%sys%';

   SET @RowsTouched = @@ROWCOUNT; -- Rows deleted this loop
   SET @TotalRowCount = @TotalRowCount+@RowsTouched; -- Increment Total rows deleted count
   SET @currenttime = GETDATE();
   SELECT @msg='Deleted ' + CONVERT(VARCHAR(9),@TotalRowCount) + ' Records. Runtime so far is '+CONVERT(VARCHAR(30),DATEDIFF(MILLISECOND,@starttime,@currenttime))+' milliseconds.'
   RAISERROR(@msg, 0, 1) WITH NOWAIT;  -- Print message after every loop. Can't use the PRINT function as SQL buffers output in loops.  

END; 
SELECT COUNT(*) TableNamesContainingSys FROM #DelTest WHERE name LIKE '%sys%'; -- Check row count after loop finish
DROP TABLE #DelTest;
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.