Disons que nous avons la table Sales avec 30 colonnes et 500 000 lignes. Je voudrais supprimer 400 000 dans la table (ceux où "toDelete='1'"
).
Mais j'ai quelques contraintes:
TRUNCATE
) mais en faisant un "DELETE ... WHERE..."
(je dois mettre une condition), mais je n'ai trouvé aucun moyen de le faire ...Tout conseil serait le bienvenu pour transformer un
DELETE FROM Sales WHERE toDelete='1'
à quelque chose de plus partitionné et éventuellement journal des transactions gratuit.
L'appel de DELETE FROM TableName
fera la suppression complète en une seule transaction importante. C'est cher.
Voici une autre option qui supprimera les lignes par lots:
deleteMore:
DELETE TOP(10000) Sales WHERE toDelete='1'
IF @@ROWCOUNT != 0
goto deleteMore
Ce que vous voulez, c'est un traitement par lots.
While (select Count(*) from sales where toDelete =1) >0
BEGIN
Delete from sales where SalesID in
(select top 1000 salesId from sales where toDelete = 1)
END
Bien sûr, vous pouvez expérimenter quelle est la meilleure valeur à utiliser pour le lot. J'ai utilisé entre 500 et 50000 en fonction du tableau. Si vous utilisez la suppression en cascade, vous aurez probablement besoin d'un nombre plus petit car vous avez ces enregistrements enfants à supprimer.
Par le passé, j'ai déjà eu recours à une procédure stockée ou à un script qui supprimenrecords. Répéter jusqu'à la fin.
DELETE TOP 1000 FROM Sales WHERE toDelete='1'
Je vais laisser ma réponse ici, car j'ai pu tester différentes approches pour la suppression et la mise à jour en masse (j'ai dû mettre à jour, puis supprimer 125 lignes ou plus, le serveur dispose de 16 Go de RAM, Xeon E5-2680 @ 2.7GHz, SQL Server 2012).
TL; DR : toujours mettre à jour/supprimer par clé primaire. Si vous ne pouvez pas utiliser directement PK, créez une table temporaire et remplissez-la de valeurs PK, puis mettez à jour/supprimez votre table à l'aide de cette table. Utilisez des index pour cela.
J'ai commencé avec la solution de ci-dessus (par @Kevin Aenmey), mais cette approche s'est révélée inappropriée, car ma base de données était active et traitait quelques centaines de transactions à la seconde, ce qui entraînait un blocage. (Il y avait un index pour tous les champs de condition, en utilisant WITH(ROWLOCK)
ne changeait rien).
J'ai donc ajouté une instruction WAITFOR
, qui permettait à la base de données de traiter d'autres transactions.
deleteMore:
WAITFOR DELAY '00:00:01'
DELETE TOP(1000) FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3
IF @@ROWCOUNT != 0
goto deleteMore
Cette approche a permis de traiter environ 1,6 million de lignes/heure pour la mise à jour et environ 0,2 million de lignes/heure pour la suppression.
Les tables temporaires ont beaucoup changé.
deleteMore:
SELECT TOP 10000 Id
INTO #Temp
FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3
DELETE MT
FROM MyTable MT
JOIN #Temp T ON T.Id = MT.Id
/* you can use IN operator, it doesn't change anything
DELETE FROM MyTable WHERE Id IN (SELECT Id FROM #Temp)
*/
IF @@ROWCOUNT > 0 BEGIN
DROP TABLE #Temp
WAITFOR DELAY '00:00:01'
goto deleteMore
END ELSE BEGIN
DROP TABLE #Temp
PRINT 'This is the end, my friend'
END
Cette solution a traité environ 25 millions de lignes/heure pour la mise à jour (15 fois plus rapide) et environ 2,2 millions de lignes/heure pour la suppression (11 fois plus rapide).
Vous devriez essayer de lui donner un indice ROWLOCK
afin qu'il ne verrouille pas la table entière. Toutefois, si vous supprimez un grand nombre de lignes, une escalade des verrous se produira.
Assurez-vous également que la colonne toDelete
contient un index non filtré filter (uniquement pour 1). Si possible, faites-en un bit de colonne, pas varchar (ou ce que c'est maintenant).
DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'
En fin de compte, vous pouvez essayer de parcourir la table et supprimer des morceaux.
Mis à jour
Étant donné que tant que les boucles et les suppressions de morceau sont la nouvelle couleur rose ici, je vais également ajouter ma version (combinée à ma réponse précédente):
SET ROWCOUNT 100
DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'
WHILE @@rowcount > 0
BEGIN
SET ROWCOUNT 100
DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'
END
Mon propre point de vue sur cette fonctionnalité serait le suivant… .. Ainsi, il n'y a pas de code répété et vous pouvez gérer la taille de votre bloc.
DECLARE @DeleteChunk INT = 10000
DECLARE @rowcount INT = 1
WHILE @rowcount > 0
BEGIN
DELETE TOP (@DeleteChunk) FROM Sales WITH(ROWLOCK)
SELECT @rowcount = @@RowCount
END
J'ai utilisé ce qui suit pour supprimer environ 50 millions d'enregistrements -
BEGIN TRANSACTION
DeleteOperation:
DELETE TOP (BatchSize)
FROM [database_name].[database_schema].[database_table]
IF @@ROWCOUNT > 0
GOTO DeleteOperation
COMMIT TRANSACTION
Veuillez noter que garder le BatchSize <5000 coûte moins cher en ressources.
Comme je le suppose, le meilleur moyen de supprimer une quantité énorme d’enregistrements est de le supprimer par Primary Key
. (Qu'est-ce que Primary Key
voir ici )
Vous devez donc générer un script tsql contenant la liste complète des lignes à supprimer, puis exécuter ce script.
Par exemple, le code ci-dessous va générer ce fichier
GO
SET NOCOUNT ON
SELECT 'DELETE FROM DATA_ACTION WHERE ID = ' + CAST(ID AS VARCHAR(50)) + ';' + CHAR(13) + CHAR(10) + 'GO'
FROM DATA_ACTION
WHERE YEAR(AtTime) = 2014
Le fichier de sortie va avoir des disques comme
DELETE FROM DATA_ACTION WHERE ID = 123;
GO
DELETE FROM DATA_ACTION WHERE ID = 124;
GO
DELETE FROM DATA_ACTION WHERE ID = 125;
GO
Et maintenant, vous devez utiliser l'utilitaire SQLCMD
pour exécuter ce script.
sqlcmd -S [Instance Name] -E -d [Database] -i [Script]
Vous pouvez trouver cette approche expliquée ici https://www.mssqltips.com/sqlservertip/3566/deleting-historical-data-from-a-large-highly-concurrent-sql-server-database-table/