简短答案
看起来其中的数据msdb.dbo.sysjobschedules
是由SQL Agent中的后台线程SQLAgent - Schedule Saver
每20分钟更新一次的,标识为,每20分钟一次(或者,如果xp_sqlagent_notify
未调用且在此期间未运行任何作业,则更不频繁)。
有关更准确的信息,请参阅next_scheduled_run_date
中的msdb.dbo.sysjobactivity
。每当更改作业或运行作业时,都会实时更新。另外,以sysjobactivity
正确的方式(作为日期时间列)存储数据,比起那些愚蠢的INT而言,使用起来要容易得多。
这是简短的答案:
sysjobschedules最多可能需要20分钟才能反映出事实。但是,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 Agent调用了以下动态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 Agent的后台和其他线程,例如:
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 Agent启动时,午夜或与计算机有关的时间有所偏移。在同一台物理服务器上的两个不同实例上,“ Schedule Saver”线程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 Agent重新启动而不是按需持久化),则可以运行appname = 'SQLAgent - Schedule Saver'
...
因此,我认为,如果您想立即知道下一个运行时间,请查看sysjobactivity
,而不是sysjobschedules
。当活动发生时或由通知时,此表由Agent或其后台线程(“更新作业活动”,“作业管理器”和“作业调用引擎”)直接更新xp_sqlagent_notify
。
但是请注意,将两个表中的任何一个都变得非常简单-因为没有防止从这些表中删除数据的保护措施。(因此,例如,如果您决定清理,则可以轻松地从活动表中删除该作业的所有行。)在这种情况下,我不确定SQL Server代理如何获取或保存下一个运行日期。也许以后我有空的时候值得进一步调查...