Короткий ответ
Похоже, что данные в msdb.dbo.sysjobschedules
обновляются фоновым потоком в агенте SQL, который идентифицируется как SQLAgent - Schedule Saver
каждые 20 минут (или реже, если xp_sqlagent_notify
он не был вызван и в это время не выполнялись никакие задания).
Для более точной информации, посмотрите next_scheduled_run_date
в msdb.dbo.sysjobactivity
. Он обновляется в режиме реального времени при каждом изменении задания или выполнении задания. В качестве дополнительного бонуса sysjobactivity
данные хранятся правильно (в виде столбца даты и времени), что делает работу с ними намного проще, чем с этими глупыми INT.
Вот краткий ответ:
Это может занять до 20 минут, прежде чем sysjobschedules отражает правду; однако, sysjobactivity всегда будет в курсе. Если вы хотите больше подробностей об этом, или как я понял это ...
Длинный ответ
Если вам захочется проследить за кроликом на мгновение, когда вы позвоните sp_add_jobschedule
, эта цепочка событий запускается:
msdb.dbo.sp_add_jobschedule == calls ==> msdb.dbo.sp_add_schedule
msdb.dbo.sp_attach_schedule
msdb.dbo.sp_attach_schedule == calls ==> msdb.dbo.sp_sqlagent_notify
msdb.dbo.sp_sqlagent_notify == calls ==> msdb.dbo.xp_sqlagent_notify
Теперь, мы не можем преследовать кролика дальше, потому что мы не можем действительно заглянуть в то, что xp_sqlagent_notify
делает. Но я думаю, что мы можем предположить, что эта расширенная процедура взаимодействует со службой агента и сообщает, что в этой конкретной работе и расписании произошли изменения. Запустив трассировку на стороне сервера, мы увидим, что агент SQL немедленно вызывает следующий динамический SQL:
exec sp_executesql N'DECLARE @nextScheduledRunDate DATETIME
SET @nextScheduledRunDate = msdb.dbo.agent_datetime(@P1, @P2)
UPDATE msdb.dbo.sysjobactivity
SET next_scheduled_run_date = @nextScheduledRunDate
WHERE session_id = @P3 AND job_id = @P4',
N'@P1 int,@P2 int,@P3 int,@P4 uniqueidentifier',
20120819,181600,5,'36924B24-9706-4FD7-8B3A-1F9F0BECB52C'
Кажется, что sysjobactivity
он обновляется немедленно и sysjobschedules
обновляется только по расписанию. Если мы изменим новое расписание на один раз в день, например
@freq_type=4,
@freq_interval=1,
@freq_subday_type=1,
@freq_subday_interval=0,
@freq_relative_interval=0,
@freq_recurrence_factor=1,
Мы по-прежнему видим немедленное обновление, sysjobactivity
как указано выше, а затем еще одно обновление после завершения работы. Различные обновления происходят из фона и других потоков в агенте SQL, например:
SQLAgent - Job Manager
SQLAgent - Update job activity
SQLAgent - Job invocation engine
SQLAgent - Schedule Saver
Фоновый поток (поток «Schedule Saver») в конце концов появляется и обновляется sysjobschedules
; из моего первоначального исследования выясняется, что это происходит каждые 20 минут и происходит только в том случае, если xp_sqlagent_notify
он был вызван из-за изменения, внесенного в задание с момента его последнего запуска (я не проводил никаких дальнейших проверок, чтобы увидеть, что произойдет, если одно задание было изменился, и другой был запущен, если поток «Schedule Saver» обновляет оба - я подозреваю, что должен, но оставит это как упражнение для читателя).
Я не уверен, смещен ли 20-минутный цикл с момента запуска агента SQL, или с полуночи, или с чего-то конкретного компьютера. В двух разных экземплярах на одном физическом сервере поток «Расписание по расписанию» обновлялся sysjobschedules
в обоих случаях практически в одно и то же время - 18:31:37 & 18:51:37 на одном и 18:31:39 & 18:51:39 с другой. Я не запускал агент SQL Server одновременно на этих серверах, но существует вероятность того, что время запуска смещено на 20 минут. Я сомневаюсь в этом, но у меня нет времени, чтобы подтвердить это, перезапустив Агент на одном из них и ожидая, пока не произойдет больше обновлений.
Я знаю, кто это сделал, и когда это произошло, потому что я поместил там триггер и захватил его, на случай, если я не смог найти его в трассировке или я случайно отфильтровал его.
CREATE TABLE dbo.JobAudit
(
[action] CHAR(1),
[table] CHAR(1),
hostname SYSNAME NOT NULL DEFAULT HOST_NAME(),
appname SYSNAME NOT NULL DEFAULT PROGRAM_NAME(),
dt DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);
CREATE TRIGGER dbo.schedule1 ON dbo.sysjobactivity FOR INSERT
AS
INSERT dbo.JobAudit([action], [table] SELECT 'I', 'A';
GO
CREATE TRIGGER dbo.schedule2 ON dbo.sysjobactivity FOR UPDATE
AS
INSERT dbo.JobAudit([action], [table] SELECT 'U', 'A';
GO
CREATE TRIGGER dbo.schedule3 ON dbo.sysjobschedules FOR INSERT
AS
INSERT dbo.JobAudit([action], [table] SELECT 'I', 'S';
GO
CREATE TRIGGER dbo.schedule4 ON dbo.sysjobschedules FOR UPDATE
AS
INSERT dbo.JobAudit([action], [table] SELECT 'U', 'S';
GO
Тем не менее, это не трудно поймать со стандартной трассировкой, этот даже выглядит как нединамический DML:
UPDATE msdb.dbo.sysjobschedules
SET next_run_date = 20120817,
next_run_time = 20000
WHERE (job_id = 0xB87B329BFBF7BA40B30D9B27E0B120DE
and schedule_id = 8)
Если вы хотите запустить более отфильтрованную трассировку, чтобы отслеживать это поведение с течением времени (например, сохранение через перезапуск агента SQL вместо запроса по требованию), вы можете запустить тот, который имеет appname = 'SQLAgent - Schedule Saver'
...
Поэтому я думаю, что если вы хотите узнать время следующего запуска сразу, посмотрите sysjobactivity
, нет sysjobschedules
. Эта таблица напрямую обновляется Агентом или его фоновыми потоками («Активность обновления задания», «Диспетчер заданий» и «Механизм вызова задания») по мере того, как происходит действие или когда оно получает уведомление xp_sqlagent_notify
.
Имейте в виду, однако, что очень легко испортить любую из таблиц - поскольку нет защиты от удаления данных из этих таблиц. (Поэтому, если вы решили очистить, например, вы можете легко удалить все строки для этого задания из таблицы действий.) В этом случае я не совсем уверен, каким образом агент SQL Server получает или сохраняет следующую дату выполнения. Возможно, стоит провести дополнительное расследование позже, когда у меня будет немного свободного времени ...