web-dev-qa-db-fra.com

Méthodes pour accélérer un énorme DELETE FROM <table> sans clauses

Utilisation de SQL Server 2005.

J'effectue un énorme DELETE FROM sans clause where. C'est fondamentalement équivalent à une instruction TRUNCATE TABLE - sauf que je ne suis pas autorisé à utiliser TRUNCATE. Le problème est que le tableau est énorme - 10 millions de lignes, et il faut plus d'une heure pour terminer. Existe-t-il un moyen de le rendre plus rapide sans:

  • Utilisation de Tronquer
  • Désactiver ou supprimer des index?

Le t-log est déjà sur un disque séparé.

Toutes les suggestions sont les bienvenues!

37
tuseau

Ce que vous pouvez faire est de supprimer des lots comme ceci:

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

Où xxx est, disons, 50000

Une modification de cela, si vous souhaitez supprimer un pourcentage très élevé de lignes ...

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
39
gbn

Vous pouvez utiliser la clause TOP pour y parvenir facilement:

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

Je suis d'accord avec les suggestions de regrouper vos suppressions en morceaux gérables si vous n'êtes pas en mesure d'utiliser TRUNCATE, et j'aime la suggestion drop/create pour son originalité, mais je suis curieux de savoir le commentaire suivant dans votre question:

C'est fondamentalement équivalent à une instruction TRUNCATE TABLE - sauf que je ne suis pas autorisé à utiliser TRUNCATE

Je suppose que la raison de cette restriction est liée à la sécurité qui doit être accordée pour tronquer directement une table et au fait qu'elle vous permettrait de tronquer des tables autres que celle qui vous concerne.

En supposant que c'est le cas, je me demande si la création d'une procédure stockée qui utilise TRUNCATE TABLE et utilise "EXECUTE AS" serait considérée comme une alternative viable à l'octroi des droits de sécurité nécessaires pour tronquer directement la table.

J'espère que cela vous donnera la vitesse dont vous avez besoin tout en répondant aux problèmes de sécurité que votre entreprise peut avoir avec l'ajout de votre compte au rôle db_ddladmin.

Un autre avantage de l'utilisation d'une procédure stockée de cette façon est que la procédure stockée elle-même peut être verrouillée afin que seuls des comptes spécifiques soient autorisés à l'utiliser.

Si, pour une raison quelconque, ce n'est pas une solution acceptable et que votre besoin de supprimer les données de ce tableau doit être effectué une fois par jour/heure/etc., je demande qu'un travail d'agent SQL soit créé pour tronquer le tableau. à une heure programmée chaque jour.

J'espère que cela t'aides!

7
Jeff

Sauf tronquer .. seule la suppression par lots peut vous aider.

Vous pouvez supprimer la table et la recréer, avec toutes les contraintes et les index, bien sûr. Dans Management Studio, vous avez la possibilité de scripter une table à supprimer et à créer, il devrait donc s'agir d'une option banale. Mais cela uniquement si vous êtes autorisé à effectuer des actions DDL, ce qui, à mon avis, n'est pas vraiment une option.

5
Marian

Étant donné que cette question est une référence si importante, je publie ce code qui m'a vraiment aidé à comprendre la suppression avec des boucles et également la messagerie dans une boucle pour suivre les progrès.

La requête est modifiée de this question en double. Crédit à @ RLF pour la base de requête.

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;
1
Max xaM