J'ai une grande table 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())
J'ai une grande table 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())
Réponses:
Si vous supprimez toutes les lignes de cette table, l'option la plus simple consiste à tronquer la table, quelque chose comme
TRUNCATE TABLE LargeTable
GO
Tronquer la table videra simplement la table, vous ne pouvez pas utiliser la clause WHERE pour limiter les lignes supprimées et aucun déclencheur ne sera déclenché.
D'autre part, si vous supprimez plus de 80 à 90% des données, par exemple, si vous avez un total de 11 millions de lignes et que vous souhaitez supprimer 10 millions, une autre façon serait d'insérer ces 1 million de lignes (enregistrements que vous souhaitez conserver ) vers une autre table intermédiaire. Tronquez ce grand tableau et réinsérez ces 1 million de lignes.
Ou si les autorisations / vues ou d'autres objets qui ont 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 table ex-Large.
Une dernière option à laquelle je peux penser est de changer votre base de données, Recovery Mode to SIMPLE
puis de supprimer les lignes par 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 à plein et je pense que vous devez faire une sauvegarde pour le rendre pleinement affectif (les modes de changement ou de récupération).
optimal solution for unknown case
c'est le rêve n'est-ce pas? Malheureusement, vous ne pouvez pas guérir toutes les maladies avec une seule pilule; J'ai suggéré des solutions possibles pour différents scénarios. Il n'y a malheureusement pas de balle en argent ici.
La réponse @ m-ali est correcte, mais gardez également à l'esprit que les journaux peuvent augmenter beaucoup si vous ne validez pas la transaction après chaque segment 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 performances 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
COMMIT TRANSACTION
et CHECKPOINT
les grumes continuent de croître. Merci d'avoir précisé cela.
@Deleted_Rows
à 10000 ou vous pourriez vous retrouver avec une boucle infinie en raison de la suppression indéfinie de petits ensembles de données. Donc WHILE (@Deleted_Rows = 10000)
- dès qu'il n'y a pas une "page" complète de données à supprimer, elle s'arrête. Dans votre implémentation, WHILE (@Deleted_Rows > 0)
la boucle while s'exécutera à nouveau même si elle n'a supprimé qu'une seule ligne, et la prochaine exécution pourrait également trouver une ligne ou deux à supprimer - résultant en une boucle infinie.
WHILE
boucle elle - même: dateadd(MONTH,-7,GETDATE())
.
WHILE
boucle.
Vous pouvez également utiliser GO + combien de fois vous souhaitez 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
GO xx
censée fonctionner? J'obtiens une erreur "Impossible de trouver la procédure stockée ''" . Sans la GO
commande, cela fonctionne bien.
@Francisco Goldenstein, juste une petite correction. 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
Cette variante de M.Ali fonctionne très bien pour moi. Il en supprime certains, efface le journal et se répète. Je regarde le journal grandir, 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
# of rows
pour supprimer à la fois, ainsi que la WHERE
clause. Fonctionne comme un charme!
Si vous êtes disposé (et capable) à implémenter le partitionnement, c'est une technique efficace pour supprimer de grandes quantités de données avec peu de temps d'exécution. Pas rentable pour un exercice ponctuel, cependant.
J'ai pu supprimer 19 millions de lignes de mon tableau de 21 millions de lignes 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.
Obtenez la valeur minimale de la clé primaire de la grande table où readTime <dateadd (MONTH, -7, GETDATE ()). (Ajoutez un index sur readTime, s'il n'est pas déjà présent, cet index sera de toute façon 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 intermédiaire (table mémoire si le nombre de lignes n'est pas grand).
Déposez la grande table.
Recréez la table. Copiez toutes les lignes de la table intermédiaire vers la table principale.
Supprimez la table intermédiaire.
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;
Si vous utilisez SQL Server 2016 ou version ultérieure et si votre table comporte des partitions créées en fonction de la colonne que vous essayez de supprimer (par exemple, colonne d'horodatage), vous pouvez utiliser cette nouvelle commande pour supprimer des données par partitions.
TABLE TRONCÉE AVEC (PARTITIONS ({|} [, ... n]))
Cela supprimera les données de la ou des partitions sélectionnées uniquement et devrait être le moyen le plus efficace de supprimer des données d'une partie de la table car cela ne créera pas de journaux de transactions et sera effectué aussi rapidement que la troncature normale, mais sans supprimer toutes les donné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 que vous puissiez le faire à l'avenir, ce que j'ai fait. J'ai ajouté la création et la suppression de 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, reportez-vous aux liens ci-dessous: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL Server 2016 Tronquer la table avec des 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 pendant la fenêtre de temps spécifiée jusqu'à ce que les données soient supprimé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()
Si je dis sans boucle, je peux utiliser une GOTO
instruction pour supprimer une grande quantité d'enregistrements à l'aide du serveur SQL. exa.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
de cette façon, vous pouvez supprimer une grande quantité de données avec une taille de suppression plus petite.
laissez-moi savoir si vous avez besoin de plus d'informations.