• subscribe
August 28, 2008 12:00 AM

Plugging the Gaps in SQL Server Job Tracking

Track down and solve job-scheduling problems using two helpful scripts
SQL Server Pro
InstantDoc ID #99593
Downloads
99593.zip

When the scripts are run, the next_run_date is recorded from sysjobschedules for each job and schedule. To get the most up-to-date values for the next_run_date, the scripts also call the (undocumented) extended stored procedure master..xp_sqlagent_enum_ jobs, which queries the SQL Server Agent to return the latest data. If the returned next-run values are later than those in sysjobschedules, the script overwrites the previously recorded values.

Once the scripts know that a job is scheduled to run at a certain date, they can extrapolate all the run dates for each schedule. Starting at the date in next_run_date, the script adds the appropriate frequency interval (i.e., days, weeks, months) and records all run dates in the #JobScheduledDates table, until the datetime value in max_datetime_to_consider is reached. Next, the script returns and determines all run dates between next_run_date and min_datetime_to_consider.

After obtaining the run dates, you need to calculate the times when jobs run in each run date. To do so, you start with the active_start_time: If a job is scheduled to run once a day, the active_start_time is the runtime. If jobs run every several (say x) minutes or hours, you add x minutes (or hours) to the active_start_time until the end of the day is reached.

Finding Missed Jobs and Conflicting Schedules
The run dates and times are stored in the #JobScheduledDatesAndTimes table. To find missed jobs, the JobScript_FindMissedJobs.sql script compares the content of this table with the values in sysjobhistory to check whether the first step was invoked on (or a few seconds after) the scheduled date and time. You can now easily obtain conflicting schedules by comparing the dates and times in #JobScheduledDatesAndTimes. If two run dates and times are only several seconds apart (the number of seconds is configurable and is stored in the variable @TimeGapInSeconds), then the schedules indeed conflict.

Special Situations
Obtaining the run dates can still be challenging for some schedules, and the scripts address such cases. One such example is when, every few months, a job is scheduled to run on the third or last weekend day. The code to determine whether a day is a weekend or a business day depends on the @@DATEFIRST settings; Listing 2 shows an example of this code. The code in Web Listing 1 returns the nth day of the month (where n is the freq_relative_interval value in Table 1 that corresponds to jobs with freq_type = 32 and the freq_interval is either 9 or 10).

Quick Job Tracking
You can also query SQL Server Agent scheduling information by using Windows Management Instrumentation (WMI) or another scripting language besides T-SQL. The main advantage of my scripts, though, is that they can return missed jobs and conflicting schedules quickly, using simple T-SQL code, even when many SQL Server jobs are on the server. However, the scripts have a couple potential limitations. If you run the scripts against a large time window (e.g., several years), they might take a while to run. Additionally, the scripts don’t consider scheduling changes. If a schedule changes at some point in time, the information reported for the dates prior to the change might be incorrect. If you keep in mind these minor caveats, the scripts should help you improve your ability to keep a closer watch on SQL Server job scheduling.



ARTICLE TOOLS

Comments
  • marius
    4 years ago
    Nov 19, 2008

    Also see http://weblogs.sqlteam.com/peterl/archive/2008/10/10/Keep-track-of-all-your-jobs-schedules.aspx

  • Gabriela
    4 years ago
    Sep 24, 2008

    (similar change for a.freq_subday_type = 4 above in the script.)
    -- Finally, we do the same as above for all jobs that run every Y hours.

    SET @i = 0
    SET @Cnt = 1

    WHILE @Cnt > 0
    BEGIN
    INSERT INTO #JobScheduledDatesAndTimes (job_id, schedule_name, RunDateTime)
    SELECT b.job_id, b.schedule_name, DATEADD(ss, @i*3600*a.freq_subday_interval + b.StartTimeInSecFromStartOfDay, b.RunDate)
    FROM #JobScheduleInfo a
    INNER JOIN #JobScheduledDates b
    ON a.job_id = b.job_id
    AND a.schedule_name = b.schedule_name
    WHERE a.freq_subday_type = 8
    -- We need to make sure that we don't cross over to the next day.
    AND DATEPART(day, DATEADD(ss, @i*3600*a.freq_subday_interval + b.StartTimeInSecFromStartOfDay, b.RunDate)) =
    DATEPART(day, b.RunDate)
    --and the time is whithin the daily interval (Gabriela Nanau)
    AND replace(convert(varchar(8),DATEADD(ss, @i*3600*a.freq_subday_interval + b.StartTimeInSecFromStartOfDay, b.RunDate),114),':',')<=
    REPLICATE('0', 6 - LEN(LTRIM(RTRIM(CAST(active_end_time AS VARCHAR(6))))) )
    + LTRIM(RTRIM(CAST(active_end_time AS VARCHAR(6))))
    -- Also consider the min/max_datetime_to_consider
    AND DATEADD(ss, @i*3600*a.freq_subday_interval + b.StartTimeInSecFromStartOfDay, b.RunDate) >= a.min_datetime_to_consider
    AND DATEADD(ss, @i*3600*a.freq_subday_interval + b.StartTimeInSecFromStartOfDay, b.RunDate) <= a.max_datetime_to_consider

    SET @Cnt = @@ROWCOUNT
    SET @i = @i + 1
    END

  • Gabriela
    4 years ago
    Sep 24, 2008

    Great solution and a lot of work! I had this kind of script on my to-do list for some time but I never found the time to start working on it. So here comes your script and saves the day!
    I made a small adjustment to JobScript_FindMissedJobs.sql, so that for jobs that run several times in a day will also consider the maximum time during the day. So if a job is scheduled to run daily, every hour between 2:00 Am and 22:59:59 PM it will not consider a valid time to run 23:00:00 (step # 3, please see below my comment (Gabriela Nanau).

    Another thing I would mention is that because the values in field FirstJobRunAfterMissedSchedule come from sysjobhistory this date is as accurate as sysjobhistory. If the interval considered for analyisis is long enough and the job history was overwritten by newer events the value in FirstJobRunAfterMissedSchedule might not necessarily be the first run after the schedule, but the first one still available in the history. As long as the studied interval don't go before the oldest record in job jistory, the script works fine.

    Once again, this is a good script and provides a lot of help for a DBA! Congratulations!

  • Marcos
    4 years ago
    Sep 04, 2008

    Good and deep aproach to a real problem, this is the type of article that I look for from time to time, I like to know the does and doesn't of SQL Server and the impossibility to track the jobs is a large doesn't...
    Thank you for sharing your code....

You must log on before posting a comment.

Are you a new visitor? Register Here