Technical Article

Job Summary

,

Some background: I am not a DBA and it's my first contribution here. Sorry for any typos / errors if any in advance. I develop an application which runs on several SQL servers where I have many jobs with different schedules on each server. To be able to monitor all these servers at once, I wrote an Asp page which executes this script and summarizes the results.

Installation: You can create the script in whatever database you like, while I preferred master.

Parameters: (All parameters are optional)

  • @JobName: The exact name of the Job you want to filter. If omitted, all mathing jobs listed.
  • @ShowDisabled: Inludes or excludes disabled jobs.
  • @ShowUnscheduled: Includes or excludes jobs who have no schedule at all.
  • @JobThresholdSec: If this parameters is positive, shows only the jobs with Last duration above it.
  • @AvgExecThresholdSec: If this parameters is positive, shows only the jobs with Average duration above it.

Output Columns:

  • JobName : Name of the job
  • LastStep : The last step executed / currently executing
  • Info : General information about the status of the job (Running, Disabled, No steps, Not scheduled, Never Run, Failed, Succeeded, Retry, Canceled or Unknown)
  • DatabaseName : The db name of the job
  • Enabled, ScheduleCount, StepCount : Obvious I guess.
  • StartDate : Execution start
  • FinishDate : Execution end (if ended)
  • DurationSec : Seconds between StartDate and FinishDate. If currently running, between StartDate and GetDate(). (i.e. 3828)
  • DurationSecFormatted : DurationSec above, human-readably formatted (i.e. 01:03:48)
  • avgDurationSec : Average execution time in seconds.
  • avgDurationSecFormatted : avgDurationSec above, formatted in hh:mm:ss (i.e. 02:19:12)
  • DurationRatio : The current duration divided by average duration, as a percentage. This might give hint that something might be wrong with the job, if above a threshold of your own. For example a DurationRatio of 140 means that the job is taking / has taken a time 40 percent longer than it normally used to.
  • NextRunDate : The next time the job will run. 
  • StepCommand : The text for the step (SQL / command line, etc) Becomes handy if you have really many jobs, whose details can't be understood from its name.
  • HistoryMessage: The last history message.

Samples:

Default usage lists all enabled jobs with a schedule:

  • exec JobSummaryUtil

You can filter to see only one job at a time:

  • exec JobSummaryUtil 'syspolicy_purge_history'

List all jobs:

  • exec JobSummaryUtil null, 1, 1

List all jobs, whose last execution time is above 1 minute:

  • exec JobSummaryUtil null, 1, 1, 60

List all jobs, whose average execution time is above 1 hour:

  • exec JobSummaryUtil null, 1, 1, 0, 3600
CREATE PROCEDURE JobSummaryUtil
@JobName VARCHAR(255) = null, -- Optional job name filter
@ShowDisabled bit = 0, -- Include disabled jobs?
@ShowUnscheduled bit = 0, -- Include Unscheduled jobs?
@JobThresholdSec INT = 0, -- If positive, show only the jobs with LAST duration above this.
@AvgExecThresholdSec INT = 0 -- If positive, show only the jobs with AVERAGE duration above this.
AS
SELECT *
FROM 
( 
 SELECT JobName, ISNULL(LastStep,'') LastStep,
 CASE WHEN StartDate IS NOT NULL AND FinishDate IS NULL THEN 'Running' 
 WHEN Enabled = 0 THEN 'Disabled' 
 WHEN StepCount = 0 THEN 'No steps' 
 WHEN RunStatus IS NOT NULL THEN RunStatus 
 WHEN ScheduleCount = 0 THEN 'Not scheduled' 
 ELSE 'UNKNOWN' END Info,
 DatabaseName, Enabled, ScheduleCount, StepCount, 
 StartDate, FinishDate, DurationSec, 
 RIGHT('0'+convert(varchar(5),DurationSec/3600),2)+':'+RIGHT('0'+convert(varchar(5),DurationSec%3600/60),2)+':'+ RIGHT('0'+convert(varchar(5),(DurationSec%60)),2) DurationSecFormatted, 
 avgDurationSec,
 RIGHT('0'+convert(varchar(5),avgDurationSec/3600),2)+':'+RIGHT('0'+convert(varchar(5),avgDurationSec%3600/60),2)+':'+ RIGHT('0'+convert(varchar(5),(avgDurationSec%60)),2) avgDurationSecFormatted, 
 CASE WHEN (DurationSec IS NULL OR ISNULL(avgDurationSec, 0) = 0) THEN 0 ELSE CONVERT(DECIMAL(18,2), (100*CAST(DurationSec AS DECIMAL)) / CAST (avgDurationSec as DECIMAL)) END AS DurationRatio, 
 NextRunDate, 
 StepCommand, 
 HistoryMessage 
 FROM 
 ( 
 SELECT j.name JobName,j.enabled Enabled, 
 (select COUNT(1) from msdb..sysjobschedules jss where jss.job_id = j.job_id) ScheduleCount, 
 (select COUNT(1) from msdb..sysjobsteps jps where jps.job_id = j.job_id) StepCount, 
 ls1.job_history_id HistoryID, 
 ls1.start_execution_date StartDate, 
 ls1.stop_execution_date FinishDate, 
 ls1.last_executed_step_id LastStepID, 
 DATEDIFF(SECOND, ls1.start_execution_date, CASE WHEN ls1.stop_execution_date IS NULL THEN GETDATE() ELSE ls1.stop_execution_date END) DurationSec, 
 ISNULL(avgSec, 0) avgDurationSec, 
 ls1.next_scheduled_run_date NextRunDate, 
 st.step_name LastStep, st.command StepCommand, st.database_name DatabaseName, 
 h.message HistoryMessage, 
 CASE WHEN h.job_id IS NULL THEN 'Never Run' ELSE 
 CASE h.run_status 
 WHEN 0 THEN 'Failed' 
 WHEN 1 THEN 'Succeeded' 
 WHEN 2 THEN 'Retry' 
 WHEN 3 THEN 'Canceled' END END RunStatus, 
 h.run_date rawRunDate, 
 h.run_time rawRunTime, 
 h.run_duration rawRunDuration 
 FROM msdb..sysjobactivity ls1 (NOLOCK) 
 INNER JOIN msdb..sysjobs j (NOLOCK) ON ls1.job_id = j.job_id 
 INNER JOIN 
 ( 
 SELECT job_id JobID, MAX(session_id) LastSessionID 
 FROM msdb..sysjobactivity (NOLOCK) 
 GROUP BY job_id 
 ) ls2 ON ls1.job_id = ls2.JobID and ls1.session_id = ls2.LastSessionID 
 LEFT OUTER JOIN msdb..sysjobsteps st (NOLOCK) ON st.job_id = j.job_id and ls1.last_executed_step_id = st.step_id 
 LEFT OUTER JOIN msdb..sysjobhistory h (NOLOCK) ON h.instance_id = ls1.job_history_id 
 LEFT OUTER JOIN 
 ( 
 SELECT j.job_id JobID, SUM(h.avgSecs) avgSec 
 FROM msdb..sysjobs j (NOLOCK) 
 INNER JOIN 
 ( 
 SELECT job_id, step_id, AVG(run_duration/10000*3600 + run_duration%10000/100*60 + run_duration%100) avgSecs 
 FROM msdb..sysjobhistory 
 WHERE step_id > 0 AND run_status = 1 
 GROUP BY job_id,step_id 
 ) h on j.job_id = h.job_id 
 GROUP BY j.job_id 
 ) jobavg ON jobavg.JobID = j.job_id 
 )jj 
 WHERE (@ShowDisabled = 1 OR (StartDate IS NOT NULL AND FinishDate IS NULL) OR Enabled = 1) 
 AND (@JobName IS NULL OR JobName = @JobName) 
 AND (@ShowUnscheduled = 1 OR (StartDate IS NOT NULL AND FinishDate IS NULL) OR ScheduleCount > 0) 
 AND (@JobThresholdSec = 0 OR DurationSec >= @JobThresholdSec) 
 AND (@AvgExecThresholdSec = 0 OR avgDurationSec >= @AvgExecThresholdSec) 
)x 
ORDER BY CASE Info 
WHEN 'Running' THEN 0 
WHEN 'Failed' THEN 1 
WHEN 'Retry' THEN 2
WHEN 'Succeeded' THEN 3
WHEN 'Canceled' THEN 4
WHEN 'No steps' THEN 5 
WHEN 'Not scheduled' THEN 6
WHEN 'Disabled' THEN 7
WHEN 'Never Run' THEN 8
WHEN 'UNKNOWN' THEN -1
ELSE -2 END, NextRunDate, JobName

Rate

5 (10)

You rated this post out of 5. Change rating

Share

Share

Rate

5 (10)

You rated this post out of 5. Change rating