Uso de un trabajo del Agente SQL para llamar a procedimientos en un bucle
-
05-07-2019 - |
Pregunta
Estoy preparando un trabajo en SQL Enterprise Manager 2000 para copiar y eliminar registros en un par de tablas de bases de datos. Hemos ejecutado un procedimiento almacenado directo de copia masiva y eliminación, pero podría estar ejecutándolo en millones de filas y, por lo tanto, bloquea el servidor. Estaba interesado en intentar ejecutar el servicio en fragmentos de registro de 100 ish a la vez, para que el servidor no se detenga (esta es una base de datos web en vivo). Quiero que este servicio se ejecute una vez por noche, por eso lo puse en un trabajo de agente. ¿Hay alguna manera de repetir las llamadas a los procedimientos almacenados que realmente copian y eliminan, y luego "dormir"? entre cada llamada para darle tiempo al servidor para ponerse al día? Sé que existe el comando WAITFOR, pero no estoy seguro de si esto mantendrá el procesador o permitirá que ejecute otras consultas mientras tanto.
¡Gracias!
Solución
" Troceado " sus eliminaciones es la forma preferida de eliminar cantidades excesivas de datos sin acumular archivos de registro de transacciones. La publicación de BradC es un ejemplo razonable de esto.
La gestión de dichos bucles se realiza mejor dentro de un único procedimiento almacenado. Para difundir tal trabajo con el tiempo, aún lo mantendría en el procedimiento. Insertar un WAITFOR en el bucle pondrá una " pausa " entre cada conjunto de eliminaciones, si lo considera necesario para tratar posibles problemas de concurrencia. Use un trabajo de Agente SQL para determinar cuándo comienza el procedimiento, y si necesita asegurarse de que se detenga en un momento determinado, también incorpore el ciclo.
Mi giro en este código sería:
-- 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. Volver a leer su publicación implica que primero desea copiar los datos en algún lugar antes de eliminarlos. Para hacer eso, configuraría una tabla temporal, y dentro del bucle primero truncaría la tabla temporal, luego copiaría las claves primarias de los elementos TOP N, insertar en el " archivo " tabla a través de una unión a la tabla temporal, luego elimine la tabla fuente también a través de una unión a la tabla temporal. (Solo un poco más complejo que una eliminación directa, ¿no?)
Otros consejos
No se preocupe por esperar entre bucles, el servidor SQL debe manejar la disputa entre su trabajo de mantenimiento y la actividad regular en el servidor.
Lo que realmente causa el problema en este tipo de situaciones es que todo el proceso de eliminación ocurre de una vez, dentro de una sola transacción. Esto explota el registro de la base de datos y puede causar el tipo de problemas que parece que está experimentando.
Use un bucle como este para eliminar en fragmentos manejables:
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
Puede usar una lógica similar para su copia.
WAITFOR
permitirá que otros procesos 'tengan una oportunidad'. He usado esta técnica para evitar que DELETE grande bloquee la máquina. Cree un ciclo WHILE, elimine un bloque de filas y luego espere unos segundos (o menos, lo que sea apropiado).