Úvod
Pro správce databáze je důležité vědět, jaké úkoly a jak byly dokončeny. Chcete-li tento proces zjednodušit, je lepší jej automatizovat, než jej provádět ručně.
V tomto článku na konkrétním příkladu rozeberu, jak automaticky sbírat data o dokončených úlohách SQL Server Agent.
Řešení
Algoritmus:
- Vytvořte instanci pro výběr úloh:
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; GOK tomu použijte instance sysjobservers a sysjobs_view.
- Vytvořte tabulku pro uložení vybraných dat:
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 - Vytvořte úlohu v SQL Server Agent a získejte informace o těch úlohách, které byly buď prováděny po dlouhou dobu (více než 30 sekund), nebo se nepodařilo dokončit. Tyto informace musíte shromáždit za poslední dva dny:
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()); GOZde můžete nastavit filtr pro odstranění všech nepotřebných úkolů. Například ty úkoly, které odkazují na replikaci, protože její dokončení trvá mnohem déle.
Vygenerujte HTML zprávu a odešlete ji na e-mail administrátorů:
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 @example@sqldat.com+'<TR>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'№ p/p';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'GUID';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'TASK';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'STATUS';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'DATE AND TIME';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'DURATION';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'MESSAGE';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+'SERVER';
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'</TR>';
while((select top 1 1 from @tbl)>0)
begin
set @example@sqldat.com+'<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 @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@ID as nvarchar(max));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+cast(@Job_GUID as nvarchar(255));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@Job_Name,'');
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@LastFinishRunState,'');
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+rep.GetDateFormat(@LastDateTime, default)+' '+rep.GetTimeFormat(@LastDateTime, default);--cast(@InsertDate as nvarchar(max));
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@LastRunDurationString,'');
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@LastOutcomeMessage, '');
set @example@sqldat.com+'</TD>';
set @example@sqldat.com+'<TD>';
set @example@sqldat.com+coalesce(@Server, '');
set @example@sqldat.com+'</TD>';
delete from @tbl
where example@sqldat.com;
set @example@sqldat.com+'</TR>';
end
set @example@sqldat.com+'</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 @example@sqldat.com+'<br><br>For the detailed information, please refer to the table DATABASE_NAME.srv.ShortInfoRunJobs';
END
GO Tato uložená procedura generuje HTML zprávu o dokončených úkolech, které byly provedeny po dobu 30 sekund nebo které se nepodařilo dokončit.
Výsledek
V tomto článku jsem na konkrétním příkladu prozkoumal implementaci každodenního automatického sběru dat o dokončených úkolech v SQL Server Agent. Tyto informace pomáhají určit úlohy, které byly prováděny po dlouhou dobu nebo byly dokončeny s chybou. Umožňuje správci přijmout opatření, aby se v budoucnu takovým chybám vyhnul. Například je možné zrychlit běh úlohy nebo nastavit maximální dobu pro zadanou úlohu.
Toto řešení také pomáhá sledovat problémy související se zálohováním. Přesto o tom budeme diskutovat později, protože nestačí informovat o důležitých úkolech jednou denně. Je nutné o nich okamžitě a pravidelně zasílat emaily, dokud nebude chyba opravena.
Pokud potřebujete vybrat data z několika serverů, je možné výsledky spojit a odeslat je `prostřednictvím jednoho e-mailu.
Odkazy:
» sysjobs
» sysjobservers
Další čtení:
Automatický sběr dat změn databázového schématu na MS SQL Server
Automatický sběr dat:Databázové soubory a logické jednotky na MS SQL Server
Konfigurace notifikací e-mailu databáze v MS SQL Server