web-dev-qa-db-fra.com

Comment supprimer des données volumineuses d'une table en SQL sans journal?

J'ai un grand tableau de données .. Il y a 10 millions d'enregistrements dans ce tableau.

Quel est le meilleur moyen pour cette requête

   Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
89
user3107343
  1. Si vous supprimez toutes les lignes de cette table, l'option la plus simple consiste à tronquer la table. 

    TRUNCATE TABLE LargeTable
    GO
    

    Truncate table videra simplement la table, vous ne pouvez pas utiliser la clause WHERE pour limiter les lignes en cours de suppression et aucun déclencheur ne sera déclenché. 

  2. Par contre, si vous supprimez plus de 80 à 90% des données, si vous avez un total de 11 millions de lignes et que vous souhaitez supprimer 10 millions, vous pouvez également insérer ces 1 million de lignes (enregistrements que vous souhaitez conserver). ) vers une autre table de préparation. Tronquez cette grande table et insérez ces 1 million de lignes. 

  3. Ou si les autorisations/vues ou autres objets ayant cette grande table comme table sous-jacente ne sont pas affectés par la suppression de cette table, vous pouvez obtenir cette quantité relativement petite de lignes dans une autre table, supprimer cette table et créer une autre table avec le même schéma et les importer. rangées dans cette ex-grande table.

  4. Une dernière option à laquelle je peux penser est de changer le Recovery Mode to SIMPLE de votre base de données, puis de supprimer les lignes en lots plus petits en utilisant une boucle while quelque chose comme ça.

    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
    

et n'oubliez pas de changer le mode de récupération en mode complet et je pense que vous devez faire une sauvegarde pour le rendre pleinement affectif (les modes de changement ou de récupération).

147
M.Ali

La réponse de @ m-ALi est correcte, mais gardez également à l'esprit que les journaux peuvent beaucoup se développer si vous ne validez pas la transaction après chaque bloc et effectuez un point de contrôle. Voici comment je le ferais et prendrais cet article http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes comme référence, avec des tests de performance et des graphiques:

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
63

Vous pouvez également utiliser GO + combien de fois vous voulez exécuter la même requête. 

DELETE TOP (10000)  [TARGETDATABASE].[SCHEMA].[TARGETTABLE] 
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
34
Bunkerbuster

Cette variation de M.ALi fonctionne bien pour moi. Il en supprime, efface le journal et se répète. Je regarde le journal grandir, laisser tomber et recommencer.

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
8
Ken Koehler

@ Francisco Goldenstein, juste une correction mineure. Le COMMIT doit être utilisé après avoir défini la variable, sinon le WHILE sera exécuté une seule fois:

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
8
Cassio Veras

Si vous êtes prêt (et capable) d'implémenter le partitionnement, il s'agit d'une technique efficace pour supprimer de grandes quantités de données avec une surcharge de temps d'exécution. Pas rentable pour un exercice unique, cependant.

7
Michael Green

J'ai pu supprimer 19 millions de lignes de mon tableau de 21 millions en quelques minutes. Voici mon approche.

Si vous avez une clé primaire auto-incrémentée sur cette table, vous pouvez utiliser cette clé primaire.

  1. Obtenir la valeur minimale de la clé primaire de la grande table où readTime <dateadd (MONTH, -7, GETDATE ()). (Ajouter index sur readTime, s'il n'est pas déjà présent, cet index sera quand même supprimé avec la table de l'étape 3.). Permet de le stocker dans une variable 'min_primary'

  2. Insérez toutes les lignes ayant la clé primaire> min_primary dans une table de transfert (la table mémoire si le nombre de lignes n'est pas grande). 

  3. Laisse tomber la grande table.

  4. Recréez la table. Copiez toutes les lignes de la table intermédiaire dans la table principale.

  5. Déposez la table de préparation.

4
Arpan Jain

Vous pouvez supprimer de petits lots en utilisant une boucle while, quelque chose comme ceci:

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
3
fnascimento

Une autre utilisation:

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

Optionnel; 

Si le journal des transactions est activé, désactivez les journaux des transactions.

ALTER DATABASE dbname SET RECOVERY SIMPLE;
2
Ali Osman Yavuz

Syntaxe plus courte 

select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
  DELETE TOP (10000) LargeTable 
  WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
1
paparazzo

Si vous utilisez SQL Server 2016 ou version ultérieure et si des partitions sont créées pour votre table en fonction de la colonne que vous essayez de supprimer (par exemple, la colonne Timestamp), vous pouvez utiliser cette nouvelle commande pour supprimer les données par partitions.

TRUNCATE TABLE AVEC (PARTITIONS ({|} [ ... n])) 

Cela supprimera uniquement les données de la (des) partition (s) sélectionnée (s) et devrait constituer le moyen le plus efficace de supprimer des données d’une partie de la table car il ne créera pas de journal des transactions et s’effectuera aussi rapidement que les fichiers tronqués habituels, sans que toutes les données ne soient supprimées. de la table.

L'inconvénient est que si votre table n'est pas configurée avec une partition, vous devez alors aller à l'ancienne et supprimer les données avec une approche régulière, puis recréer la table avec des partitions afin de pouvoir le faire à l'avenir, ce que j'ai fait. J'ai ajouté la création et la suppression de la partition dans la procédure d'insertion elle-même. J'avais une table avec 500 millions de lignes, donc c'était la seule option pour réduire le temps de suppression.

Pour plus de détails, consultez les liens ci-dessous: https://docs.Microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017

Table SQL Server 2016 Truncate avec partitions

Voici ce que j'ai fait en premier pour supprimer les données avant de pouvoir recréer la table avec des partitions contenant les données requises. Cette requête s'exécutera pendant des jours au cours de la période spécifiée jusqu'à la suppression des données.

: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()
0
digitally_inspired