MS SQL: Sperren melden als Agentjob

Sperren automatisch erkennen und melden als E-Mail:
 


/*
Erkennen von Sperren und Emails wenn Session dabei warten muessen
- -
Changes:
2015-09-24 11:47 ODFR: Die CSV-Datei kleiner gemacht + den echten Zeitpunkt einer Sperre die behindert! 
2015-09-29 12:34 ODFR: Kommentare sollen nur Slash+Stern sein, sonst Gefahr auf fehlerhafter Funktionalit‰t
2015-09-30 13:22 ODFR: Auch Backup-Jobs m¸ssen ggf. auch dargestellt werden (diese sind keine Transactions)
*/

/*Vorbereitungen:*/
DECLARE @iMinuten int;
SET @iMinuten = 2;
DECLARE @iMinutenSeitSperre int;
DECLARE @cQuery_attachment_filename varchar(64);
SET @cQuery_attachment_filename='Sperren_'+replace(replace(replace(convert(char(16), Getdate(), 121),' ','_'),'-',''),':','')+'.csv';
DECLARE @cQuery_result_separator char(1);
SET @cQuery_result_separator=CHAR(9);


/*Die Query vorbereiten, die sowohl zum Bewerten von Sperren, als auch ggf. in einer Email benutzt wird:*/
DECLARE @cQuery varchar(8000);
SET @cQuery = 'SELECT DISTINCT L.request_session_id AS request_session_id,    /* Anfragender System Process Identifier */
  t2.blocking_session_id as blocking_session_id,    /*Blockierender System Process Identifier */
  /*request_owner_guid AS UOW,*/                      /* Identifiziert die Arbeitseinheits-ID (Unit of Work, UOW) verteilter Transaktionen */
  DB_NAME(L.resource_database_id) AS DatabaseName,  /* Databasename */
  O.Name AS LockedObjectName,                       /* Object name */
  P.object_id AS LockedObjectId,                    /* Eindeutige Object identification number */
  L.resource_type AS LockedResource,                /* Resource which is locked: DATABASE, FILE, OBJECT, PAGE, KEY, EXTENT, RID, APPLICATION, METADATA, HOBT, ALLOCATION_UNIT. */
  L.request_mode AS LockType,                       /* Lock Type: Exclusive locks (X), Intent locks (I), Shared locks (S), Update locks (U),  Schema locks (Sch) */
  /*L.resource_description AS LockDescription,*/      /* LockDescription */
  replace(replace(replace(  ST.text   ,CHAR(9),'' '')  ,CHAR(13),''/*CR*/'')  ,CHAR(10),''/LF*/'')  AS SqlStatementText,                      /* SQL Statement */
  ES.login_name AS LoginName,                       /* Loginname mit dem der User an der DB angemeldet ist */
  ES.host_name AS HostName,                         /* Hostname des verbunden clients */
  TST.is_user_transaction as IsUserTransaction,     /* 1 = Die Transaktion wurde von einer Benutzeranforderung initiiert.  0 = Systemtransaktion. */
  AT.name as TransactionName,                       /* Transaktionsname */
  AT.transaction_begin_time as TransactionStartTime,/* Uhrzeit des Transaktionsbeginns.  */
  coalesce(AT.transaction_begin_time, ES.last_request_start_time) as SQLJobStartTime, /* Uhrzeit des Beginns des laufenden SQLs.  */
  CN.auth_scheme as AuthenticationMethod            /* Login Methode SQL / Windows Authentifizierung */
FROM sys.dm_tran_locks L 
  left JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id 
  left JOIN sys.objects O ON O.object_id = P.object_id 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
  left JOIN sys.dm_exec_connections CN ON CN.session_id = ES.session_id 
  CROSS APPLY sys.dm_exec_sql_text(CN.most_recent_sql_handle) AS ST 
  left  JOIN sys.dm_os_waiting_tasks as t2
        ON L.lock_owner_address = t2.resource_address
WHERE 1=1
      /*and resource_database_id = db_id(''MYDATABASE'')*/  /*nur Locks einer bestimmten Datenbank betrachten*/
      /*and L.request_mode in (''X'', ''IX'')     */               /* nur Exklusive Locks */
      and (t2.blocking_session_id IS NOT NULL OR L.request_mode like ''%X%'' or L.request_mode = ''NULL''  or L.request_mode is NULL)  /*mˆgliche Stˆrer und klare Opfer darstellen*/
ORDER BY L.request_session_id';


/*Code zum Bewerten von Sperren:*/
Declare @dTransactionStartTime Datetime;
Declare @tTable Table (
request_session_id int,    /* Anfragender System Process Identifier */
blocking_session_id int,    /*Blockierender System Process Identifier */
DatabaseName nvarchar(255),  /* Databasename */
LockedObjectName nvarchar(255),                       /* Object name */
LockedObjectId int,                    /* Eindeutige Object identification number */
LockedResource nvarchar(255),          /* Resource which is locked: DATABASE, FILE, OBJECT, PAGE, KEY, EXTENT, RID, APPLICATION, METADATA, HOBT, ALLOCATION_UNIT. */
LockType nvarchar(10),                       /* Lock Type: Exclusive locks (X), Intent locks (I), Shared locks (S), Update locks (U),  Schema locks (Sch) */
SqlStatementText nvarchar(max),                      /* SQL Statement */
LoginName nvarchar(255),                       /* Loginname mit dem der User an der DB angemeldet ist */
HostName nvarchar(255),                         /* Hostname des verbunden clients */
IsUserTransaction int,     /* 1 = Die Transaktion wurde von einer Benutzeranforderung initiiert.  0 = Systemtransaktion. */
TransactionName nvarchar(255),                       /* Transaktionsname */
TransactionStartTime datetime,/* Uhrzeit des Transaktionsbeginns.  */
SQLJobStartTime datetime,/* Uhrzeit des Transaktionsbeginns.  */
AuthenticationMethod nvarchar(255)           /* Login Methode SQL / Windows Authentifizierung */
)
/*Einmal die Eckwerte aller aktuellen Session einlesen, die entweder sperren oder selber warten muessen*/
Insert into @tTable
exec ( @cQuery )
/* Aelteste Sperre, egal ob ein echtes Warten vorliegt oder nicht */
/*
SELECT TOP 1 @dTransactionStartTime=AT.transaction_begin_time
FROM sys.dm_tran_locks L 
  left JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id 
  left JOIN sys.objects O ON O.object_id = P.object_id 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
WHERE AT.transaction_begin_time IS NOT NULL
      and L.request_mode NOT in ('S','IS')
ORDER BY AT.transaction_begin_time ASC;
*/
/* Nur Sperren beachten, wenn ein andere Session wartet */
/*
Select @dTransactionStartTime=Min(AT.transaction_begin_time)
FROM sys.dm_tran_locks L 
  left JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id 
  left JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id 
  left JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id 
  left  JOIN sys.dm_os_waiting_tasks as t2 ON L.lock_owner_address = t2.resource_address
having count(t2.blocking_session_id) > 0
*/
/*Tabelle definieren nur für sperrende und abhaengige Sessions:*/
Declare @tTable2 Table (
request_session_id int,    /* Anfragender System Process Identifier */
blocking_session_id int,    /*Blockierender System Process Identifier */
TransactionStartTime datetime /* Uhrzeit des Transaktionsbeginns.  */
)
/*Und nun die sperrende und abhaengige Sessions aus vorhin befuellten Tabelle bestimmen:*/
;With hLocks (request_session_id, blocking_session_id, TransactionStartTime)
as (
	Select request_session_id, blocking_session_id, SQLJobStartTime
	From @tTable t1 
	where blocking_session_id is NOT NULL
	union all
	Select t2.request_session_id, t2.blocking_session_id, t2.SQLJobStartTime 
	from (
		Select request_session_id, blocking_session_id, SQLJobStartTime
		From @tTable 
		) t2 inner join hLocks on t2.request_session_id = hLocks.blocking_session_id
)
Insert into @tTable2
Select * from hLocks
Select @dTransactionStartTime = Min(TransactionStartTime) from @tTable2
/*
Select * from @tTable
Select @dTransactionStartTime
*/


/*Nun die Ergbnisse bewerten:*/
SET @iMinutenSeitSperre=coalesce(datediff(mi,@dTransactionStartTime,GetDate()),0);

DECLARE @cSubject varchar(128);
SET @cSubject = 'SPERRE SEIT MEHR ALS '+cast(@iMinutenSeitSperre as varchar(10))+' Minuten';

DECLARE @cBody varchar(512);
SET @cBody = 'Die Sperre fing an um '+convert(char(16), @dTransactionStartTime, 121)+' und somit vor '+cast(@iMinutenSeitSperre as varchar(10))+' Minuten.'
	+CHAR(13)+'Es könnte in '+cast((60 - (@iMinutenSeitSperre % 60) ) as varchar(10))+' Minuten eine neue Email versendet werden,'
	+CHAR(13)+'wenn das Problem bis dahin nicht gelöst ist!';

/*Die erste Email darf natürlich kommen und erst dann nur alle volle Stunden*/
/*es können derzeit (leider) bis 3 Email versendet werden */
If @iMinutenSeitSperre > 0
	If (@iMinutenSeitSperre % 60) between @iMinuten and (@iMinuten * 2)
	BEGIN
		/*print 'SPERRE SEIT MEHR ALS '+cast(@iMinuten as varchar(10))+' Minuten'*/
		EXEC msdb.dbo.sp_send_dbmail
		@profile_name = 'SQLService'
		,@recipients = 'admins@emailserver.local'
	    	,@copy_recipients = 'user1@emailserver.local'
		/*,@blind_copy_recipients = 'private1@emailserver.local'*/
	    	,@subject = @cSubject
		,@body = @cBody
		,@query = @cQuery
		,@query_result_header = 1
		,@query_result_separator = @cQuery_result_separator
		,@query_no_truncate = 1
		,@append_query_error = 1
		,@query_result_width = 32767
		,@query_attachment_filename = @cQuery_attachment_filename  /*'Sperren.csv'*/
		,@attach_query_result_as_file = 1 ;
	END
/*
	ELSE
	BEGIN
		print @cBody;
	END
Else
	print 'Keine Sperren.'	
*/

Voraussetzungen: Database Mail ist aktiviert und eingerichtet. Es muss das „Profil“ und natürlich die Ziel Emailadressen angepasst werden.

Ebenso kann es sein, dass im Database Mail die Beschränkung der Attachment-Größe etwas gelockert wird (Systemparameter in Database Mail).

Link: https://docs.microsoft.com/de-de/sql/relational-databases/database-mail/configure-database-mail#SystemParameters