First of all, I prepared the data in SQL, this is the statement:
--===============================================================
--// Created a table to save all the Locks
--===============================================================
USE [DBA]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ServerLock](
[ServerLockID] [int] IDENTITY(1,1) NOT NULL,
[ServerName] [varchar](100) NOT NULL,
[PerformanceIndicatorCategoryName] [varchar](100) NOT NULL,
[PerformanceIndicatorName] [varchar](100) NOT NULL,
[PerformanceAVGIndicatorValue] [float] NULL,
[PerformanceIndicatorValue] [float] NOT NULL,
[SPID] [int] NULL,
[Status] [varchar](1000) NULL,
[Login] [sysname] NULL,
[HostName] [sysname] NULL,
[BlkBy] [int] NULL,
[DBName] [sysname] NULL,
[Command] [varchar](1000) NULL,
[CPUTime_ms] [bigint] NULL,
[waittime_ms] [bigint] NULL,
[Open_Tran] [varchar](4) NULL,
[waitresource] [nvarchar](1000) NULL,
[lastwaittype] [nvarchar](1000) NULL,
[memusage_pages] [bigint] NULL,
[DiskIO_RW] [int] NULL,
[LastBatch] [datetime] NULL,
[ProgramName] [varchar](1000) NULL,
[ModifiedDateOn] [smalldatetime] NULL,
[DataHash] [binary](1) NULL,
[CreatedDateOn] [datetime] NOT NULL,
[IsOperational] [bit] NULL,
[LockDateID] [int] NULL,
CONSTRAINT [ServerLockID_pk] PRIMARY KEY CLUSTERED
(
[ServerLockID] DESC
)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 [dbo].[ServerLock] ADD DEFAULT (getdate()) FOR [ModifiedDateOn]
GO
--===============================================================
--// Created a SP to gathers all the Locks
--===============================================================
USE [DBA]
GO
/****** Object: StoredProcedure [dbo].[sp_DBAWEB_Lock] Script Date: 15/10/2018 4:47:46 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER proc [dbo].[sp_DBAWEB_Lock] (
@OperatorGroup Varchar(50) = 'DBA',
@IntervalTime Int = 5,
@SendEmail char(1) = 'N',
@AlertName varchar(200) = 'Lock Wait Time',
@AlertWindowStarts Int = '6',
@AlertWindowStops Int = '21'
)
as
/*****************************************************************************************************************
Name: dbo.sp_DBAWEB_lock
Shows the existents transaction locks and evidence causes.
*****************************************************************************************************************
Change History
*****************************************************************************************************************
Date: Author: Description:
----- ------- -------------------
10 Oct 2018 Leandro Buffone Created Initial Version
*****************************************************************************************************************
Usage:
set statistics io on
set statistics time on
exec dbo.sp_DBAWEB_lock @SendEmail = 'Y'
*****************************************************************************************************************/
If (datepart(Hour, getdate()) between @AlertWindowStarts and @AlertWindowStops) Begin
DECLARE @ID varchar(10),
@EmailOperator varchar(400),
@AlertComments varchar(800),
@strEmailBody varchar(Max),
@nOccurence varchar(4),
@strEmailSubject varchar(400),
@strSQLTranCause varchar(max),
@strSQL nvarchar(400),
@DataHash binary,
@ServerName nvarchar(100),
@PerformanceIndicatorCategoryName nvarchar(100),
@PerformanceIndicatorName nvarchar(100),
@PerformanceAVGIndicatorValue int,
@PerformanceIndicatorValue int,
@IsOperational int
Set @ServerName = @@ServerName
Set @PerformanceIndicatorCategoryName = 'Lock'
Set @PerformanceIndicatorName = 'Lock Wait Time'
Set @PerformanceAVGIndicatorValue = 1
Set @PerformanceIndicatorValue = 1
Set @DataHash = (Select hashbytes('MD5',concat(getdate(),'')))
Set @IsOperational = (Case When (datepart(hour, getdate()) Between 8 and 18) Then 1 Else 0 End);
DECLARE @tbSP_who2Result TABLE
(
SPID INT,
Status VARCHAR(1000) NULL,
Login SYSNAME NULL,
HostName SYSNAME NULL,
BlkBy INT NULL,
DBName SYSNAME NULL,
Command VARCHAR(1000) NULL,
CPUTime_ms BIGINT NULL,
waittime_ms BIGINT,
Open_Tran BIGINT,
waitresource NVARCHAR(1000),
lastwaittype NVARCHAR(1000),
memusage_pages BIGINT,
DiskIO_RW INT NULL,
LastBatch DATETIME,--VARCHAR(1000) NULL,
ProgramName VARCHAR(1000) NULL,
ModifiedDateOn SMALLDATETIME DEFAULT GETDATE(),
CreatedDateOn SMALLDATETIME DEFAULT GETDATE(),
DataHash binary,
LockDateId int,
IsOperational int
)
Declare @tranResult Table (Event varchar(50), Parameters varchar(50), EventInfo varchar(max))
--// Converted to millisecond
set @IntervalTime = ((@IntervalTime * 60) )
if (select count(1) from sysprocesses where blocked > 0)>0 and (select max(waittime) from sysprocesses where blocked > 0) > @IntervalTime begin
INSERT INTO @tbSP_who2Result
( SPID ,
status,
Login,
HostName,
BlkBy,
DBName,
Command,
CPUTime_ms,
waittime_ms,
Open_Tran,
waitresource,
lastwaittype,
memusage_pages,
DiskIO_RW,
LastBatch,
ProgramName,
DataHash,
LockDateId
)
SELECT
spid
,p.status
,convert(sysname, rtrim(p.loginame)) as loginname
,p.hostname
,blocked
,DB_NAME(p.dbid)
,p.cmd
,p.cpu
,p.waittime
,p.open_Tran
,waitresource
,lastwaittype
,memusage
,physical_io
,p.last_batch
,p.program_name
,@DataHash
,dbo.fnDateKey(getdate())
from master.dbo.sysprocesses p (nolock)
where
(
blocked <> 0
or
spid in (Select blocked from sysprocesses)
)
If @SendEmail = 'N' Begin
Select '--// -- Locked Transactions'
SELECT
SPID,
BlkBy,
Login,
ProgramName,
DBName,
Command,
HostName,
LastBatch,
CPUTime_ms,
Open_Tran = Case When open_Tran = 0 then 'No' Else 'Yes' End,
DiskIO_RW,
status,
DataHash
,dbo.fnDateKey(getdate())
FROM @tbSP_who2Result a
where BlkBy <> 0
order by 2
Select '--// -- Caused the lock by the transaction'
SELECT
SPID,
Login,
ProgramName,
DBName,
Command,
HostName,
LastBatch,
CPUTime_ms,
Open_Tran = Case When open_Tran = 0 then 'No' Else 'Yes' End,
DiskIO_RW,
status,
DataHash
,dbo.fnDateKey(getdate())
FROM @tbSP_who2Result a
where SPID in (Select BlkBy from @tbSP_who2Result)
order by 2
End
--// Details about which transaction is causing the lock'
Declare CUR CURSOR FOR
SELECT distinct blkby FROM @tbSP_who2Result where blkby <> 0
Open CUR
Fetch Next From CUR
Into @id
While @@FETCH_STATUS = 0
Begin
set @strSQL = 'dbcc inputbuffer(' + @id +')'
insert @tranResult
exec sp_executeSQL @strSQL
set @strSQLTranCause = (select EventInfo from @tranResult)
Delete @tranResult
Update @tbSP_who2Result Set Command = @strSQLTranCause where SPID = @id
Fetch Next From CUR
Into @id
End
Close CUR
Deallocate CUR
--// Save for historical analysis
Insert [dbo].[ServerLock] ([ServerName],[PerformanceIndicatorCategoryName],[PerformanceIndicatorName],[PerformanceAVGIndicatorValue],[PerformanceIndicatorValue],SPID,Status,Login,HostName,BlkBy,DBName,Command,CPUTime_ms,waittime_ms, Open_Tran,waitresource,lastwaittype,memusage_pages,DiskIO_RW,LastBatch,ProgramName,ModifiedDateOn,DataHash,[CreatedDateOn], IsOperational,LockDateID)
Select @ServerName, @PerformanceIndicatorCategoryName, @PerformanceIndicatorName, @PerformanceAVGIndicatorValue, @PerformanceIndicatorValue,SPID,Status,Login,HostName,BlkBy,DBName,Command,CPUTime_ms,waittime_ms,Case When open_Tran = 0 then 'No' Else 'Yes' End,waitresource,lastwaittype,memusage_pages,DiskIO_RW,LastBatch,ProgramName,ModifiedDateOn,DataHash,[CreatedDateOn],@IsOperational,LockDateID from @tbSP_who2Result
--// Gather the operator email
Set @EmailOperator = (select email_address from msdb.dbo.sysoperators where name = 'DBA')
If @SendEmail = 'Y' and @EmailOperator is not null Begin
set @AlertComments = (select notification_message from msdb.dbo.sysalerts where name = @AlertName)
set @nOccurence = (select occurrence_count from msdb.dbo.sysalerts where name = @AlertName)
--// Reset the count - Lock Wait Time
update msdb.dbo.sysalerts set occurrence_count = 0 where name = @AlertName
set @strEmailBody = 'DATE/TIME: ' + convert(varchar(100), getdate()) + '
'
set @strEmailBody = @strEmailBody + 'DESCRIPTION:
The SQL Server performance counter Lock waits (instance Waits in progress) of object Wait Statistics is now above the threshold of 0.00 (the current value is ' + @nOccurence + ').
'
set @strEmailBody = @strEmailBody + '
Impacted Processes:
'
set @strEmailBody = @strEmailBody + '
'+
CAST ( (
SELECT td = CAST(SPID AS VARCHAR(100)),'',
td = CAST(BlkBy AS VARCHAR(100)),'',
td = Login,'',
td = ProgramName ,'',
td = DBName ,'',
td = Command,'',
td = HostName,'',
td = status,''
FROM @tbSP_who2Result a
WHERE blkby <> 0
order by 2
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
N'
SPID |
BlkBy |
Login |
ProgramName |
DBName |
Command |
HostName |
status |
'
set @strEmailBody = @strEmailBody + '
Caused by Process:
'
set @strEmailBody = @strEmailBody + '
'+
CAST ( (
SELECT td = CAST(SPID AS VARCHAR(100)),'',
td = CAST(BlkBy AS VARCHAR(100)),'',
td = Login,'',
td = ProgramName ,'',
td = DBName ,'',
td = Command,'',
td = HostName,'',
td = status,''
FROM @tbSP_who2Result a
WHERE exists
(select 1 from @tbSP_who2Result b
where blkby <> 0
and a.spid=b.blkby
)
order by 2
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
N'
SPID |
BlkBy |
Login |
ProgramName |
DBName |
Command |
HostName |
status |
'
set @strEmailBody = @strEmailBody + '
Caused by Transaction:
'
set @strEmailBody = @strEmailBody + @strSQLTranCause + '
'
set @strEmailSubject = Concat(@@SERVERNAME,' - SQL Server Alert System: Lock Wait Time')
EXEC msdb..sp_send_dbmail
@recipients = @EmailOperator
,@subject = @strEmailSubject
,@body_format = 'HTML'
,@body = @strEmailBody
End
End
End
--===============================================================
--// Created a SQL Job to call the SP (don't create any schedule)
--===============================================================
-- I called the SQL Job as: DBAWEB - Lock Wait Time - Analysis
--===============================================================
--// Created an Operator Email called DBA
--===============================================================
USE [msdb]
GO
/****** Object: Operator [DBA] Script Date: 15/10/2018 4:55:28 PM ******/
EXEC msdb.dbo.sp_add_operator @name=N'DBA',
@enabled=1,
@weekday_pager_start_time=90000,
@weekday_pager_end_time=180000,
@saturday_pager_start_time=90000,
@saturday_pager_end_time=180000,
@sunday_pager_start_time=90000,
@sunday_pager_end_time=180000,
@pager_days=0,
@email_address=N'myemail@onedbsolution.com',
@category_name=N'[Uncategorized]'
GO
--===============================================================
--// Created an Alert called Lock Wait Time
--===============================================================
USE [msdb]
GO
EXEC msdb.dbo.sp_add_alert @name=N'Lock Wait Time',
@message_id=0,
@severity=0,
@enabled=1,
@delay_between_responses=300,
@include_event_description_in=1,
@category_name=N'[Uncategorized]',
@performance_condition=N'Wait Statistics|Lock waits|Waits in progress|>|0',
@job_id=N'0ea99502-abac-4f62-9f8e-000112c756f4'
GO
--===============================================================
--// Created a View used as my Power By source
--===============================================================
use DBA
go
create or alter view vwSQLLock
as
SELECT [ServerLockID]
,[ServerName]
,[PerformanceIndicatorCategoryName]
,[PerformanceIndicatorName]
,[PerformanceAVGIndicatorValue]
,[PerformanceIndicatorValue]
,[SPID]
,[Status]
,[Login]
,[HostName]
,[BlkBy]
,[DBName]
,[Command]
,[CPUTime_ms]
,[waittime_ms]
,[Open_Tran]
,[waitresource]
,[lastwaittype]
,[memusage_pages]
,[DiskIO_RW]
,[LastBatch]
,[ProgramName]
,[ModifiedDateOn]
,[DataHash]
,[CreatedDateOn]
,[IsOperational]
,[LockDateID]
,LockTotal = Case When (Lag(DataHash,1,0) over (order by createdDateOn ) <> DataHash) then 1 else 0 End
FROM [DBA].[dbo].[ServerLock]