Въведение
Важно е администраторът на база данни да знае какви задачи и как са били изпълнени. За да опростите този процес, по-добре е да го автоматизирате, вместо да го извършвате ръчно.
В тази статия ще анализирам на конкретен пример как автоматично да събирам данни за изпълнени задачи на SQL Server Agent.
Решение
Алгоритъм:
- Създайте екземпляр, за да изберете задачи:
USE [DATABASE_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE view [srv].[vJobRunShortInfo] as SELECT sj.[job_id] as Job_GUID ,j.name as Job_Name ,case sj.[last_run_outcome] when 0 then 'Error' when 1 then 'Successful' when 3 then 'Canceled' else case when sj.[last_run_date] is not null and len(sj.[last_run_date])=8 then 'Inconsistent state' else NULL end end as LastFinishRunState ,sj.[last_run_outcome] as LastRunOutcome ,case when sj.[last_run_date] is not null and len(sj.[last_run_date])=8 then DATETIMEFROMPARTS( substring(cast(sj.[last_run_date] as nvarchar(255)),1,4), substring(cast(sj.[last_run_date] as nvarchar(255)),5,2), substring(cast(sj.[last_run_date] as nvarchar(255)),7,2), case when len(cast(sj.[last_run_time] as nvarchar(255)))>=5 then substring(cast(sj.[last_run_time] as nvarchar(255)),1,len(cast(sj.[last_run_time] as nvarchar(255)))-4) else 0 end, case when len(right(cast(sj.[last_run_time] as nvarchar(255)),4))>=4 then substring(right(cast(sj.[last_run_time] as nvarchar(255)),4),1,2) when len(right(cast(sj.[last_run_time] as nvarchar(255)),4))=3 then substring(right(cast(sj.[last_run_time] as nvarchar(255)),4),1,1) else 0 end, right(cast(sj.[last_run_duration] as nvarchar(255)),2), 0 ) else NULL end as LastDateTime ,case when len(cast(sj.[last_run_duration] as nvarchar(255)))>5 then substring(cast(sj.[last_run_duration] as nvarchar(255)),1,len(cast(sj.[last_run_duration] as nvarchar(255)))-4) when len(cast(sj.[last_run_duration] as nvarchar(255)))=5 then '0'+substring(cast(sj.[last_run_duration] as nvarchar(255)),1,len(cast(sj.[last_run_duration] as nvarchar(255)))-4) else '00' end +':' +case when len(cast(sj.[last_run_duration] as nvarchar(255)))>=4 then substring(right(cast(sj.[last_run_duration] as nvarchar(255)),4),1,2) when len(cast(sj.[last_run_duration] as nvarchar(255)))=3 then '0'+substring(right(cast(sj.[last_run_duration] as nvarchar(255)),4),1,1) else '00' end +':' +case when len(cast(sj.[last_run_duration] as nvarchar(255)))>=2 then substring(right(cast(sj.[last_run_duration] as nvarchar(255)),2),1,2) when len(cast(sj.[last_run_duration] as nvarchar(255)))=2 then '0'+substring(right(cast(sj.[last_run_duration] as nvarchar(255)),2),1,1) else '00' end as [LastRunDurationString] ,sj.last_run_duration as LastRunDurationInt ,sj.[last_outcome_message] as LastOutcomeMessage ,j.enabled as [Enabled] FROM [msdb].[dbo].[sysjobservers] as sj inner join msdb.dbo.sysjobs_view as j on j.job_id=sj.job_id; GO
За да направите това, използвайте екземплярите sysjobservers и sysjobs_view.
- Създайте таблица за съхраняване на избрани данни:
USE [DATABASE_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [srv].[ShortInfoRunJobs]( [Job_GUID] [uniqueidentifier] NOT NULL, [Job_Name] [nvarchar](255) NOT NULL, [LastFinishRunState] [nvarchar](255) NULL, [LastDateTime] [datetime] NOT NULL, [LastRunDurationString] [nvarchar](255) NULL, [LastRunDurationInt] [int] NULL, [LastOutcomeMessage] [nvarchar](255) NULL, [LastRunOutcome] [tinyint] NOT NULL, [Server] [nvarchar](255) NOT NULL, [InsertUTCDate] [datetime] NOT NULL, [ID] [int] IDENTITY(1,1) NOT NULL, CONSTRAINT [PK_ShortInfoRunJobs] PRIMARY KEY CLUSTERED ( [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] GO ALTER TABLE [srv].[ShortInfoRunJobs] ADD CONSTRAINT [DF_ShortInfoRunJobs_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate] GO
- Създайте задача в агента на SQL Server и получете информация за онези задачи, които са били изпълнявани дълго време (повече от 30 секунди) или не са били завършени. Трябва да съберете тази информация за последните два дни:
USE [DATABASE_NAME]; GO truncate table [srv].[ShortInfoRunJobs]; INSERT INTO [srv].[ShortInfoRunJobs] ([Job_GUID] ,[Job_Name] ,[LastFinishRunState] ,[LastDateTime] ,[LastRunDurationString] ,[LastRunDurationInt] ,[LastOutcomeMessage] ,[LastRunOutcome] ,[Server]) SELECT [Job_GUID] ,[Job_Name] ,[LastFinishRunState] ,[LastDateTime] ,[LastRunDurationString] ,[LastRunDurationInt] ,[LastOutcomeMessage] ,LastRunOutcome ,@@SERVERNAME FROM [srv].[vJobRunShortInfo] where [Enabled]=1 and ([LastRunOutcome]=0 or [LastRunDurationInt]>=30) and LastDateTime>=DateAdd(day,-2,getdate()); GO
Тук можете да зададете филтър за премахване на всички ненужни задачи. Например тези задачи, които се отнасят до репликация, тъй като отнема много повече време за завършване.
Генерирайте HTML-отчет, за да го изпратите на имейла на администраторите:
USE [DATABASE_NAME] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [srv].[GetHTMLTableShortInfoRunJobs] @body nvarchar(max) OUTPUT AS BEGIN /* generates an HTML-code for the tables of completed tasks */ SET NOCOUNT ON; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; declare @tbl table ( Job_GUID uniqueidentifier ,Job_Name nvarchar(255) ,LastFinishRunState nvarchar(255) ,LastDateTime datetime ,LastRunDurationString nvarchar(255) ,LastOutcomeMessage nvarchar(max) ,[Server] nvarchar(255) ,ID int identity(1,1) ); declare @Job_GUID uniqueidentifier ,@Job_Name nvarchar(255) ,@LastFinishRunState nvarchar(255) ,@LastDateTime datetime ,@LastRunDurationString nvarchar(255) ,@LastOutcomeMessage nvarchar(max) ,@Server nvarchar(255) ,@ID int; insert into @tbl( Job_GUID ,Job_Name ,LastFinishRunState ,LastDateTime ,LastRunDurationString ,LastOutcomeMessage ,[Server] ) select Job_GUID ,Job_Name ,LastFinishRunState ,LastDateTime ,LastRunDurationString ,LastOutcomeMessage ,[Server] from srv.ShortInfoRunJobs --order by LastRunDurationInt desc; if(exists(select top(1) 1 from @tbl)) begin set @body='When analyzing these tasks execution, I have found out the tasks that either have failed with an error, or, it has taken more than 30 seconds for their execution :<br><br>'+'<TABLE BORDER=5>'; set @[email protected]+'<TR>'; set @[email protected]+'<TD>'; set @[email protected]+'№ p/p'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'GUID'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'TASK'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'STATUS'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'DATE AND TIME'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'DURATION'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'MESSAGE'; set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+'SERVER'; set @[email protected]+'</TD>'; set @[email protected]+'</TR>'; while((select top 1 1 from @tbl)>0) begin set @[email protected]+'<TR>'; select top 1 @ID = [ID] ,@Job_GUID = Job_GUID ,@Job_Name = Job_Name ,@LastFinishRunState = LastFinishRunState ,@LastDateTime = LastDateTime ,@LastRunDurationString = LastRunDurationString ,@LastOutcomeMessage = LastOutcomeMessage ,@Server = [Server] from @tbl order by LastRunDurationInt desc; set @[email protected]+'<TD>'; set @[email protected]+cast(@ID as nvarchar(max)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+cast(@Job_GUID as nvarchar(255)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@Job_Name,''); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@LastFinishRunState,''); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+rep.GetDateFormat(@LastDateTime, default)+' '+rep.GetTimeFormat(@LastDateTime, default);--cast(@InsertDate as nvarchar(max)); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@LastRunDurationString,''); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@LastOutcomeMessage, ''); set @[email protected]+'</TD>'; set @[email protected]+'<TD>'; set @[email protected]+coalesce(@Server, ''); set @[email protected]+'</TD>'; delete from @tbl where [email protected]; set @[email protected]+'</TR>'; end set @[email protected]+'</TABLE>'; end else begin set @body='The tasks, that have failed with an error or that have been executed for more than 30 seconds, have not been found'; end set @[email protected]+'<br><br>For the detailed information, please refer to the table DATABASE_NAME.srv.ShortInfoRunJobs'; END GO
Тази съхранена процедура генерира HTML-отчет за изпълнени задачи, които са били изпълнени за 30 секунди или които не са били завършени.
Резултат
В тази статия проучих на конкретен пример изпълнението на ежедневно автоматично събиране на данни за изпълнени задачи в SQL Server Agent. Тази информация помага да се определят задачи, които са били изпълнявани от дълго време или завършени с грешка. Това позволява на администратора да предприеме мерки за избягване на подобни грешки в бъдеще. Например, възможно е задачата да се изпълнява по-бързо или да зададете максимално време за посочената задача.
Това решение също помага за наблюдение на проблеми, свързани с архивирането. Все пак ще го обсъдим по-късно, тъй като не е достатъчно да уведомявате за важни задачи веднъж на ден. Необходимо е да изпращате имейли за тях незабавно и редовно, докато грешката бъде отстранена.
Ако трябва да изберете данни от няколко сървъра, тогава е възможно да комбинирате резултатите и да ги изпратите `чрез един имейл.
Препратки:
» sysjobs
» sysjobservers
Допълнително четене:
Автоматично събиране на данни за промени в схемата на базата данни в MS SQL Server
Автоматично събиране на данни:файлове с бази данни и логически устройства в MS SQL Server
Конфигуриране на известия за поща в базата данни в MS SQL Server