/*============================================================================= Création d'un package pour la gestion du monitoring des status des Jobs SQL -> Création des tables -> Création des FK et index -> Deploy de la SP d'initialisation du status des jobs : mon.Initialize_Jobs_Status -> Deploy de la SP de mise à jour du status des jobs : mon.Maj_Jobs_Status -> Deploy de la fonction du calcul de prochain schedule : GetNextScheduleForJob Création : 13.07.2023 / FLA Modifications : 17.08.2023 / FLA : Changement de methode d'update, changement du calcul du prochain schedule, add SP to check status 21.09.2023 / FLA : Improve performance for function GetNextScheduleForJob and for SP Maj_Jobs_Status =============================================================================*/ /***********************************************************************/ /* STRUCTURE */ /* */ /***********************************************************************/ USE HCITools; IF NOT EXISTS ( SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[Job_monitoring_config]') AND type IN ( N'U' )) BEGIN CREATE TABLE [mon].[Job_monitoring_config]( [Job_monitoring_config_ID] [INT] IDENTITY(1,1) NOT NULL, [JMC_name] [VARCHAR](255) NOT NULL, [JMC_need_history] [BIT] NOT NULL, [JMC_caller] [VARCHAR](255) NULL, [JMC_ignore] [BIT] NOT NULL, CONSTRAINT [PK_Job_monitoring_config] PRIMARY KEY CLUSTERED ( [Job_monitoring_config_ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] END IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[DF_JMC_need_history]') AND type = 'D') BEGIN ALTER TABLE [mon].[Job_monitoring_config] ADD CONSTRAINT DF_JMC_need_history DEFAULT (1) FOR [JMC_need_history] END GO IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[DF_JMC_ignore]') AND type = 'D') BEGIN ALTER TABLE [mon].[Job_monitoring_config] ADD CONSTRAINT DF_JMC_ignore DEFAULT (0) FOR [JMC_ignore] END GO IF NOT EXISTS (SELECT * FROM ::fn_listextendedproperty ('MS_Description', 'Schema', 'mon', 'Table', 'Job_monitoring_config', 'Column', 'JMC_need_history')) BEGIN EXEC sp_addextendedproperty 'MS_Description', '0: same row updated in table Job_monitoring_history, 1:one row for each execution of job', 'Schema', [mon], 'table', [Job_monitoring_config], 'column', [JMC_need_history] END GO IF NOT EXISTS (SELECT * FROM ::fn_listextendedproperty ('MS_Description', 'Schema', 'mon', 'Table', 'Job_monitoring_config', 'Column', 'JMC_caller')) BEGIN EXEC sp_addextendedproperty 'MS_Description', 'Name of job If job called by another one', 'Schema', [mon], 'table', [Job_monitoring_config], 'column', [JMC_caller] END GO IF NOT EXISTS (SELECT * FROM ::fn_listextendedproperty ('MS_Description', 'Schema', 'mon', 'Table', 'Job_monitoring_config', 'Column', 'JMC_ignore')) BEGIN EXEC sp_addextendedproperty 'MS_Description', '0: To monitor, 1:To don''t monitor', 'Schema', [mon], 'table', [Job_monitoring_config], 'column', [JMC_ignore] END GO IF NOT EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = N'Job_monitoring_config') BEGIN ALTER TABLE [mon].[Job_monitoring_config] ADD CONSTRAINT [PK_Job_monitoring_config] PRIMARY KEY CLUSTERED ([Job_monitoring_config_ID] ASC); END; IF NOT EXISTS ( SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'[mon].[Job_monitoring_config]') AND name = N'NCIX_Job_monitoring_config_COL_JMC_name') BEGIN CREATE NONCLUSTERED INDEX [NCIX_Job_monitoring_config_COL_JMC_name] ON [mon].[Job_monitoring_config] ([JMC_name] ASC) INCLUDE (JMC_need_history, JMC_caller, JMC_ignore); END; IF NOT EXISTS ( SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[Job_monitoring_history]') AND type IN ( N'U' )) BEGIN CREATE TABLE [mon].[Job_monitoring_history]( [Job_monitoring_history_ID] [INT] IDENTITY(1,1) NOT NULL, [JMH_job_monitoring_config_ID] [INT] NOT NULL, [JMH_next_schedule] [DATETIME] NULL, [JMH_monitoring_status] [TINYINT] NOT NULL, [JMH_job_status] [TINYINT] NULL, [JMH_error_message] [NVARCHAR](4000) NULL, [JMH_update_status] [DATETIME] NOT NULL, [JMH_monitoring_date] [DATETIME] NULL, CONSTRAINT [PK_Job_monitoring_history] PRIMARY KEY CLUSTERED ( [Job_monitoring_history_ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] END IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[DF_JMH_monitoring_status]') AND type = 'D') BEGIN ALTER TABLE [mon].[Job_monitoring_history] ADD CONSTRAINT DF_JMH_monitoring_status DEFAULT ((0)) FOR [JMH_monitoring_status] END GO IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[DF_JMH_update_status]') AND type = 'D') BEGIN ALTER TABLE [mon].[Job_monitoring_history] ADD CONSTRAINT DF_JMH_update_status DEFAULT (GETDATE()) FOR [JMH_update_status] END GO IF NOT EXISTS (SELECT * FROM ::fn_listextendedproperty ('MS_Description', 'Schema', 'mon', 'Table', 'Job_monitoring_history', 'Column', 'JMH_monitoring_status')) BEGIN EXEC sp_addextendedproperty 'MS_Description', '0: No error, 1:Error not checked, 2:Error checked', 'Schema', [mon], 'table', [Job_monitoring_history], 'column', [JMH_monitoring_status] END GO IF NOT EXISTS (SELECT * FROM ::fn_listextendedproperty ('MS_Description', 'Schema', 'mon', 'Table', 'Job_monitoring_history', 'Column', 'JMH_job_status')) BEGIN EXEC sp_addextendedproperty 'MS_Description', '0 = Failed 1 = Succeeded 2 = No execution', 'Schema', [mon], 'table', [Job_monitoring_history], 'column', [JMH_job_status] END GO IF NOT EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = N'Job_monitoring_history') BEGIN ALTER TABLE [mon].[Job_monitoring_history] ADD CONSTRAINT [PK_Job_monitoring_history] PRIMARY KEY CLUSTERED ([Job_monitoring_history_ID] ASC); END; IF NOT EXISTS ( SELECT 1 FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'mon.FK_Job_monitoring_config_COL_Job_monitoring_config_ID') AND parent_object_id = OBJECT_ID(N'[mon].[Job_monitoring_history]')) BEGIN ALTER TABLE [mon].[Job_monitoring_history] ADD CONSTRAINT [FK_Job_monitoring_config_COL_Job_monitoring_config_ID] FOREIGN KEY ([JMH_Job_monitoring_config_ID]) REFERENCES [mon].[Job_monitoring_config] ([Job_monitoring_config_ID]) ON DELETE NO ACTION ON UPDATE NO ACTION; END; GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[Initialize_Jobs_Status]') AND type IN (N'P', N'PC')) DROP PROCEDURE [mon].[Initialize_Jobs_Status] GO CREATE PROCEDURE [mon].[Initialize_Jobs_Status] @in_debug TINYINT = 0, @in_JobName NVARCHAR(1000) = NULL AS /*============================================================================= Explication du traitement realise par la SP ------------------------------------------- Cette SP sert a initialiser le status des jobs Contexte d'utilisation ---------------------- Appelée à la main en passant le nom du job à initialiser si besoin sinon all jobs Parametres ---------- @in_debug : non utilisé @in_JobName : nom du job (si null All jobs) Creation : 13.07.23 / FLA Modifications : =============================================================================*/ SET NOCOUNT ON; /*------------------- Declaration des variables --------------------*/ DECLARE @JobName VARCHAR(255), @LastStatus TINYINT, @LastMessage NVARCHAR(4000), @NextRun DATETIME, @ID INT; /*------------ Affectation des parametres aux variables ------------*/ /*-------------------------- Traitement ---------------------------*/ BEGIN TRY DECLARE c_Jobs CURSOR FAST_FORWARD READ_ONLY FOR SELECT SJ.name, SJH.LastStatus, SJH.Lastmessage, SJA.NextRun FROM msdb.dbo.sysjobs SJ OUTER APPLY ( SELECT TOP 1 NextRun = JA.next_scheduled_run_date FROM msdb.dbo.sysjobactivity JA WHERE JA.job_id = SJ.job_id ORDER BY session_id DESC) AS SJA OUTER APPLY ( SELECT TOP 1 LastStatus = MIN(run_status), Lastmessage = CASE WHEN JH.sql_severity > 0 THEN message ELSE NULL END FROM msdb.dbo.sysjobhistory JH WHERE JH.job_id = SJ.job_id AND JH.run_date = ( SELECT MAX(T.run_date) FROM msdb.dbo.sysjobhistory T WHERE T.job_id = JH.job_id) AND JH.run_time >= ( SELECT MAX(T.run_time) FROM msdb.dbo.sysjobhistory T WHERE T.job_id = JH.job_id AND JH.run_date = T.run_date AND T.step_id = 0) GROUP BY CASE WHEN JH.sql_severity > 0 THEN message ELSE NULL END ORDER BY MIN(run_status)) AS SJH WHERE SJ.enabled = 1 AND (( SJ.name LIKE 'DR[0-9]%' OR SJ.name LIKE 'D[0-9]%' OR SJ.name LIKE 'M[0-9]%' OR SJ.name LIKE 'W[0-9]%' OR SJ.name LIKE '[_]%') AND @in_JobName IS NULL) AND SJ.name NOT LIKE '%Log reader%' OR (SJ.name = @in_JobName AND @in_JobName IS NOT NULL) ORDER BY SJ.name; OPEN c_Jobs; FETCH NEXT FROM c_Jobs INTO @JobName, @LastStatus, @LastMessage, @NextRun; WHILE @@FETCH_STATUS = 0 BEGIN IF NOT EXISTS ( SELECT 1 FROM mon.Job_monitoring_config WHERE JMC_name = @JobName) BEGIN INSERT INTO mon.Job_monitoring_config (JMC_name, JMC_need_history) VALUES (@JobName, CASE WHEN @JobName LIKE '[_][0-9]%' THEN 0 WHEN @JobName LIKE 'DR[0-9]%' THEN 0 ELSE 1 END); SELECT @ID = SCOPE_IDENTITY(); INSERT INTO mon.Job_monitoring_history (JMH_job_monitoring_config_ID, JMH_next_schedule, JMH_monitoring_status, JMH_job_status, JMH_error_message) VALUES (@ID, @NextRun, CASE WHEN ISNULL(@LastStatus,1) = 1 THEN 0 ELSE 1 END, @LastStatus, @LastMessage); END; FETCH NEXT FROM c_Jobs INTO @JobName, @LastStatus, @LastMessage, @NextRun; UPDATE JMC SET JMC.JMC_Caller = SJ.name FROM msdb.dbo.sysjobs SJ INNER JOIN msdb.dbo.sysjobsteps SJS ON SJ.job_id = SJS.job_id INNER JOIN mon.Job_monitoring_config JMC ON JMC.JMC_name = REPLACE(REPLACE(SUBSTRING(SJS.step_name, 11, 80), '[', ''), ']', '') WHERE SJS.command LIKE '%aps_Launch_Job_SQL_Agent%' OR SJS.command LIKE '%aps_CheckProd_Job%'; END; CLOSE c_Jobs; DEALLOCATE c_Jobs; END TRY BEGIN CATCH /* Traitement des erreurs (sans RaiseError) */ EXEC dbo.get_Error_Info @in_RaiseError = 0; END CATCH; /*------------------ Retour au programme appelant -----------------*/ RETURN (@@error); GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GetNextScheduleForJob]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION [dbo].[GetNextScheduleForJob] GO CREATE FUNCTION [dbo].[GetNextScheduleForJob] ( @JobName SYSNAME, @startDate DATETIME, @endDate DATETIME ) RETURNS @t TABLE ( scheduleID INT NOT NULL, jobName SYSNAME NOT NULL, jobDescription NVARCHAR(512) NOT NULL, scheduleName SYSNAME NOT NULL, categoryName SYSNAME NOT NULL, startDate DATETIME NOT NULL, jobEnabled INT NOT NULL, scheduleEnabled INT NOT NULL) AS BEGIN -- Create a tally table. If you already have one of your own please use that instead. DECLARE @tallyNumbers TABLE ( num SMALLINT PRIMARY KEY CLUSTERED ) DECLARE @index SMALLINT SET @index = 1 WHILE @index <= 8640 BEGIN INSERT @tallyNumbers ( num ) VALUES ( @index ) SET @index = @index + 1 END -- Create a staging table for jobschedules DECLARE @jobSchedules TABLE ( rowID INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED, jobName SYSNAME NOT NULL, jobDescription NVARCHAR(512) NOT NULL, scheduleName SYSNAME NOT NULL, scheduleID INT NOT NULL, categoryName SYSNAME NOT NULL, freq_type INT NOT NULL, freq_interval INT NOT NULL, freq_subday_type INT NOT NULL, freq_subday_interval INT NOT NULL, freq_relative_interval INT NOT NULL, freq_recurrence_factor INT NOT NULL, startDate DATETIME NOT NULL, startTime DATETIME NOT NULL, endDate DATETIME NOT NULL, endTime DATETIME NOT NULL, jobEnabled INT NOT NULL, scheduleEnabled INT NOT NULL ) -- Populate the staging table for JobSchedules with SQL Server 2005 and SQL Server 2008 INSERT @JobSchedules ( jobName, jobDescription, scheduleName, scheduleID, categoryName, freq_type, freq_interval, freq_subday_type, freq_subday_interval, freq_relative_interval, freq_recurrence_factor, startDate, startTime, endDate, endTime, jobEnabled, scheduleEnabled ) SELECT sj.name, COALESCE(sj.description, ''), ss.name, ss.schedule_id, sc.name, ss.freq_type, ss.freq_interval, ss.freq_subday_type, ss.freq_subday_interval, ss.freq_relative_interval, ss.freq_recurrence_factor, COALESCE(STR(ss.active_start_date, 8), CONVERT(CHAR(8), GETDATE(), 112)) startDate, STUFF(STUFF(REPLACE(STR(ss.active_start_time, 6), ' ', '0'), 3, 0, ':'), 6, 0, ':') startTime, STR(ss.active_end_date, 8) endDate, STUFF(STUFF(REPLACE(STR(ss.active_end_time, 6), ' ', '0'), 3, 0, ':'), 6, 0, ':') endTime, sj.enabled, ss.enabled FROM msdb..sysschedules AS ss WITH (NOLOCK) INNER JOIN msdb..sysjobschedules AS sjs WITH (NOLOCK) ON sjs.schedule_id = ss.schedule_id INNER JOIN msdb..sysjobs AS sj WITH (NOLOCK) ON sj.job_id = sjs.job_id INNER JOIN msdb..syscategories AS sc WITH (NOLOCK) ON sc.category_id = sj.category_id WHERE ss.freq_type IN (1, 4, 8, 16, 32) AND sj.name = @JobName -- Deal with first, second, third, fourth and last occurence DECLARE @tempStart DATETIME, @tempEnd DATETIME SELECT @tempStart = DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @startDate), '19000101'), @TempEnd = DATEADD(MONTH, DATEDIFF(MONTH, '18991231', @endDate), '18991231') DECLARE @dayInformation TABLE ( infoDate DATETIME PRIMARY KEY CLUSTERED, weekdayName VARCHAR(9) NOT NULL, statusCode INT NOT NULL, lastDay TINYINT DEFAULT 0 ) WHILE @tempStart <= @tempEnd BEGIN INSERT @dayInformation ( infoDate, weekdayName, statusCode ) SELECT @tempStart, DATENAME(WEEKDAY, @tempStart), CASE WHEN DATEPART(DAY, @tempStart) BETWEEN 1 AND 7 THEN 1 WHEN DATEPART(DAY, @tempStart) BETWEEN 8 AND 14 THEN 2 WHEN DATEPART(DAY, @tempStart) BETWEEN 15 AND 21 THEN 4 WHEN DATEPART(DAY, @tempStart) BETWEEN 22 AND 28 THEN 8 ELSE 0 END SET @tempStart = DATEADD(DAY, 1, @tempStart) END UPDATE di SET di.statusCode = di.statusCode + 16 FROM @dayInformation AS di INNER JOIN ( SELECT DATEDIFF(MONTH, '19000101', infoDate) AS theMonth, DATEPART(DAY, MAX(infoDate)) - 6 AS theDay FROM @dayInformation GROUP BY DATEDIFF(MONTH, '19000101', infoDate) ) AS x ON x.theMonth = DATEDIFF(MONTH, '19000101', di.infoDate) WHERE DATEPART(DAY, di.infoDate) >= x.theDay UPDATE di SET di.lastDay = 16 FROM @dayInformation AS di INNER JOIN ( SELECT DATEDIFF(MONTH, '19000101', infoDate) AS theMonth, MAX(infoDate) AS theDay FROM @dayInformation GROUP BY DATEDIFF(MONTH, '19000101', infoDate) ) AS x ON x.theMonth = DATEDIFF(MONTH, '19000101', di.infoDate) WHERE di.infoDate = x.theDay UPDATE @dayInformation SET lastDay = DATEPART(DAY, infoDate) WHERE DATEPART(DAY, infoDate) BETWEEN 1 AND 4 -- Stage all individual schedule times DECLARE @scheduleTimes TABLE ( rowID INT NOT NULL, startDate DATETIME NOT NULL, endDate DATETIME NOT NULL, waitSeconds INT DEFAULT 0 ) -- Insert one time only schedules INSERT @scheduleTimes ( rowID, startDate, endDate ) SELECT rowID, startDate+startTime, startDate+startTime FROM @jobSchedules WHERE freq_type = 1 AND startDate+startTime BETWEEN @StartDate AND @EndDate -- Insert daily schedules INSERT @scheduleTimes ( rowID, startDate, endDate, waitSeconds ) SELECT js.rowID, di.infoDate+js.startTime, di.infoDate+js.endTime, CASE js.freq_subday_type WHEN 1 THEN 0 WHEN 2 THEN js.freq_subday_interval WHEN 4 THEN 60 * js.freq_subday_interval WHEN 8 THEN 3600 * js.freq_subday_interval END FROM @jobSchedules AS js INNER JOIN @dayInformation AS di ON di.infoDate >= js.startDate AND di.infoDate <= js.endDate WHERE js.freq_type = 4 AND DATEDIFF(DAY, js.startDate, di.infoDate) % js.freq_interval = 0 -- Insert weekly schedules INSERT @scheduleTimes ( rowID, startDate, endDate, waitSeconds ) SELECT js.rowID, di.infoDate+js.startTime, di.infoDate+js.endTime, CASE js.freq_subday_type WHEN 1 THEN 0 WHEN 2 THEN js.freq_subday_interval WHEN 4 THEN 60 * js.freq_subday_interval WHEN 8 THEN 3600 * js.freq_subday_interval END FROM @jobSchedules AS js INNER JOIN @dayInformation AS di ON di.infoDate >= js.startDate AND di.infoDate <= js.endDate WHERE js.freq_type = 8 AND 1 = CASE WHEN js.freq_interval & 1 = 1 AND di.weekdayName = 'Sunday' THEN 1 WHEN js.freq_interval & 2 = 2 AND di.weekdayName = 'Monday' THEN 1 WHEN js.freq_interval & 4 = 4 AND di.weekdayName = 'Tuesday' THEN 1 WHEN js.freq_interval & 8 = 8 AND di.weekdayName = 'Wednesday' THEN 1 WHEN js.freq_interval & 16 = 16 AND di.weekdayName = 'Thursday' THEN 1 WHEN js.freq_interval & 32 = 32 AND di.weekdayName = 'Friday' THEN 1 WHEN js.freq_interval & 64 = 64 AND di.weekdayName = 'Saturday' THEN 1 ELSE 0 END AND (DATEDIFF(DAY, js.startDate, di.infoDate) / 7) % js.freq_recurrence_factor = 0 -- Insert monthly schedules INSERT @scheduleTimes ( rowID, startDate, endDate, waitSeconds ) SELECT js.rowID, di.infoDate+js.startTime, di.infoDate+js.endTime, CASE js.freq_subday_type WHEN 1 THEN 0 WHEN 2 THEN js.freq_subday_interval WHEN 4 THEN 60 * js.freq_subday_interval WHEN 8 THEN 3600 * js.freq_subday_interval END FROM @jobSchedules AS js INNER JOIN @dayInformation AS di ON di.infoDate >= js.startDate AND di.infoDate <= js.endDate WHERE js.freq_type = 16 AND DATEPART(DAY, di.infoDate) = js.freq_interval AND DATEDIFF(MONTH, js.startDate, di.infoDate) % js.freq_recurrence_factor = 0 -- Insert monthly relative schedules INSERT @scheduleTimes ( rowID, startDate, endDate, waitSeconds ) SELECT js.rowID, di.infoDate+js.startTime, di.infoDate+js.endTime, CASE js.freq_subday_type WHEN 1 THEN 0 WHEN 2 THEN js.freq_subday_interval WHEN 4 THEN 60 * js.freq_subday_interval WHEN 8 THEN 3600 * js.freq_subday_interval END FROM @jobSchedules AS js INNER JOIN @dayInformation AS di ON di.infoDate >= js.startDate AND di.infoDate <= js.endDate WHERE js.freq_type = 32 AND 1 = CASE WHEN js.freq_interval = 1 AND di.weekdayName = 'Sunday' THEN 1 WHEN js.freq_interval = 2 AND di.weekdayName = 'Monday' THEN 1 WHEN js.freq_interval = 3 AND di.weekdayName = 'Tuesday' THEN 1 WHEN js.freq_interval = 4 AND di.weekdayName = 'Wednesday' THEN 1 WHEN js.freq_interval = 5 AND di.weekdayName = 'Thursday' THEN 1 WHEN js.freq_interval = 6 AND di.weekdayName = 'Friday' THEN 1 WHEN js.freq_interval = 7 AND di.weekdayName = 'Saturday' THEN 1 WHEN js.freq_interval = 8 AND js.freq_relative_interval = di.lastDay THEN 1 WHEN js.freq_interval = 9 AND di.weekdayName NOT IN ('Sunday', 'Saturday') THEN 1 WHEN js.freq_interval = 10 AND di.weekdayName IN ('Sunday', 'Saturday') THEN 1 ELSE 0 END AND di.statusCode & js.freq_relative_interval = js.freq_relative_interval AND DATEDIFF(MONTH, js.startDate, di.infoDate) % js.freq_recurrence_factor = 0 -- Get the daily recurring schedule times INSERT @scheduleTimes ( rowID, startDate, endDate, waitSeconds ) SELECT st.rowID, DATEADD(SECOND, tn.num * st.waitSeconds, st.startDate), st.endDate, st.waitSeconds FROM @scheduleTimes AS st CROSS JOIN @tallyNumbers AS tn WHERE tn.num * st.waitSeconds <= DATEDIFF(SECOND, st.startDate, st.endDate) AND st.waitSeconds > 0 -- Present the result INSERT @t (scheduleID, jobName, jobDescription, scheduleName, categoryName, startDate, jobEnabled, scheduleEnabled) SELECT js.scheduleID, js.jobName, js.jobDescription, js.scheduleName, js.categoryName, st.startDate, js.jobEnabled, js.scheduleEnabled FROM @scheduleTimes AS st INNER JOIN @jobSchedules AS js ON js.rowID = st.rowID WHERE js.jobEnabled = 1 AND js.scheduleEnabled = 1 AND st.startDate >= @startDate AND st.startDate <= @endDate RETURN END GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[Maj_Jobs_Status]') AND type IN (N'P', N'PC')) DROP PROCEDURE [mon].[Maj_Jobs_Status] GO CREATE PROCEDURE [mon].[Maj_Jobs_Status] @in_debug TINYINT = 0, @in_JobName NVARCHAR(1000) = NULL, @in_JobID UNIQUEIDENTIFIER = NULL AS /*============================================================================= Explication du traitement realise par la SP ------------------------------------------- Cette SP sert a mettre à jour le status des jobs Contexte d'utilisation ---------------------- Appelée depuis le step Empty Step et Send mail KO des jobs Parametres ---------- @in_debug : non utilisé @in_JobName : nom du job (si null All jobs) Creation : 03.08.23 / FLA Modifications : 22.08.23 : Get directly the job_id in job instead of put job name in parameter 21.09.23 : Call the new version of function [GetNextScheduleForJob] =============================================================================*/ SET NOCOUNT ON; /*------------------- Declaration des variables --------------------*/ DECLARE @LastMessage NVARCHAR(4000) = NULL, @JMC_ID INT = 0, @JMH_ID INT = NULL, @NeedHistory BIT = 1, @NextRun DATETIME /*------------ Affectation des parametres aux variables ------------*/ /*-------------------------- Traitement ---------------------------*/ BEGIN TRY IF @in_JobID IS NOT NULL SELECT @in_JobName = name FROM msdb.dbo.sysjobs WHERE job_id = @in_JobID SELECT @LastMessage = jh.message FROM msdb.dbo.sysjobhistory jh INNER JOIN msdb.dbo.sysjobactivity ja ON ja.job_id = jh.job_id INNER JOIN msdb.dbo.sysjobs j ON j.job_id = ja.job_id WHERE j.name = @in_JobName AND run_status = 0 --and ja.stop_execution_date is null-- AND ja.session_id = (SELECT TOP 1 session_id FROM msdb.dbo.syssessions ORDER BY agent_start_date DESC) AND start_execution_date IS NOT NULL --AND stop_execution_date is null AND msdb.dbo.agent_datetime(run_date, run_time) >= start_execution_date; /* Gestion des Query Timeout Error */ IF EXISTS (SELECT 1 FROM msdb..sysjobhistory jh INNER JOIN msdb..sysjobactivity ja ON ja.job_id = jh.job_id INNER JOIN msdb..sysjobs j ON j.job_id = ja.job_id WHERE j.name = @in_JobName AND run_status = 1 --and ja.stop_execution_date is null-- AND ja.session_id = (SELECT TOP 1 session_id FROM msdb.dbo.syssessions ORDER BY agent_start_date DESC) AND start_execution_date IS NOT NULL AND jh.sql_message_id = 7412 AND msdb.dbo.agent_datetime(run_date, run_time) >= start_execution_date) BEGIN SELECT @LastMessage = jh.message FROM msdb..sysjobhistory jh INNER JOIN msdb..sysjobactivity ja ON ja.job_id = jh.job_id INNER JOIN msdb..sysjobs j ON j.job_id = ja.job_id WHERE j.name = @in_JobName AND run_status = 1 --and ja.stop_execution_date is null-- AND ja.session_id = (SELECT TOP 1 session_id FROM msdb.dbo.syssessions ORDER BY agent_start_date DESC) AND start_execution_date IS NOT NULL AND jh.sql_message_id = 7412 AND msdb.dbo.agent_datetime(run_date, run_time) >= start_execution_date; END; SELECT @JMC_ID = JMC.Job_monitoring_config_ID, @NeedHistory = JMC.JMC_need_history, @JMH_ID = JMH.Job_monitoring_history_ID FROM [mon].[Job_monitoring_config] JMC LEFT OUTER JOIN [mon].[Job_monitoring_history] JMH ON JMC.Job_monitoring_config_ID = JMH.JMH_job_monitoring_config_ID WHERE JMC_name = @in_JobName AND JMC_ignore = 0 IF @JMC_ID <> 0 BEGIN SELECT @NextRun = Job.startDate FROM (SELECT TOP 1 startDate FROM [dbo].[GetNextScheduleForJob](@in_JobName,GETDATE(), GETDATE() + 1) ORDER BY startDate) AS Job IF @NeedHistory = 0 BEGIN UPDATE [mon].[Job_monitoring_history] SET [JMH_next_schedule] = @NextRun, [JMH_monitoring_status] = CASE WHEN @LastMessage IS NOT NULL THEN 1 ELSE 0 END, [JMH_job_status] = CASE WHEN @LastMessage IS NOT NULL THEN 0 ELSE 1 END, [JMH_error_message] = @LastMessage, [JMH_update_status] = GETDATE() WHERE JMH_job_monitoring_config_ID = @JMH_ID END ELSE BEGIN INSERT INTO [mon].[Job_monitoring_history] ([JMH_job_monitoring_config_ID], [JMH_next_schedule], [JMH_monitoring_status], [JMH_job_status], [JMH_error_message], [JMH_update_status]) VALUES (@JMC_ID, @NextRun, CASE WHEN @LastMessage IS NOT NULL THEN 1 ELSE 0 END, CASE WHEN @LastMessage IS NOT NULL THEN 0 ELSE 1 END, @LastMessage, DEFAULT ) END END END TRY BEGIN CATCH /* Traitement des erreurs (sans RaiseError) */ EXEC dbo.get_Error_Info @in_RaiseError = 0; END CATCH; /*------------------ Retour au programme appelant -----------------*/ RETURN (@@error); GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[mon].[Get_Jobs_Status]') AND type in (N'P', N'PC')) DROP PROCEDURE [mon].[Get_Jobs_Status] GO CREATE PROCEDURE [mon].[Get_Jobs_Status] @in_debug TINYINT = 0, @in_JobName NVARCHAR(1000) = NULL AS /*============================================================================= Explication du traitement realise par la SP ------------------------------------------- Cette SP sert à retourner le status des jobs Contexte d'utilisation ---------------------- Appelée depuis le MDV ou à la main Parametres ---------- @in_debug : non utilisé @in_JobName : nom du job (si null All jobs) Creation : 17.08.23 / FLA Modifications : =============================================================================*/ SET NOCOUNT ON; /*------------------- Declaration des variables --------------------*/ /*------------ Affectation des parametres aux variables ------------*/ /*-------------------------- Traitement ---------------------------*/ BEGIN TRY SELECT JMC.JMC_name AS JobName, JMH.JMH_error_message AS Error, CASE WHEN ( JMH.JMH_job_status = 0 AND JMH_monitoring_status = 1) THEN 'ERROR' WHEN (CASE WHEN ISNULL(JMH.JMH_next_schedule,T1.JMH_next_schedule) < ISNULL(T1.JMH_next_schedule,JMH.JMH_next_schedule) THEN ISNULL(JMH.JMH_next_schedule,T1.JMH_next_schedule) ELSE ISNULL(T1.JMH_next_schedule,JMH.JMH_next_schedule) END < GETDATE()) THEN 'NOT EXECUTED' ELSE '' END AS Status FROM mon.Job_monitoring_config JMC INNER JOIN mon.Job_monitoring_history JMH ON JMC.Job_monitoring_config_ID = JMH.JMH_job_monitoring_config_ID AND JMC.JMC_ignore = 0 AND JMH.JMH_update_status = (SELECT MAX(JMH2.JMH_update_status) FROM mon.Job_monitoring_history JMH2 WHERE JMH2.JMH_job_monitoring_config_ID = JMC.Job_monitoring_config_ID) OUTER APPLY (SELECT JMH3.JMH_next_schedule FROM mon.Job_monitoring_config JMC3 INNER JOIN mon.Job_monitoring_history JMH3 ON JMC3.Job_monitoring_config_ID = JMH3.JMH_job_monitoring_config_ID AND JMH3.JMH_update_status = (SELECT MAX(JMH4.JMH_update_status) FROM mon.Job_monitoring_history JMH4 WHERE JMH4.JMH_job_monitoring_config_ID = JMC3.Job_monitoring_config_ID) WHERE JMC3.JMC_name = JMC.JMC_caller) AS T1 WHERE JMC.JMC_name = COALESCE(@in_JobName, JMC.JMC_name) ORDER BY JMC.JMC_name END TRY BEGIN CATCH /* Traitement des erreurs (sans RaiseError) */ EXEC dbo.get_Error_Info @in_RaiseError = 0; END CATCH; /*------------------ Retour au programme appelant -----------------*/ RETURN (@@error); GO /***********************************************************************/ /* INITIALISATION */ /* */ /***********************************************************************/ EXEC [mon].[Initialize_Jobs_Status] SELECT * FROM mon.Job_monitoring_config JMC INNER JOIN mon.Job_monitoring_history JMH ON JMC.Job_monitoring_config_ID = JMH.JMH_Job_monitoring_config_ID ORDER BY JMC.JMC_name EXEC [mon].[Get_Jobs_Status]