Использование задания агента SQL для вызова процедур в цикле
-
05-07-2019 - |
Вопрос
Я собираю задание на SQL Enterprise Manager 2000 для копирования и удаления записей в паре таблиц базы данных.Мы запустили обычную хранимую процедуру массового копирования и удаления, но она может работать с миллионами строк и, следовательно, зависать на сервере.Мне было интересно попытаться запустить службу единовременно порциями по 100 записей, чтобы сервер не останавливался (это живая веб-база данных).Я хочу, чтобы эта служба запускалась один раз в ночь, именно поэтому я включил ее в задание агента.Есть ли какой-нибудь способ зациклить вызовы хранимых процедур, которые фактически выполняют копирование и удаление, а затем "спят" между каждым вызовом, чтобы дать серверу время наверстать упущенное?Я знаю, что есть команда WAITFOR, но я не уверен, задержит ли это процессор или позволит ему выполнять другие запросы в то же время.
Спасибо!
Решение
"Разбиениена части" ваших удалений - предпочтительный способ удаления чрезмерных объемов данных без раздувания файлов журнала транзакций.Сообщение Брэдка является разумным примером этого.
Управление такими циклами лучше всего выполнять в рамках одной хранимой процедуры.Чтобы распределить такую работу во времени, я бы все равно включил ее в процедуру.Вставка WAITFOR в цикл приведет к "паузе" между каждым набором удалений, если вы сочтете это необходимым для решения возможных проблем с параллелизмом.Используйте задание агента SQL, чтобы определить, когда начнется процедура - и если вам нужно убедиться, что она остановится к определенному времени, включите это также в цикл.
Мой подход к этому коду был бы следующим:
-- 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
Хм.Перечитывание вашего сообщения подразумевает, что вы хотите сначала куда-нибудь скопировать данные, прежде чем удалять их.Чтобы сделать это, я бы настроил временную таблицу и внутри цикла сначала обрезал временную таблицу, затем скопировал первичные ключи ВЕРХНИХ N элементов, вставил в таблицу "архив" через соединение с временной таблицей, затем удалил исходную таблицу также через соединение с временной таблицей.(Просто немного сложнее, чем прямое удаление, не так ли?)
Другие советы
Не беспокойтесь о ожидании между циклами, SQL server должен обрабатывать конфликт между вашим заданием на обслуживание и обычной активностью на сервере.
Что действительно вызывает проблему в подобных ситуациях, так это то, что весь процесс удаления происходит одновременно, внутри одной транзакции.Это приводит к разрыву журнала базы данных и может вызвать проблемы, с которыми, как кажется, вы столкнулись.
Используйте цикл, подобный этому, для удаления управляемыми фрагментами:
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
Вы можете использовать аналогичную логику для своей копии.
WAITFOR
позволит другим процессам "пуститься в ход".Я использовал этот метод, чтобы остановить блокировку компьютера при большом УДАЛЕНИИ.Создайте цикл WHILE, удалите блок строк, а затем ПОДОЖДИТЕ несколько секунд (или меньше, в зависимости от того, что подходит).