Introdução
É importante para um administrador de banco de dados saber quais tarefas e como elas foram concluídas. Para simplificar esse processo, é melhor automatizá-lo, em vez de executá-lo manualmente.
Neste artigo, analisarei em um exemplo específico como coletar dados automaticamente sobre tarefas concluídas do SQL Server Agent.
Solução
Algoritmo:
- Crie uma instância para selecionar tarefas:
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
Para fazer isso, use as instâncias sysjobservers e sysjobs_view. - Crie uma tabela para armazenar os dados selecionados:
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
- Crie uma tarefa no SQL Server Agent e obtenha informações sobre as tarefas que foram executadas por um longo tempo (mais de 30 segundos) ou não foram concluídas. Você precisa coletar essas informações nos últimos dois dias:
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
Aqui você pode definir um filtro para remover todas as tarefas desnecessárias. Por exemplo, as tarefas que se referem à replicação, pois levam muito mais tempo para serem concluídas.
Gere um relatório HTML para enviá-lo ao e-mail dos administradores:
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
Esse procedimento armazenado gera um relatório HTML sobre tarefas concluídas que foram executadas por 30 segundos ou que não foram concluídas.
Resultado
Neste artigo, explorei em um exemplo específico a implementação de uma coleta automática diária de dados sobre tarefas concluídas no SQL Server Agent. Essas informações ajudam a determinar tarefas que foram executadas por um longo tempo ou concluídas com um erro. Ele permite que um administrador tome medidas para evitar tais erros no futuro. Por exemplo, é possível tornar a tarefa mais rápida ou definir o tempo máximo para a tarefa especificada.
Essa solução também ajuda a monitorar problemas relacionados a backups. Ainda assim, discutiremos isso mais tarde, pois não é suficiente notificar sobre tarefas importantes uma vez por dia. É necessário enviar e-mail sobre eles imediatamente e regularmente até que o erro seja corrigido.
Se você precisar selecionar dados de vários servidores, é possível combinar os resultados e enviá-los `através de um e-mail.
Referências:
» sysjobs
» sysjobservers
Leituras adicionais:
Coleta Automática de Dados de Alterações de Esquema de Banco de Dados no MS SQL Server
Coleta automática de dados:arquivos de banco de dados e unidades lógicas no MS SQL Server
Configurando notificações do Database Mail no MS SQL Server