Büyük bir veri tablom var. Bu tabloda 10 milyon kayıt var.
Bu sorgu için en iyi yol nedir
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Büyük bir veri tablom var. Bu tabloda 10 milyon kayıt var.
Bu sorgu için en iyi yol nedir
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Yanıtlar:
Bu tablodaki tüm satırları Siliyorsanız, en basit seçenek tabloyu Kesmektir.
TRUNCATE TABLE LargeTable
GO
Truncate table basitçe tabloyu boşaltır, silinen satırları sınırlamak için WHERE cümlesini kullanamazsınız ve hiçbir tetikleyici tetiklenmez.
Öte yandan, verilerin yüzde 80-90'ından fazlasını siliyorsanız, örneğin toplam 11 Milyon satırınız varsa ve 10 milyonu silmek istiyorsanız, bu 1 milyon satırı (saklamak istediğiniz kayıtları eklemek) ) başka bir aşama tablosuna. Bu Büyük tabloyu kesin ve bu 1 Milyon satırı geri ekleyin.
Veya altta yatan tablo olarak bu büyük tabloya sahip olan izinler / görünümler veya diğer nesneler bu tabloyu bırakarak etkilenmezse, bu nispeten küçük miktarda satırları başka bir tabloya alabilir, bu tabloyu bırakabilir ve aynı şemayla başka bir tablo oluşturabilir ve bunları içe aktarabilirsiniz. bu eski Büyük tabloya geri döner.
Aklıma gelen son bir seçenek, veritabanınızı değiştirmek Recovery Mode to SIMPLE
ve daha sonra bunun gibi bir süre döngüsü kullanarak daha küçük gruplar halinde satırları silmek.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
END
ve Kurtarma modunu tekrar tam olarak değiştirmeyi unutmayın ve bence tamamen etkili hale getirmek için bir yedek almanız gerekiyor (değişiklik veya kurtarma modları).
optimal solution for unknown case
bu rüya değil mi? Ne yazık ki her hastalığı tek bir hapla tedavi edemezsiniz; Farklı senaryolar için bazı olası çözümler önerdim. Maalesef burada şerit mermi yok.
@ m-ali yanıtı doğrudur, ancak her parçadan sonra işlemi gerçekleştirmezseniz ve bir kontrol noktası gerçekleştirmezseniz günlüklerin çok büyüyebileceğini unutmayın. Bunu nasıl yapacağım ve performans testleri ve grafiklerle birlikte http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes adlı bu makaleyi referans olarak alıyorum :
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
COMMIT TRANSACTION
ve CHECKPOINT
günlükler hala büyüyor. Bunu netleştirdiğiniz için teşekkürler.
@Deleted_Rows
10000 ile karşılaştırmak isteyebileceğinizi veya küçük veri kümelerini süresiz olarak sildiği için sonsuz döngü ile sonuçlanabileceğinizi unutmayın. Yani WHILE (@Deleted_Rows = 10000)
- silinecek tam bir veri "sayfası" kalmaz silinmez. Uygulamanızda, WHILE (@Deleted_Rows > 0)
while döngüsü yalnızca bir satırı silse bile yeniden yürütülür ve sonraki yürütme de silinecek bir veya iki satır bulabilir - bu da sonsuz bir döngü ile sonuçlanır.
WHILE
döngü kendisi: dateadd(MONTH,-7,GETDATE())
.
WHILE
döngünün farklı yinelemeleri arasında silinebilecek yeni kayıtlarla sonuçlanacak temel tabloya eklenebilir .
Aynı sorguyu kaç kez yürütmek istediğinizi GO + 'yı da kullanabilirsiniz.
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
çalışması gerekiyor? Ben olsun "saklı yordam bulunamadı ''" hatası. GO
Komut olmadan da iyi çalışıyor.
@Francisco Goldenstein, sadece küçük bir düzeltme. Değişkeni ayarladıktan sonra COMMIT kullanılmalıdır, aksi takdirde WHILE yalnızca bir kez çalıştırılacaktır:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
M.Ali'nin bu varyasyonu benim için iyi çalışıyor. Bazılarını siler, günlüğü temizler ve tekrarlar. Günlüğün büyümesini, düşmesini ve yeniden başlamasını izliyorum.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
SET @Deleted_Rows = @@ROWCOUNT;
dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
# of rows
Bir seferde silmek için parametresini ve ayrıca WHERE
tümceyi parametrelendirmek için değiştirdim . Tıkır tıkır çalışıyor!
Bölümlemeyi uygulamaya istekliyseniz (ve yapabiliyorsanız), bu, çok az çalışma süresi ek yükü ile büyük miktarda veriyi kaldırmak için etkili bir tekniktir. Yine de bir defalık bir egzersiz için uygun maliyetli değil.
21 milyon satırlık tablomdan 19 milyon satırı dakikalar içinde silebildim . İşte benim yaklaşımım.
Eğer bir varsa otomatik artan birincil anahtar bu masada, o zaman bu birincil anahtar yararlanabilir.
ReadTime <dateadd (MONTH, -7, GETDATE ()) olan büyük tablonun birincil anahtarının minimum değerini alın. (ReadTime'da dizin ekleyin, zaten yoksa, bu dizin yine de 3. adımdaki tabloyla birlikte silinecektir.) Onu bir 'min_primary' değişkeninde saklayalım
Birincil anahtara sahip tüm satırları> min_primary bir aşama tablosuna yerleştirin (satır sayısı büyük değilse bellek tablosu).
Büyük masayı bırak.
Masayı yeniden oluşturun. Tüm satırları aşama tablosundan ana tabloya kopyalayın.
Aşama tablosunu bırakın.
Bir while döngüsü kullanarak küçük grupları silebilirsiniz, bunun gibi bir şey:
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
Başka bir kullanım:
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
İsteğe bağlı;
İşlem günlüğü etkinse, işlem günlüklerini devre dışı bırakın.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
SQL server 2016 veya üstünü kullanıyorsanız ve tablonuzda silmeye çalıştığınız sütuna göre oluşturulmuş bölümler varsa (örneğin, Zaman Damgası sütunu), verileri bölümlere göre silmek için bu yeni komutu kullanabilirsiniz.
(BÖLÜMLEMELER ({|} [, ... n])) İLE TRUNCATE TABLE
Bu, yalnızca seçilen bölümlerdeki verileri siler ve işlem günlükleri oluşturmayacağından ve tüm verilerin silinmesine gerek kalmadan normal kesme kadar hızlı yapılacağından, tablonun bir bölümündeki verileri silmenin en etkili yolu olmalıdır. masadan.
Dezavantajı, tablonuz bölümle kurulmamışsa, eski moda gitmeniz ve verileri düzenli bir yaklaşımla silmeniz ve daha sonra bunu gelecekte yapabilmeniz için bölümlerle tabloyu yeniden oluşturmanız gerekir, ben de öyle yaptım. Bölüm oluşturma ve silme işlemlerini ekleme prosedürünün kendisine ekledim. 500 milyon satırlık bir masam vardı, bu yüzden silme süresini azaltmak için tek seçenek buydu.
Daha fazla ayrıntı için aşağıdaki bağlantılara bakın: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL server 2016 Bölümlerle tabloyu kes
Aşağıda, gerekli verilerin bulunduğu bölümlerle tabloyu yeniden oluşturmadan önce verileri silmek için ilk yaptığım şey. Bu sorgu, veriler silinene kadar belirtilen zaman aralığında günlerce çalışacaktır.
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()
Döngü olmadan dersem, GOTO
sql server kullanarak büyük miktarda kayıt silmek için deyimi kullanabilirim . eksa.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
bu şekilde daha küçük boyutta silme ile büyük miktarda veriyi silebilirsiniz.
daha fazla bilgi gerekiyorsa bana bildirin.