Created
October 15, 2015 16:04
-
-
Save TheRockStarDBA/20d2815a5dfc52e483bf to your computer and use it in GitHub Desktop.
Find the Last_Run_Duration with Avg, Max and Min for SQL Agent RUN Times along with schedules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SET NOCOUNT ON | |
GO | |
/*********************************************************************************************************** | |
************************************************************************************************************ | |
Author : KIN SHAH - Sr.DBA (MS SQL Server and SybaseASE) | |
Date : Feb 18th 2014 | |
Purpose : Find the Last_Run_Duration with Avg, Max and Min for SQL Agent RUN Times. | |
This script can be used to do a trend analysis using SSRS. | |
RDBMS : Compatible with SQL Server 2005, 2008 R2, 2012, 2014 and 2016 :-) | |
Compatible with servers running standard and binary Collations as well. | |
Version : 1.0.0 | |
Change History : ** NONE ** | |
License : This script is free to download and use for personal, educational, and internal | |
corporate purposes, provided that this header is preserved. Redistribution or sale | |
of this script in whole or in part, is prohibited without the author's express | |
written consent. | |
************************************************************************************************************ | |
************************************************************************************************************/ | |
-- DROP all temp tables if existed | |
IF OBJECT_ID('tempdb..#temp_runhistory') IS NOT NULL | |
DROP TABLE #temp_runhistory; | |
IF OBJECT_ID('tempdb..#temp_jobhistory') IS NOT NULL | |
DROP TABLE #temp_jobhistory; | |
IF OBJECT_ID('tempdb..#schedules') IS NOT NULL | |
DROP TABLE #schedules; | |
IF OBJECT_ID('tempdb..#finalReport') IS NOT NULL | |
DROP TABLE #finalReport; | |
-- Variable Declaration | |
DECLARE @num_days INT | |
DECLARE @first_day DATETIME | |
DECLARE @last_day DATETIME | |
DECLARE @first_num INT | |
IF @num_days IS NULL | |
SET @num_days = 30 -- this will be how much the job history will be utilize to analyze the average run times !! | |
SET @last_day = getdate() | |
SET @first_day = dateadd(dd, - @num_days, @last_day) | |
SELECT @first_num = cast(year(@first_day) AS CHAR(4)) + replicate('0', 2 - len(month(@first_day))) + cast(month(@first_day) AS VARCHAR(2)) + replicate('0', 2 - len(day(@first_day))) + cast(day(@first_day) AS VARCHAR(2)) | |
SELECT h.instance_id | |
,h.job_id | |
,j.name | |
,h.step_id | |
,h.step_name | |
,h.sql_message_id | |
,h.sql_severity | |
,h.run_status | |
,'run_date' = cast(h.run_date AS VARCHAR(8)) | |
,'run_time' = replicate('0', 6 - len(h.run_time)) + cast(h.run_time AS VARCHAR(6)) | |
,'run_datetime' = left(cast(h.run_date AS VARCHAR(8)), 4) + '/' + substring(cast(h.run_date AS VARCHAR(8)), 5, 2) + '/' + right(cast(h.run_date AS VARCHAR(8)), 2) + ' ' + left(replicate('0', 6 - len(h.run_time)) + cast(h.run_time AS VARCHAR(6)), 2) + ':' + substring(replicate('0', 6 - len(h.run_time)) + cast(h.run_time AS VARCHAR(6)), 3, 2) + ':' + right(replicate('0', 6 - len(h.run_time)) + cast(h.run_time AS VARCHAR(6)), 2) | |
,run_duration = cast(h.run_duration AS VARCHAR(20)) | |
,run_duration_conv = CASE | |
WHEN (len(cast(h.run_duration AS VARCHAR(20))) < 3) | |
THEN cast(h.run_duration AS VARCHAR(6)) | |
WHEN (len(cast(h.run_duration AS VARCHAR(20))) = 3) | |
THEN LEFT(cast(h.run_duration AS VARCHAR(6)), 1) * 60 --min | |
+ RIGHT(cast(h.run_duration AS VARCHAR(6)), 2) --sec | |
WHEN (len(cast(h.run_duration AS VARCHAR(20))) = 4) | |
THEN LEFT(cast(h.run_duration AS VARCHAR(6)), 2) * 60 --min | |
+ RIGHT(cast(h.run_duration AS VARCHAR(6)), 2) --sec | |
WHEN (len(cast(h.run_duration AS VARCHAR(20))) >= 5) | |
THEN (Left(cast(h.run_duration AS VARCHAR(20)), len(h.run_duration) - 4)) * 3600 --hour | |
+ (substring(cast(h.run_duration AS VARCHAR(20)), len(h.run_duration) - 3, 2)) * 60 --min | |
+ Right(cast(h.run_duration AS VARCHAR(20)), 2) --sec | |
END | |
,h.retries_attempted | |
,h.server | |
INTO #temp_jobhistory | |
FROM msdb..sysjobhistory h | |
,msdb..sysjobs j | |
WHERE h.job_id = j.job_id | |
AND h.run_date >= @first_num | |
AND h.step_id = 0 | |
option (recompile) | |
SELECT j.job_id | |
,j.name | |
,'Sampling' = ( | |
SELECT count(*) | |
FROM #temp_jobhistory h | |
WHERE h.job_id = j.job_id | |
) | |
,'fromRunDate' = ( | |
SELECT min(run_date) | |
FROM #temp_jobhistory h | |
WHERE h.job_id = j.job_id | |
) | |
,'run_duration_max' = ( | |
SELECT max(run_duration_conv) | |
FROM #temp_jobhistory h | |
WHERE h.job_id = j.job_id | |
) | |
,'run_duration_min' = ( | |
SELECT min(run_duration_conv) | |
FROM #temp_jobhistory h | |
WHERE h.job_id = j.job_id | |
) | |
,'run_duration_avg' = ( | |
SELECT avg(run_duration_conv) | |
FROM #temp_jobhistory h | |
WHERE h.job_id = j.job_id | |
) | |
,'Last_RunDate' = ( | |
SELECT max(run_datetime) | |
FROM #temp_jobhistory h | |
WHERE h.job_id = j.job_id | |
) | |
,'Last_RunStatus' = NULL --(select run_status from #temp_jobhistory h where h.job_id=j.job_id) | |
,'Last_RunDuration' = NULL | |
INTO #temp_runhistory | |
FROM msdb..sysjobs j | |
WHERE j.enabled = 1 -- only look for enabled JOBS !! | |
option (recompile) | |
UPDATE #temp_runhistory | |
SET Last_RunStatus = j.run_status | |
,Last_RunDuration = j.run_duration_conv | |
FROM #temp_jobhistory j | |
WHERE #temp_runhistory.job_id = j.job_id | |
AND #temp_runhistory.Last_RunDate = j.run_datetime | |
AND j.run_datetime = ( | |
SELECT max(run_datetime) | |
FROM #temp_jobhistory j1 | |
WHERE j1.job_id = #temp_runhistory.job_id | |
) | |
---- GET schedules in HUMAN READABLE FORMAT | |
SELECT SJ.job_id | |
,SJ.name AS job_name | |
,CASE freq_type | |
WHEN 1 | |
THEN 'Occurs on ' + STUFF(RIGHT(active_start_date, 4), 3, 0, '/') + '/' + LEFT(active_start_date, 4) + ' at ' + REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME) /* hh:mm:ss 24H */, 9), 14), ':000', ' ') /* HH:mm:ss:000AM/PM then replace the :000 with space.*/ | |
WHEN 4 | |
THEN 'Occurs every ' + CAST(freq_interval AS VARCHAR(10)) + ' day(s) ' + CASE freq_subday_type | |
WHEN 1 | |
THEN 'at ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
WHEN 2 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' second(s)' | |
WHEN 4 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minute(s)' | |
WHEN 8 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hour(s)' | |
ELSE '' | |
END + CASE | |
WHEN freq_subday_type IN ( | |
2 | |
,4 | |
,8 | |
) /* repeat seconds/mins/hours */ | |
THEN ' between ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) + ' and ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
ELSE '' | |
END | |
WHEN 8 | |
THEN 'Occurs every ' + CAST(freq_recurrence_factor AS VARCHAR(10)) + ' week(s) on ' + REPLACE(CASE | |
WHEN freq_interval & 1 = 1 | |
THEN 'Sunday, ' | |
ELSE '' | |
END + CASE | |
WHEN freq_interval & 2 = 2 | |
THEN 'Monday, ' | |
ELSE '' | |
END + CASE | |
WHEN freq_interval & 4 = 4 | |
THEN 'Tuesday, ' | |
ELSE '' | |
END + CASE | |
WHEN freq_interval & 8 = 8 | |
THEN 'Wednesday, ' | |
ELSE '' | |
END + CASE | |
WHEN freq_interval & 16 = 16 | |
THEN 'Thursday, ' | |
ELSE '' | |
END + CASE | |
WHEN freq_interval & 32 = 32 | |
THEN 'Friday, ' | |
ELSE '' | |
END + CASE | |
WHEN freq_interval & 64 = 64 | |
THEN 'Saturday, ' | |
ELSE '' | |
END + '|', ', |', ' ') /* get rid of trailing comma */ | |
+ CASE freq_subday_type | |
WHEN 1 | |
THEN 'at ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
WHEN 2 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' second(s)' | |
WHEN 4 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minute(s)' | |
WHEN 8 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hour(s)' | |
ELSE '' | |
END + CASE | |
WHEN freq_subday_type IN ( | |
2 | |
,4 | |
,8 | |
) /* repeat seconds/mins/hours */ | |
THEN ' between ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) + ' and ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
ELSE '' | |
END | |
WHEN 16 | |
THEN 'Occurs every ' + CAST(freq_recurrence_factor AS VARCHAR(10)) + ' month(s) on ' + 'day ' + CAST(freq_interval AS VARCHAR(10)) + ' of that month ' + CASE freq_subday_type | |
WHEN 1 | |
THEN 'at ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
WHEN 2 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' second(s)' | |
WHEN 4 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minute(s)' | |
WHEN 8 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hour(s)' | |
ELSE '' | |
END + CASE | |
WHEN freq_subday_type IN ( | |
2 | |
,4 | |
,8 | |
) /* repeat seconds/mins/hours */ | |
THEN ' between ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) + ' and ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
ELSE '' | |
END | |
WHEN 32 | |
THEN 'Occurs ' + CASE freq_relative_interval | |
WHEN 1 | |
THEN 'every first ' | |
WHEN 2 | |
THEN 'every second ' | |
WHEN 4 | |
THEN 'every third ' | |
WHEN 8 | |
THEN 'every fourth ' | |
WHEN 16 | |
THEN 'on the last ' | |
END + CASE freq_interval | |
WHEN 1 | |
THEN 'Sunday' | |
WHEN 2 | |
THEN 'Monday' | |
WHEN 3 | |
THEN 'Tuesday' | |
WHEN 4 | |
THEN 'Wednesday' | |
WHEN 5 | |
THEN 'Thursday' | |
WHEN 6 | |
THEN 'Friday' | |
WHEN 7 | |
THEN 'Saturday' | |
WHEN 8 | |
THEN 'day' | |
WHEN 9 | |
THEN 'weekday' | |
WHEN 10 | |
THEN 'weekend' | |
END + ' of every ' + CAST(freq_recurrence_factor AS VARCHAR(10)) + ' month(s) ' + CASE freq_subday_type | |
WHEN 1 | |
THEN 'at ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
WHEN 2 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' second(s)' | |
WHEN 4 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' minute(s)' | |
WHEN 8 | |
THEN 'every ' + CAST(freq_subday_interval AS VARCHAR(10)) + ' hour(s)' | |
ELSE '' | |
END + CASE | |
WHEN freq_subday_type IN ( | |
2 | |
,4 | |
,8 | |
) /* repeat seconds/mins/hours */ | |
THEN ' between ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_start_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) + ' and ' + LTRIM(REPLACE(RIGHT(CONVERT(VARCHAR(30), CAST(convert(VARCHAR(8), STUFF(STUFF(RIGHT('000000' + CAST(active_end_time AS VARCHAR(10)), 6), 3, 0, ':'), 6, 0, ':'), 8) AS DATETIME), 9), 14), ':000', ' ')) | |
ELSE '' | |
END | |
WHEN 64 | |
THEN 'Runs when the SQL Server Agent service starts' | |
WHEN 128 | |
THEN 'Runs when the computer is idle' | |
END AS [Schedule_Description] | |
INTO #schedules | |
FROM msdb.dbo.sysjobs SJ | |
INNER JOIN msdb.dbo.sysjobschedules SJS ON SJ.job_id = SJS.job_id | |
INNER JOIN msdb.dbo.sysschedules SS ON SJS.schedule_id = SS.schedule_id | |
option (recompile); | |
--- Get all details into a temp table with all fancy calculations !! | |
SELECT DISTINCT CONVERT(NVARCHAR(128), SERVERPROPERTY('servername')) AS [Server_Name] | |
,trh.job_id AS 'Job_ID' | |
,trh.name AS 'Job_Name' | |
,trh.Last_RunDate AS 'Last_Run_Date' | |
,'Last_RunStatus' = CASE trh.Last_RunStatus | |
WHEN 0 | |
THEN 'Failed' | |
WHEN 1 | |
THEN 'Succeeded' | |
WHEN 2 | |
THEN 'Retry' | |
WHEN 3 | |
THEN 'Canceled' | |
WHEN 4 | |
THEN 'In progress' | |
END | |
-- MIND BOGGLING CALCULATIONS .... IT will spin your head if you try to decode it !! | |
,'Last_RunDuration (hh:mm:ss)' = cast(trh.Last_RunDuration / 3600 AS VARCHAR(10)) + ':' + replicate('0', 2 - len((trh.Last_RunDuration % 3600) / 60)) + cast((trh.Last_RunDuration % 3600) / 60 AS VARCHAR(2)) + ':' + replicate('0', 2 - len((trh.Last_RunDuration % 3600) % 60)) + cast((trh.Last_RunDuration % 3600) % 60 AS VARCHAR(2)) | |
,'Avg_Duration (hh:mm:ss)' = cast(trh.run_duration_avg / 3600 AS VARCHAR(10)) + ':' + replicate('0', 2 - len((trh.run_duration_avg % 3600) / 60)) + cast((trh.run_duration_avg % 3600) / 60 AS VARCHAR(2)) + ':' + replicate('0', 2 - len((trh.run_duration_avg % 3600) % 60)) + cast((trh.run_duration_avg % 3600) % 60 AS VARCHAR(2)) | |
,'Max_Duration (hh:mm:ss)' = cast(trh.run_duration_max / 3600 AS VARCHAR(10)) + ':' + replicate('0', 2 - len((trh.run_duration_max % 3600) / 60)) + cast((trh.run_duration_max % 3600) / 60 AS VARCHAR(2)) + ':' + replicate('0', 2 - len((trh.run_duration_max % 3600) % 60)) + cast((trh.run_duration_max % 3600) % 60 AS VARCHAR(2)) | |
,'Min_Duration (hh:mm:ss)' = cast(trh.run_duration_min / 3600 AS VARCHAR(10)) + ':' + replicate('0', 2 - len((trh.run_duration_min % 3600) / 60)) + cast((trh.run_duration_min % 3600) / 60 AS VARCHAR(2)) + ':' + replicate('0', 2 - len((trh.run_duration_min % 3600) % 60)) + cast((trh.run_duration_min % 3600) % 60 AS VARCHAR(2)) | |
,trh.fromRunDate AS 'From_Date' | |
,trh.Sampling | |
,sched.[Schedule_Description] | |
INTO #finalReport | |
FROM #temp_runhistory trh | |
LEFT JOIN #schedules AS sched ON trh.job_id = sched.job_id | |
option (recompile) | |
-- insert back into reporting server (if you have one ...) | |
--- Generate an AWESOME FINAL REPORT !! | |
SELECT getdate() AS [Data_Collection_Date] | |
,[Server_Name] | |
,[Job_ID] | |
,[Job_Name] | |
,[Last_Run_Date] | |
,[Last_RunStatus] | |
,[Last_RunDuration (hh:mm:ss)] | |
,[Avg_Duration (hh:mm:ss)] | |
,[Max_Duration (hh:mm:ss)] | |
,[Min_Duration (hh:mm:ss)] | |
,[From_Date] | |
,[Sampling] | |
-- we want to get all the schedule for a single JOB with ;** seperated | |
,STUFF(( | |
SELECT ';** ' + [Schedule_Description] | |
FROM #finalReport b | |
WHERE b.[Job_Name] = a.[Job_Name] | |
FOR XML PATH('') | |
), 1, 3, '') AS [Schedule_Description] | |
FROM #finalReport a | |
GROUP BY [Server_Name] | |
,[Job_ID] | |
,[Job_Name] | |
,[Last_Run_Date] | |
,[Last_RunStatus] | |
,[Last_RunDuration (hh:mm:ss)] | |
,[Avg_Duration (hh:mm:ss)] | |
,[Max_Duration (hh:mm:ss)] | |
,[Min_Duration (hh:mm:ss)] | |
,[From_Date] | |
,[Sampling] | |
ORDER BY [Last_RunDuration (hh:mm:ss)] DESC | |
option (recompile) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment