Utilisation d'un travail d'agent SQL pour appeler des procédures dans une boucle
-
05-07-2019 - |
Question
Je suis en train de créer un travail sur SQL Enterprise Manager 2000 pour copier et supprimer des enregistrements dans plusieurs tables de base de données. Nous avons exécuté une procédure de copie en masse et de suppression stockée directement, mais il est possible qu'elle s'exécute sur des millions de lignes et bloque donc le serveur. J'essayais d'essayer de faire fonctionner le service par tranches de 100 disques à la fois, pour que le serveur ne soit pas bloqué (il s'agit d'une base de données Web active). Je veux que ce service fonctionne une fois par nuit, c'est pourquoi je l'ai mis dans un poste d'agent. Existe-t-il un moyen de mettre en boucle les appels aux procédures stockées qui effectuent réellement la copie et la suppression, puis "sommeil"? entre chaque appel pour donner au serveur le temps de rattraper son retard? Je sais qu'il y a la commande WAITFOR, mais je ne sais pas si cela tiendra le processeur ou le laissera exécuter d'autres requêtes entre-temps.
Merci!
La solution
"Chunkifying" vos suppressions est le moyen préféré pour supprimer des quantités excessives de données sans gonfler les fichiers du journal des transactions. Le message de BradC en est un exemple raisonnable.
Il est préférable de gérer ces boucles au sein d’une procédure stockée unique. Pour étendre ce travail dans le temps, je le garderais toujours dans la procédure. Insérer une WAITFOR dans la boucle mettra une "pause". entre chaque série de suppressions, si vous le jugez nécessaire pour traiter les éventuels problèmes de simultanéité. Utilisez un travail d'agent SQL pour déterminer le moment où la procédure démarre - et si vous devez vous assurer qu'elle s'arrête à une certaine heure, intégrez-la également dans la boucle.
Mon comportement sur ce code serait:
-- NOTE: This is a code sample, I have not tested it
CREATE PROCEDURE ArchiveData
@StopBy DateTime
-- Pass in a cutoff time. If it runs this long, the procedure will stop.
AS
DECLARE @LastBatch int
SET @LastBatch = 1
-- Initialized to make sure the loop runs at least once
WHILE @LastBatch > 0
BEGIN
WAITFOR DELAY '00:00:02'
-- Set this to your desired delay factor
DELETE top 1000 -- Or however many per pass are desired
from SourceTable
-- Be sure to add a where clause if you don't want to delete everything!
SET @LastBatch = @@rowcount
IF getdate() > @StopBy
SET @LastBatch = 0
END
RETURN 0
Hmm. La relecture de votre message implique que vous souhaitiez copier les données quelque part avant de les supprimer. Pour ce faire, je mettrais en place une table temporaire. À l’intérieur de la boucle, tout d’abord tronquer la table temporaire, puis copier les clés primaires des éléments TOP N, les insérer dans le fichier "archive". table via une jointure à la table temporaire, puis supprimez la table source également via une jointure à la table temporaire. (Juste un peu plus complexe qu'une suppression directe, n'est-ce pas?)
Autres conseils
Ne vous inquiétez pas de l'attente entre les boucles, SQL Server doit gérer le conflit entre votre travail de maintenance et l'activité habituelle du serveur.
Ce qui cause vraiment le problème dans ce type de situation, c’est que l’ensemble du processus de suppression se déroule en une seule fois, au sein d’une même transaction. Cela fait exploser le journal de la base de données et peut entraîner le type de problèmes que vous pouvez rencontrer.
Utilisez une boucle comme celle-ci pour supprimer des fragments gérables:
DECLARE @i INT
SET @i = 1
SET ROWCOUNT 10000
WHILE @i > 0
BEGIN
BEGIN TRAN
DELETE TOP 1000 FROM dbo.SuperBigTable
WHERE RowDate < '2009-01-01'
COMMIT
SELECT @i = @@ROWCOUNT
END
SET ROWCOUNT 0
Vous pouvez utiliser une logique similaire pour votre copie.
WAITFOR
laissera les autres processus s’essayer. J'ai utilisé cette technique pour empêcher les gros utilisateurs DELETE de verrouiller la machine. Créez une boucle WHILE, supprimez un bloc de lignes, puis WAITFOR quelques secondes (ou moins, selon le cas).