Utilizzo di un processo agente SQL per chiamare le procedure in un ciclo
-
05-07-2019 - |
Domanda
Sto mettendo insieme un lavoro su SQL Enterprise Manager 2000 per copiare ed eliminare i record in un paio di tabelle del database. Abbiamo eseguito una copia di massa diretta ed eliminato la procedura memorizzata, ma potrebbe essere in esecuzione su milioni di righe e quindi si blocca il server. Ero interessato a provare a eseguire il servizio in blocchi di record di 100 ish alla volta, quindi il server non si fermava (questo è un database web live). Voglio che questo servizio venga eseguito una volta a notte, motivo per cui l'ho inserito in un lavoro di agente. Esiste un modo per eseguire il loop delle chiamate alle procedure memorizzate che eseguono effettivamente la copia e l'eliminazione e quindi "sleep" tra una chiamata e l'altra per dare al server il tempo di recuperare il ritardo? So che c'è il comando WAITFOR, ma non sono sicuro che questo possa trattenere il processore o lasciarlo eseguire altre query nel frattempo.
Grazie!
Soluzione
" Chunkifying " le tue eliminazioni sono il modo preferito per eliminare quantità eccessive di dati senza gonfiare i file di registro delle transazioni. Il post di BradC ne è un ragionevole esempio.
La gestione di tali loop viene eseguita al meglio in un'unica procedura memorizzata. Per diffondere tale lavoro nel tempo, lo terrei ancora nella procedura. L'inserimento di un WAITFOR nel loop comporterà una "pausa" tra ogni serie di eliminazioni, se lo ritieni necessario per affrontare eventuali problemi di concorrenza. Utilizzare un processo di SQL Agent per determinare quando inizia la procedura e, se è necessario assicurarsi che si interrompa entro un determinato momento, inserirla anche nel ciclo.
Il mio giro su questo codice sarebbe:
-- 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 rilettura del post implica che si desidera copiare i dati da qualche parte prima di eliminarli. Per fare ciò, ho impostato una tabella temporanea e all'interno del ciclo prima troncare la tabella temporanea, quindi copiare le chiavi primarie degli elementi TOP N, inserirle nell'archivio " tabella tramite un join alla tabella temporanea, quindi eliminare la tabella di origine anche tramite un join alla tabella temporanea. (Solo un po 'più complesso di una cancellazione diretta, non è vero?)
Altri suggerimenti
Non preoccuparti di aspettare tra i loop, il server SQL dovrebbe gestire la contesa tra il tuo lavoro di manutenzione e l'attività normale sul server.
Ciò che realmente causa il problema in questi tipi di situazioni è che l'intero processo di cancellazione avviene tutto in una volta, all'interno di una singola transazione. Questo fa esplodere il registro per il database e può causare il tipo di problemi che sembra si stiano verificando.
Usa un ciclo come questo per eliminare in blocchi gestibili:
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
Puoi usare una logica simile per la tua copia.
WAITFOR
consentirà ad altri processi di "provare". Ho usato questa tecnica per impedire a DELETE di grandi dimensioni di bloccare la macchina. Crea un ciclo WHILE, elimina un blocco di righe e quindi ATTENDI qualche secondo (o meno, qualunque cosa sia appropriata).