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())
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é.
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.
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.
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).
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
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
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
@ 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
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.
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.
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'
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).
Laisse tomber la grande table.
Recréez la table. Copiez toutes les lignes de la table intermédiaire dans la table principale.
Déposez la table de préparation.
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
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;
Syntaxe plus courte
select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
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()