Msdbデータベースのsp_add_jobschedule procを使用して、SQLエージェントジョブに新しいスケジュールを追加するためにT-SQLのコードに取り組んでいます。新しいスケジュール(通常は特定の日時に1回実行)を追加し、すぐにsysjobschedulesおよびsysschedulesの値を確認すると、新しいスケジュールが追加され、SQLエージェントのjob_idに関連付けられていることがわかります。ジョブ。ただし、next_run_dateおよびnext_run_timeの値は0です。私が戻ってきて、2〜3分後に再びそれらを見るとき、それらはまだそれらに0を示しています。ただし、5分または10分後にまた戻ってきたときには、次にスケジュールされた実行に対応する日付と時刻の値が正しく表示されるようになりました。
だから私の質問は:
新しいスケジュールを追加するために使用するコードの例:
exec msdb.dbo.sp_add_jobschedule @job_id = @jobID
, @name = @JobName
, @enabled = 1
, @freq_type = 1
, @freq_interval = 0
, @freq_subday_type = 0
, @freq_subday_interval = 0
, @freq_relative_interval = 0
, @freq_recurrence_factor = 0
, @active_start_date = @ScheduleRunDate
, @active_end_date = 99991231
, @active_start_time = @ScheduleRunTime
, @active_end_time = 235959
ここで、@ jobIDは問題のジョブのjob_idを保持するbinary(16)で、@ ScheduleRunDateと@ScheduleRunTimeはそれぞれ日付と時刻を含むINTです。
msdb.dbo.sysjobschedules
のデータは、SQLエージェントのバックグラウンドスレッドによって20分ごとに更新されているようです(SQLAgent - Schedule Saver
が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が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
は定期的に更新されるようです。新しいスケジュールを1日に1回に変更するとします。
@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
バックグラウンドスレッド(「スケジュールセーバー」スレッド)が最終的に発生し、sysjobschedules
を更新します。私の最初の調査から、これは20分ごとであるように見え、最後に実行してからジョブに変更が加えられたためにxp_sqlagent_notify
が呼び出された場合にのみ発生します(何が起こるかを確認するためのさらなるテストは実行しませんでした) 1つのジョブが変更され、別のジョブが実行された場合、「スケジュールセーバー」スレッドが両方を更新した場合-私はそれを行う必要があると思いますが、読者への演習として残します)。
20分サイクルがSQLエージェントの開始時から、または真夜中から、またはマシン固有の何かからオフセットされているかどうかはわかりません。同じ物理サーバー上の2つの異なるインスタンスで、「スケジュールセーバー」スレッドが両方のインスタンスでsysjobschedules
をほぼ正確に同時に更新しました-1つで18:31:37と18:51:37。 18:31:39と18:51:39。これらのサーバーで同時にSQL Serverエージェントを起動しませんでしたが、起動時間が20分ずれる可能性があります。疑わしいですが、今のところ、そのうちの1つでエージェントを再起動して更新が行われるのを待つことで確認する時間はありません。
トレースでそれが見つからなかった場合、または誤ってそれをフィルターで除外した場合に備えて、トリガーをそこに配置してキャプチャしたため、誰がそれを実行したか、いつ発生したかを知っています。
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エージェントが次の実行日を取得または保存する方法が正確にわかりません。おそらく、私が自由時間があるときに、後でさらに調査する価値があります...
msdb.dbo.sp_help_job
は常に正しい実際のnext_run_date
/next_run_time
を返すように見えます。
sp_get_composite_job_info
を使用します。これは、次の呼び出しを行って実際にnext_run_date/time
を取得します。
IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater
INSERT INTO @xp_results
EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner, @job_id
ELSE
INSERT INTO @xp_results
EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner
sysjobschedule
は信頼できないようなので、sp_help_job
を使用します。