Ich habe eine große Datentabelle. Diese Tabelle enthält 10 Millionen Datensätze.
Was ist der beste Weg für diese Abfrage
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Ich habe eine große Datentabelle. Diese Tabelle enthält 10 Millionen Datensätze.
Was ist der beste Weg für diese Abfrage
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Antworten:
Wenn Sie alle Zeilen in dieser Tabelle löschen, ist es am einfachsten, die Tabelle abzuschneiden
TRUNCATE TABLE LargeTable
GO
Beim Abschneiden der Tabelle wird die Tabelle einfach geleert. Sie können die WHERE-Klausel nicht verwenden, um das Löschen der Zeilen zu begrenzen, und es werden keine Trigger ausgelöst.
Wenn Sie dagegen mehr als 80-90 Prozent der Daten löschen, sagen Sie, wenn Sie insgesamt 11 Millionen Zeilen haben und 10 Millionen löschen möchten, besteht eine andere Möglichkeit darin, diese 1 Million Zeilen (Datensätze, die Sie behalten möchten) einzufügen ) zu einem anderen Staging-Tisch. Schneiden Sie diese große Tabelle ab und fügen Sie diese 1 Million Zeilen zurück.
Wenn Berechtigungen / Ansichten oder andere Objekte, denen diese große Tabelle als zugrunde liegende Tabelle zugrunde liegt, nicht durch das Löschen dieser Tabelle beeinflusst werden, können Sie diese relativ kleine Anzahl der Zeilen in eine andere Tabelle übertragen, diese Tabelle löschen und eine andere Tabelle mit demselben Schema erstellen und diese importieren Zeilen zurück in diese ex-große Tabelle.
Eine letzte Option, die mir in den Sinn kommt, besteht darin, die Datenbank zu ändern Recovery Mode to SIMPLE
und dann Zeilen in kleineren Stapeln mit einer while-Schleife wie dieser zu löschen.
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
und vergessen Sie nicht, den Wiederherstellungsmodus wieder auf "Voll" zu ändern, und ich denke, Sie müssen ein Backup erstellen, damit es vollständig wirksam wird (der Änderungs- oder Wiederherstellungsmodus).
optimal solution for unknown case
das ist der Traum, nicht wahr ? Leider können Sie nicht jede Krankheit mit einer Pille heilen. Ich habe einige mögliche Lösungen für verschiedene Szenarien vorgeschlagen. Leider gibt es hier keine Splitterkugel.
Die Antwort von @ m-ali ist richtig, aber denken Sie auch daran, dass Protokolle stark wachsen können, wenn Sie die Transaktion nicht nach jedem Block festschreiben und einen Prüfpunkt ausführen. So würde ich es machen und diesen Artikel http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes als Referenz nehmen, mit Leistungstests und Grafiken:
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
und CHECKPOINT
die Protokolle wachsen immer noch. Vielen Dank, dass Sie dies klargestellt haben.
@Deleted_Rows
mit 10000 vergleichen möchten oder dass Sie möglicherweise eine Endlosschleife erhalten, da kleine Datenmengen auf unbestimmte Zeit gelöscht werden. Also WHILE (@Deleted_Rows = 10000)
- sobald es keine vollständige "Seite" von Daten zum Löschen gab, wird es gestoppt. In Ihrer Implementierung wird WHILE (@Deleted_Rows > 0)
die while-Schleife erneut ausgeführt, auch wenn nur eine Zeile gelöscht wurde, und bei der nächsten Ausführung werden möglicherweise auch ein oder zwei zu löschende Zeilen gefunden, was zu einer Endlosschleife führt.
WHILE
Schleife selbst wiederholt berechnen : dateadd(MONTH,-7,GETDATE())
.
WHILE
Schleife gelöscht werden können .
Sie können auch GO + verwenden, wie oft Sie dieselbe Abfrage ausführen möchten.
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
funktionieren? Ich erhalte den Fehler "Gespeicherte Prozedur konnte nicht gefunden werden" . Ohne den GO
Befehl funktioniert es aber gut.
@ Francisco Goldenstein, nur eine kleine Korrektur. Das COMMIT muss verwendet werden, nachdem Sie die Variable festgelegt haben, andernfalls wird das WHILE nur einmal ausgeführt:
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
Diese Variante von M.Ali funktioniert gut für mich. Es löscht einige, löscht das Protokoll und wiederholt. Ich beobachte, wie der Baumstamm wächst, fällt und von vorne anfängt.
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
zu löschende Zeit und auch die WHERE
Klausel zu parametrisieren . Klappt wunderbar!
Wenn Sie bereit (und in der Lage) sind, eine Partitionierung zu implementieren, ist dies eine effektive Technik zum Entfernen großer Datenmengen mit geringem Laufzeitaufwand. Nicht kosteneffektiv für eine einmalige Übung.
Ich konnte innerhalb von Minuten 19 Millionen Zeilen aus meiner Tabelle mit 21 Millionen Zeilen löschen . Hier ist mein Ansatz.
Wenn diese Tabelle einen automatisch inkrementierenden Primärschlüssel enthält , können Sie diesen Primärschlüssel verwenden.
Rufen Sie den Mindestwert des Primärschlüssels der großen Tabelle ab, in der readTime <dateadd (MONTH, -7, GETDATE ()) ist. (Fügen Sie einen Index für readTime hinzu, falls dieser noch nicht vorhanden ist. Dieser Index wird ohnehin zusammen mit der Tabelle in Schritt 3 gelöscht.) Speichern wir es in einer Variablen 'min_primary'
Fügen Sie alle Zeilen mit dem Primärschlüssel> min_primary in eine Staging-Tabelle ein (Speichertabelle, wenn die Anzahl der Zeilen nicht groß ist).
Lass den großen Tisch fallen.
Erstellen Sie die Tabelle neu. Kopieren Sie alle Zeilen von der Staging-Tabelle in die Haupttabelle.
Lassen Sie die Staging-Tabelle fallen.
Sie können kleine Stapel mit einer while-Schleife löschen.
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
Eine andere Verwendung:
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
Optional;
Wenn das Transaktionsprotokoll aktiviert ist, deaktivieren Sie die Transaktionsprotokolle.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
Wenn Sie SQL Server 2016 oder höher verwenden und in Ihrer Tabelle Partitionen basierend auf der Spalte erstellt werden, die Sie löschen möchten (z. B. Zeitstempelspalte), können Sie diesen neuen Befehl verwenden, um Daten nach Partitionen zu löschen.
TRUNCATE TABLE WITH (PARTITIONEN ({|} [, ... n]))
Dies löscht nur die Daten in ausgewählten Partitionen und sollte die effizienteste Methode zum Löschen von Daten aus einem Teil der Tabelle sein, da keine Transaktionsprotokolle erstellt werden und dies genauso schnell wie beim regulären Abschneiden erfolgt, ohne dass alle Daten gelöscht werden vom Tisch.
Nachteil ist, wenn Ihre Tabelle nicht mit Partition eingerichtet ist, müssen Sie auf die alte Schule gehen und die Daten mit regelmäßigem Ansatz löschen und dann die Tabelle mit Partitionen neu erstellen, damit Sie dies in Zukunft tun können, was ich auch getan habe. Ich habe die Partitionserstellung und -löschung in das Einfügeverfahren selbst eingefügt. Ich hatte eine Tabelle mit 500 Millionen Zeilen, daher war dies die einzige Option, um die Löschzeit zu verkürzen.
Weitere Informationen finden Sie unter den folgenden Links: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL Server 2016 Tabelle mit Partitionen abschneiden
Im Folgenden habe ich zuerst die Daten gelöscht, bevor ich die Tabelle mit Partitionen mit den erforderlichen Daten neu erstellen konnte. Diese Abfrage wird während des angegebenen Zeitfensters tagelang ausgeführt, bis die Daten gelöscht werden.
: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()
Wenn ich ohne Schleife sage, kann ich GOTO
Anweisung verwenden, um eine große Anzahl von Datensätzen mit SQL Server zu löschen. exa.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
Auf diese Weise können Sie große Datenmengen mit kleinerer Löschgröße löschen.
Lassen Sie mich wissen, wenn Sie weitere Informationen benötigen.