Die Anweisung zum Löschen in SQL ist sehr langsam


77

Ich habe Aussagen wie diese, die abgelaufen sind:

DELETE FROM [table] WHERE [COL] IN ( '1', '2', '6', '12', '24', '7', '3', '5')

Ich habe versucht, einen nach dem anderen zu machen:

DELETE FROM [table] WHERE [COL] IN ( '1' )

und bis jetzt ist es bei 22 Minuten und geht noch.

Die Tabelle enthält 260.000 Zeilen und vier Spalten.

Hat jemand eine Idee, warum dies so langsam ist und wie man es beschleunigt? Ich habe einen nicht eindeutigen, nicht gruppierten Index für die [COL], auf der ich das WHERE mache. Ich verwende SQL Server 2008 R2

Update: Ich habe keine Trigger auf dem Tisch.


1
Was passiert, wenn Sie es tun where [col] = '1'?
Alex Gitelman

3
Haben Sie den Ausführungsplan für die Abfrage überprüft, um festzustellen, ob er etwas anzeigt?
Taryn

3
Die DELETE-Anweisung ist aufgrund des Protokolls im Allgemeinen langsam. TRUNCATE ist schneller. In dieser Situation können Sie TRUNCATE jedoch nicht verwenden. Ich habe keine weitere Ahnung
Codingbiz


Es muss einen Auslöser oder etwas geben, das blockiert.
Sergey Kalinichenko

Antworten:


88

Dinge, die dazu führen können, dass ein Löschvorgang langsam ist:

  • viele Datensätze löschen
  • viele Indizes
  • fehlende Indizes für Fremdschlüssel in untergeordneten Tabellen. (Vielen Dank an @CesarAlvaradoDiaz für die Erwähnung in den Kommentaren)
  • Deadlocks und Blockieren
  • löst aus
  • Kaskadenlöschung (diese zehn übergeordneten Datensätze, die Sie löschen, können bedeuten, dass Millionen untergeordneter Datensätze gelöscht werden)
  • Transaktionsprotokoll muss wachsen
  • Viele Fremdschlüssel zu überprüfen

Sie können also herausfinden, was blockiert, und das Problem beheben oder die Löschvorgänge außerhalb der Geschäftszeiten ausführen, wenn sie die normale Produktionslast nicht beeinträchtigen. Sie können das Löschen stapelweise ausführen (nützlich, wenn Sie Trigger, Kaskadenlöschvorgänge oder eine große Anzahl von Datensätzen haben). Sie können die Indizes löschen und neu erstellen (am besten, wenn Sie dies auch außerhalb der Geschäftszeiten tun können).


28
Ich hatte einen Fall, als ich versuchte, aus einer Tabelle mit ~ 1 Million Zeilen zu löschen, aber es dauerte ewig. Das Abfragen der Zeilen mit select *war schnell, aber das Löschen war wahnsinnig langsam. Dann wurde mir klar, dass es einen Fremdschlüssel für die Tabelle von einem anderen mit 2 Milliarden (!) Zeilen gab. Natürlich wurde die FK-Spalte nicht indiziert. Lösung: FK gelöscht, Zeilen gelöscht, FK neu erstellt. Die Neuerstellung des FK dauerte noch einige Zeit, war aber viel schneller.
Daniel Dinnyes

5
Wichtig: Wenn Sie Fremdschlüssel haben, sollten diese in Ihren Tabellen indiziert werden
Cesar Alvarado Diaz

Ist es eine gute Praxis, den Trigger zu deaktivieren, während eine große Datensatzliste aus einer Tabelle gelöscht wird?
Aagjalpankaj

1
@Aviator, nur wenn Ihr Löschskript den Trigger verarbeitet. Andernfalls werden Sie wahrscheinlich ein massives Datenintegritätsproblem verursachen.
HLGEM

64
  1. Deaktivieren Sie CONSTRAINT

    ALTER TABLE [TableName] NOCHECK CONSTRAINT ALL;

  2. Index deaktivieren

    ALTER INDEX ALL ON [TableName] DISABLE;

  3. Index neu erstellen

    ALTER INDEX ALL ON [TableName] REBUILD;

  4. CONSTRAINT aktivieren

    ALTER TABLE [TableName] CHECK CONSTRAINT ALL;

  5. Erneut löschen


15
Das hat funktioniert, aber eine Erklärung wäre schön gewesen.
cdonner

2
Ein Nachteil ist, dass die alter index all on mytable disablemeisten Abfragen nicht mehr funktionieren. "Der Abfrageprozessor kann keinen Plan erstellen". Sie scheinen wieder zu funktionieren, sobald die Indizes neu erstellt und die Einschränkungen wieder aktiviert wurden.
Ed Avis

Wenn Sie die Indizes für die Tabelle deaktivieren, werden auch alle Fremdschlüssel deaktiviert, die auf die Tabelle verweisen. Dies könnte die Erklärung für die Beschleunigung sein. Anschließend können Sie select referrer, fk from foreign_keys where is_disabled = 1die Warnmeldungen überprüfen, die beim Deaktivieren der Indizes angezeigt wurden. Dann für jede betroffene untergeordnete Tabelle , alter table mychild with check check constraint all. Dies könnte einige untergeordnete Zeilen finden, die jetzt gelöscht werden müssen!
Ed Avis

Möglicherweise erhalten Sie auch FKs, die als "nicht vertrauenswürdig" gekennzeichnet sind. Korrigieren Sie select referrer, fk from foreign_keys where is_not_trusted = 1diese ebenfalls.
Ed Avis

2
WARNUNG: Das Neuerstellen von Indizes in einer BIG-Tabelle (2 Millionen +) nimmt viel Zeit in Anspruch (Stunden ...). In meinem Fall war es besser, einfach wie in @ Andomars Antwort zu tun.
ル ち. ん だ

21

Das Löschen vieler Zeilen kann sehr langsam sein. Versuchen Sie, jeweils einige zu löschen, z.

delete top (10) YourTable where col in ('1','2','3','4')
while @@rowcount > 0
    begin
    delete top (10) YourTable where col in ('1','2','3','4')
    end

4

Präventivmaßnahmen

Suchen Sie mit Hilfe von SQL Profilernach der Grundursache für dieses Problem. Möglicherweise wird Triggersdie Ausführung verzögert. Es kann alles sein. Vergessen Sie nicht, das Database Nameund Object Namewährend des TraceStarts auszuwählen, um das Scannen unnötiger Abfragen auszuschließen ...

Filtern von Datenbanknamen

Filterung von Tabellen / gespeicherten Prozeduren / Triggernamen

Korrekturmaßnahme

Wie Sie sagten, enthält Ihre Tabelle 260.000 Datensätze ... und IN Predicatesechs Werte. Jetzt wird jeder Datensatz 260.000 Mal nach jedem Wert in durchsucht IN Predicate. Stattdessen sollte es die innere Verbindung wie unten sein ...

Delete K From YourTable1 K
Inner Join YourTable2 T on T.id = K.id

Fügen Sie die IN PredicateWerte in ein Temporary Tableoder einLocal Variable


3

Wenn die Tabelle, aus der Sie löschen, BEFORE / AFTER DELETE-Trigger hat, kann etwas darin Ihre Verzögerung verursachen.

Wenn Sie Fremdschlüssel haben, die auf diese Tabelle verweisen, können zusätzliche UPDATEs oder DELETEs auftreten.


3

In meinem Fall war die Datenbankstatistik beschädigt. Die Aussage

delete from tablename where col1 = 'v1' 

dauerte 30 Sekunden, obwohl es aber keine passenden Datensätze gab

delete from tablename where col1 = 'rubbish'

lief sofort

Laufen

update statistics tablename

Das Problem wurde behoben


2

Es ist möglich, dass andere Tabellen eine FK-Einschränkung für Ihre [Tabelle] haben. Daher muss die Datenbank diese Tabellen überprüfen, um die referenzielle Integrität aufrechtzuerhalten. Selbst wenn Sie alle benötigten Indizes haben, die diesen FKs entsprechen, überprüfen Sie deren Höhe.

Ich hatte die Situation, dass NHibernate fälschlicherweise doppelte FKs in denselben Spalten erstellt hat, jedoch mit unterschiedlichen Namen (was von SQL Server zugelassen wird). Die Ausführung der DELETE-Anweisung wurde drastisch verlangsamt.


1
Dies sollte ein Kommentar sein, keine Antwort. Mit etwas mehr Wiederholungen können Sie Kommentare posten .
IKavanagh

0

Ist [COL] wirklich ein Zeichenfeld, das Zahlen enthält, oder können Sie die einfachen Anführungszeichen um die Werte entfernen? @Alex hat Recht, dass IN langsamer als = ist. Wenn Sie dies tun können, sind Sie besser dran:

DELETE FROM [table] WHERE [COL] = '1'

Aber noch besser ist es, Zahlen anstelle von Zeichenfolgen zu verwenden, um die Zeilen zu finden (SQL mag Zahlen):

 DELETE FROM [table] WHERE [COL] = 1

Vielleicht versuchen Sie:

 DELETE FROM [table] WHERE CAST([COL] AS INT) = 1

Stellen Sie in beiden Fällen sicher, dass Sie einen Index für die Spalte [COL] haben, um den Tabellenscan zu beschleunigen.


Leider enthält die Spalte Buchstaben und Zahlen und ich habe einen Index = /.
Kyle

Ich bin nicht sicher, ob es helfen wird, aber eine Sache, die wir hier tun, ist, eine "IsActive" -Spalte in alle unsere wichtigen Tabellen einzufügen, damit wir ein Feld aktualisieren können, anstatt Zeilen zu löschen. Praktisch für die Prüfung und weniger umständlich beim Wiederherstellen von Indizes beim Löschen. Natürlich müssen alle nachfolgenden Ansichten / Abfragen / Prozesse / Funktionen "WHERE ISACTIVE = 1" enthalten.
Russell Fox

2
Das Umwandeln aller Werte in der Datenbank in ein int kann die Leistung erheblich beeinträchtigen. Es kann auch verhindern, dass SQL Server den Index verwenden kann. Wenn Ihr Vorschlag also Auswirkungen hat, ist er höchstwahrscheinlich schlimmer.
Stefan Steinegger

0

Überprüfen Sie den Ausführungsplan dieser Löschanweisung. Überprüfen Sie, ob die Indexsuche verwendet wird. Auch was ist der Datentyp von col?

Wenn Sie einen falschen Datentyp verwenden, ändern Sie die Aktualisierungsanweisung (z. B. von '1' auf 1 oder N'1 ').

Wenn ein Index-Scan verwendet wird, sollten Sie einen Abfragehinweis verwenden .


0

Ich habe diesen Artikel gelesen, er war wirklich hilfreich bei der Behebung von Unannehmlichkeiten

https://support.microsoft.com/en-us/kb/224453

Dies ist ein Fall von Waitresource KEY: 16: 72057595075231744 (ab74b4daaf17)

-- First SQL Provider to find the SPID (Session ID)

-- Second Identify problem, check Status, Open_tran, Lastwaittype, waittype, and waittime
-- iMPORTANT Waitresource select * from sys.sysprocesses where spid = 57

select * from sys.databases where database_id=16

-- with Waitresource check this to obtain object id 
select * from sys.partitions where hobt_id=72057595075231744

select * from sys.objects where object_id=2105058535

0

Wenn Sie alle Datensätze in der Tabelle löschen und nicht nur einige wenige, ist es möglicherweise viel schneller, die Tabelle einfach zu löschen und neu zu erstellen.


0

Nachdem ich ein SSIS-Paket überprüft hatte (da ein SQL Server Befehle sehr langsam ausführte), das in einem unserer Clients etwa 5 bis 4 Jahre vor dem Zeitpunkt eingerichtet wurde, als ich dies schrieb, stellte ich fest, dass die folgenden Aufgaben vorhanden waren: 1 ) Daten aus einer XML-Datei in eine Tabelle namens [Importbarcdes] einfügen.

2) Zusammenführungsbefehl für eine andere Zieltabelle unter Verwendung der oben genannten Tabelle als Quelle.

3) "Aus [Importbarcodes] löschen", um die Tabelle der Zeile zu löschen, die eingefügt wurde, nachdem die XML-Datei von der Aufgabe des SSIS-Pakets gelesen wurde.

Nach einer kurzen Überprüfung dauerte die Ausführung aller Anweisungen (SELECT, UPDATE, DELETE usw.) in der Tabelle ImportBarcodes, die nur eine Zeile enthielten, ca. 2 Minuten.

Erweiterte Ereignisse zeigten eine ganze Menge PAGEIOLATCH_EX-Wartebenachrichtigungen.

In der Tabelle waren keine Indizes vorhanden und es wurden keine Trigger registriert.

Bei genauer Betrachtung der Eigenschaften der Tabelle auf der Registerkarte "Speicher" und im Abschnitt "Allgemein" wurden im Feld "Datenbereich" mehr als 6 GIGABYTES Speicherplatz angezeigt, der auf Seiten zugewiesen wurde.

Was ist passiert:

Die Abfrage wurde in den letzten 4 Jahren jeden Tag über einen guten Teil der Zeit ausgeführt, wobei Daten in die Tabelle eingefügt und gelöscht wurden, wobei nicht verwendete Auslagerungsdateien zurückblieben, ohne sie freizugeben.

Dies war der Hauptgrund für die Warteereignisse, die von der erweiterten Ereignissitzung erfasst wurden, und für die langsam ausgeführten Befehle auf dem Tisch.

Durch Ausführen wurde ALTER TABLE ImportBarcodes REBUILDdas Problem behoben, durch das der gesamte nicht verwendete Speicherplatz freigegeben wurde. TRUNCATE TABLE ImportBarcodeshat etwas Ähnliches getan, mit dem einzigen Unterschied, dass alle Auslagerungsdateien und Daten gelöscht wurden.


0

Älteres Thema, aber immer noch relevant. Ein weiteres Problem tritt auf, wenn ein Index so fragmentiert wurde, dass er eher ein Problem als eine Hilfe darstellt. In einem solchen Fall besteht die Antwort darin, den Index neu zu erstellen oder zu löschen und neu zu erstellen und die Löschanweisung erneut auszugeben.


0

Als Erweiterung von Andomars Antwort hatte ich oben ein Szenario, in dem die ersten 700.000.000 Datensätze (von ~ 1,2 Milliarden) sehr schnell verarbeitet wurden, wobei Blöcke von 25.000 Datensätzen pro Sekunde (ungefähr) verarbeitet wurden. Aber dann dauert es 15 Minuten, um eine Charge von 25.000 zu machen. Ich habe die Blockgröße auf 5.000 Datensätze reduziert und sie wurde auf die vorherige Geschwindigkeit zurückgesetzt. Ich bin nicht sicher, welchen internen Schwellenwert ich erreicht habe, aber das Problem bestand darin, die Anzahl der Datensätze weiter zu reduzieren, um die Geschwindigkeit wiederzuerlangen.


-7

Öffnen Sie CMD und führen Sie diese Befehle aus

NET STOP MSSQLSERVER
NET START MSSQLSERVER

Dadurch wird die SQL Server-Instanz neu gestartet. Versuchen Sie erneut, nach Ihrem Löschbefehl auszuführen

Ich habe diesen Befehl in einem Batch-Skript und führe ihn von Zeit zu Zeit aus, wenn ich auf solche Probleme stoße. Ein normaler PC-Neustart ist nicht derselbe. Daher ist ein Neustart der Instanz der effektivste Weg, wenn Probleme mit Ihrem SQL Server auftreten.


Dies beantwortet nicht die ursprüngliche Frage, oder wenn ja, erklären Sie nicht, warum dies helfen würde.
Mjuarez
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.