Blocking and locking is a main area for performance tuning and to ensure a good health of production system. Here is a script to notify DBAs about the locking occurs in the production system.It uses service brokers to notify the lock information.
As a first step, we need to set a threshold value for blocked processes. This can be set using sp_configure as below:
--Set a throshold for blocked process to 10 Sec
sp_configure 'blocked process threshold (s)',10
Once we set a threshold value for the blocking, the next step is to create a queue and service for the event to put an entry in the queue.
--Create a Queue for the event notification
CREATE QUEUE DBA_Prod_Lock_Queue
--Create a Service on queue for the event notification
CREATE SERVICE DBA_Prod_Lock_Service
ON QUEUE DBA_Prod_Lock_Queue ( [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification] )
We need to set up a event notification to notify the event of blocking for the threshold.
--Create an event notification for the blocked process report
CREATE EVENT NOTIFICATION DBA_Prod_Notify_Locks
ON SERVER
WITH fan_in
FOR blocked_process_report
TO SERVICE 'DBA_Prod_Lock_Service', 'current database';
Verify the messages are coming to the queue whenever a lock exceeds a threshold and the event notification.
SELECT cast( message_body as xml ), *
FROM DBA_Prod_Lock_Queue
SELECT * FROM sys.server_event_notifications
The below table is a physical table in our local db to hold the event notification further analysis.
Create Table DBA_ProductionMonitor_EventLockInformation
(
SeqID BigInt Identity(1,1),
MessageBody XML,
DatabaseID Int,
Process XML,
Is_Notified Bit Default(0)
)
The below procedure recieve the messages from the queue and push into a local table to proceed with our functionality.
Create Proc usp_DBA_ProductionMonitor_ServiceProc
AS
Begin
DECLARE @msgs TABLE ( message_body xml not null,
message_sequence_number int not null );
RECEIVE message_body, message_sequence_number
FROM DBA_Prod_Lock_Queue
INTO @msgs;
Insert Into DBA_ProductionMonitor_EventLockInformation(MessageBody,DatabaseID,Process)
SELECT message_body,
DatabaseId = cast( message_body as xml ).value( '(/EVENT_INSTANCE/DatabaseID)[1]', 'int' ),
Process = cast( message_body as xml ).query( '/EVENT_INSTANCE/TextData/blocked-process-report/blocked-process/process' )
FROM @msgs
ORDER BY message_sequence_number
End
The below procedure is used to extract the relevant information from the local table where the messages been pushed from queue and send a mail for lock notification.
Alter Procedure usp_DBA_ProductionMonitor_LockInformation
As
Begin
declare @body varchar(max) =''
Create Table #Temp_SeqIds (SeqId Int)
Insert into #Temp_SeqIds Select SeqID From DBA_ProductionMonitor_EventLockInformation Where Is_Notified=0
IF Exists(Select 1 From #Temp_SeqIds)
Begin
set @body = cast( (
select td =
Cast(BlockedSPID as Varchar(50))
+ '' + Cast(BlockedStatus as Varchar(MAX)) + ' | ' + CAST( BlockedCommand as Varchar(MAX)) + ' | ' + Cast(BlockedHostName as Varchar(50)) + ' | ' + Cast(BlockedLoginName as Varchar(MAX)) + ' | ' + CAST( BlockingSPID as Varchar(MAX)) + ' | ' + Cast(BlockingStatus as Varchar(MAX)) + ' | ' + CAST( BlockingCommand as Varchar(MAX)) + ' | ' + Cast(BlockingHostName as Varchar(50)) + ' | ' + Cast(BlockingLoginName as Varchar(MAX)) + ' | ' + CAST( DataBaseID as Varchar(MAX)) + ' | ' + CAST( ServerName as Varchar(MAX)) + ' | ' + CAST( StartTime as Varchar(MAX)) + ' | ' + CAST( EndTime as Varchar(MAX)) from ( SELECT --Blocked Information BlockedSPID=MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocked-process/process/@spid)[1]', 'varchar(100)' ) , BlockedStatus=MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocked-process/process/@status)[1]', 'varchar(100)' ) , BlockedCommand=MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocked-process/process)[1]', 'varchar(max)' ) , BlockedHostName = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocked-process/process/@hostname)[1]', 'varchar(100)' ) , BlockedLoginName = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocked-process/process/@loginname)[1]', 'varchar(100)' ) , --Blocking Information BlockingSPID = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocking-process/process/@spid)[1]', 'varchar(100)' ) , BlockingStatus = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocking-process/process/@status)[1]', 'varchar(100)' ) , BlockingCommand = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocking-process/process)[1]', 'varchar(max)' ) , BlockingHostName = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocking-process/process/@hostname)[1]', 'varchar(100)' ) , BlockingLoginName = MessageBody.value( '(/EVENT_INSTANCE/TextData/blocked-process-report/blocking-process/process/@loginname)[1]', 'varchar(100)' ) , --General Information DataBaseID = MessageBody.value( '(/EVENT_INSTANCE/DatabaseID)[1]', 'int' ) , ServerName = MessageBody.value( '(/EVENT_INSTANCE/ServerName)[1]', 'varchar(100)') , StartTime = MessageBody.value( '(/EVENT_INSTANCE/StartTime)[1]', 'datetime' ) , EndTime = MessageBody.value( '(/EVENT_INSTANCE/EndTime)[1]', 'datetime' ) FROM DBA_ProductionMonitor_EventLockInformation A Inner Join #Temp_SeqIds B On A.SeqID = B.SeqId ) as d for xml path( 'tr' ), type ) as varchar(max) ) set @body = ' ' + ' ' + ' Blocked SPID | Blocked Status | Blocked Command | Blocked HostName | Blocked LoginName | ' + ' Blocking SPID | Blocking Status | Blocking Command | Blocking HostName | Blocking LoginName | ' + ' Database ID | Server Name | Start Time | End Time | ' + ' ' + replace( replace( @body, '<', '<' ), '>', '>' ) + ' ' print @body EXEC msdb.dbo.sp_send_dbmail @PROFILE_NAME = '', @recipients='', @subject = 'DBA Notification - Production Lock Occured.', @body = @body, @body_format = 'HTML' ; Update A Set Is_Notified = 1 From DBA_ProductionMonitor_EventLockInformation A Inner Join #Temp_SeqIds B On A.SeqID = B.SeqId End
The below script to enable and disable the queue to maintain the functionality.
--Disable Queue ALTER QUEUE [dbo].[DBA_Prod_Lock_Queue] WITH STATUS = OFF , RETENTION = OFF , ACTIVATION ( STATUS = OFF , MAX_QUEUE_READERS = 0 , EXECUTE AS OWNER ) --Enable Queue ALTER QUEUE [dbo].[DBA_Prod_Lock_Queue] WITH STATUS = ON , RETENTION = OFF , ACTIVATION ( STATUS = OFF , MAX_QUEUE_READERS = 0 , EXECUTE AS OWNER )
Hope the script would be useful for all of us!!!
Here is a script for finding out the calculating the work days (excluding the saturdays and sundays). The script can be altered also to accomodate local holidays in a separate table too. Only thing is to substract the count of the table for that period. Declare @StartDate Date, @EndDate Date Set @StartDate = Getdate()-20 Set @EndDate = Getdate()
SELECT (DATEDIFF(dd, @StartDate, @EndDate) + 1) -(DATEDIFF(wk, @StartDate, @EndDate) * 2) -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END) -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
I am certain some of us would have really come across a situation that the spaces between data becomes annoying and needs to be removed on request. Many of the solutions have come across multiple blogs and sites with while loop or cursor was really made to think Is there any way??? Hmm!! On searching my tressure, after a while, found something really interested(I dont really know to whome should give the credit, ofcourse it should go the person.) . The idea was really good and quite simple.Sometimes we do take the things very complicated and dont think about a simple solution.Here it is .......
Have you ever thoguht of generating sequential numbers? I guess, many of us had atleast once a scenario atleast for our reporting purpose to think about sequencing the records. Here am going to explain how do I come across with various SQL server versions. We have something like IDENTITY (I will explain little more about this later) in all of SQL versions available. It is a kind of methodology given by SQL server to generate sequencing. How about when you do have scenario to generate the sequence while you write a procedure.Here we go!!! SQL Server 2000 SELECT RowNumber = IDENTITY(INT,1,1) ,c.LastName ,c.FirstName INTO #Customer_RowID FROM SalesLT.Customer c ORDER BY c.LastName ASC SELECT * FROM #Customer_RowID
SQL Server 2005 and 2008 SELECT PurchaseID, PurchaseDate, ROW_NUMBER() OVER (ORDER BY PurchaseDate) AS 'RowNumber' FROM PurchaseOrder
SQL Server -(Denali)In addition to ROW_NUMBER, there is something called Sequence introduced in Denali Version. I have not got any personal experience with the same.Hence am just giving a clue...
The below script will do a rebuilding of indexes of all tables in a database. We can explicitly mention the below features for this procedure: 1. Excluded list of tables 2. MAXDOP can be specified. 3. Fillfactor can be specified(by default it takes from the system catalogs. set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go
ALTER PROCEDURE [dbo].[RebuildAllIndexes] AS SET NOCOUNT ON; DECLARE @objectid int; DECLARE @indexid int; DECLARE @indexname sysname; DECLARE @maxdop tinyint; DECLARE @schemaname nvarchar(130); DECLARE @objectname nvarchar(130); DECLARE @partitionnum bigint; DECLARE @partitions bigint; DECLARE @frag float; DECLARE @command nvarchar(4000); DECLARE @infomessage varchar(1000); DECLARE @fillfactor tinyint; DECLARE @returncode tinyint; DECLARE @dbname sysname; DECLARE @localerror integer; DECLARE @ExcludeTableList varchar(100)
SET @ExcludeTableList = ''+',' SET @dbname = DB_NAME()
-- Set MAXDOP default value IF (@maxdop IS NULL) BEGIN SET @maxdop = (SELECT CONVERT(smallint, value) FROM master.sys.configurations (NOLOCK) WHERE name = 'max degree of parallelism') END SET @returncode = 0
-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function -- and convert object and index IDs to names. IF OBJECT_ID('tempdb..#work_to_do') IS NOT NULL BEGIN DROP TABLE #work_to_do END CREATE TABLE #work_to_do( [objectid] [int] NULL, [indexid] [int] NULL, [fill_factor] [int] NOT NULL ) -- Get indexes that meet our criteria INSERT INTO #work_to_do SELECT o.object_id,i.index_id, CASE i.fill_factor WHEN 0 THEN 100 ELSE i.fill_factor END AS fill_factor FROM sys.indexes i (NOLOCK) INNER JOIN sys.objects o(nolock) ON i.object_id = o.object_id WHERE i.index_id > 0 AND o.type ='U' AND CHARINDEX(o.name+',',@ExcludeTableList) = 0 ORDER BY O.name,i.index_id -- Declare the cursor for the list of partitions to be processed. DECLARE partitions CURSOR FOR SELECT * FROM #work_to_do; -- Open the cursor. OPEN partitions; -- Loop through the partitions. WHILE (1=1) BEGIN; FETCH NEXT FROM partitions INTO @objectid, @indexid, @fillfactor; --@partitionnum, @frag, ; IF @@FETCH_STATUS < objectname =" QUOTENAME(o.name)," schemaname =" QUOTENAME(s.name)" schema_id =" o.schema_id" object_id =" @objectid;" schemaname =" '[sys]')" indexname =" QUOTENAME(name)" object_id =" @objectid" index_id =" @indexid;" command =" N'ALTER" command =" @command" command =" @command" fillfactor =" '" command =" @command" maxdop =" '" command =" @command" infomessage =" 'Executing" localerror =" @@ERROR"> 0 BEGIN SET @infomessage = 'Reindex failed with error ' + CAST(@localerror AS varchar(10)) + '.' RAISERROR (@infomessage, 11, 1) WITH LOG, NOWAIT SET @returncode = @returncode + 1 END END; -- Close and deallocate the cursor. CLOSE partitions; DEALLOCATE partitions; -- Drop the temporary table. DROP TABLE #work_to_do; RETURN @returncode
Split function is one of the main string function to split a list of items separated by a character in our daily functions. 1. Using While loop
ALTER FUNCTION [dbo].[Split] ( @List nvarchar(2000), @SplitOn nvarchar(5) ) RETURNS @RtnValue table ( Id int identity(1,1), Value nvarchar(100) ) AS BEGIN While (Charindex(@SplitOn,@List)>0) Begin Insert Into @RtnValue (value) Select Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1))) Set @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List)) End Insert Into @RtnValue (Value) Select Value = ltrim(rtrim(@List)) Return END
|