<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Reducing SQL Server Deadlocks</title>
	<atom:link href="http://www.sql-server-performance.com/2006/deadlocks/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.sql-server-performance.com/2006/deadlocks/</link>
	<description>SQL Server Performance Tuning</description>
	<lastBuildDate>Fri, 17 May 2013 13:31:24 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
	<item>
		<title>By: Mike Pindrik</title>
		<link>http://www.sql-server-performance.com/2006/deadlocks/#comment-1397</link>
		<dc:creator>Mike Pindrik</dc:creator>
		<pubDate>Tue, 21 Feb 2012 16:52:42 +0000</pubDate>
		<guid isPermaLink="false">http://www.sql-server-performance.com/?=259#comment-1397</guid>
		<description><![CDATA[As mentioned in the main part, one of the improvements would be is to reduce lock time.
In our shop one of the stored procedures has a complicated query that uses many views and constructs a long XML. The query runs for about 5 minutes and occasionally causes deadlocks elsewhere. We decided to place the data, constructed by views, into temporary tables and to use temporary table instead of views in the complicated query itself. As the result, permanent tables became locked only for about 1-2 minutes, although the whole SP was still running 5 minutes or longer.
Identifying the length of time of tables being locked is not very straight forward, so we created an SP that runs in a separate Query Analyzer session and monitors another process for tables being locked. Here is the code of this monitoring SP:
IF OBJECT_ID(N&#039;DBO.MonLocks&#039;, &#039;P&#039;) &gt; 0
BEGIN
	DROP PROCEDURE DBO.MonLocks
END
GO
CREATE   PROCEDURE DBO.MonLocks
          (@Process_id as int  = NULL,
          @DBName VARCHAR (63) = &#039;DailyDB&#039;,
          @IncludeTempDB CHAR (1) = &#039;N&#039;)
AS
/************************************************************************************
PRINT @@SPID
EXEC MonLocks
@Process_id = 187,
@DBName		= &#039;WeeklyDB&#039;
	select * from ##TTot
	order by TimeLocked DESC
************************************************************************************/
BEGIN
	SET NOCOUNT ON
	DECLARE @CpuUsed BIGINT,
			@TickStart DATETIME,
			@TickEnd DATETIME,
			@Msec FLOAT,
			@SQL NVARCHAR(MAX),
			@WaitFor_Delay DATETIME = &#039;00:00:01&#039;,	-- 1 second
			-- need to count how many time in a row &#039;CPU idle&#039; happenes
			-- because in tests &#039;CPU idle&#039; was for mor than 3 seconds,
			-- but the monitored process was still running
			@WaitsTotIdleCPU INT = 15,
			@WaitsCntIdleCPU INT
	IF OBJECT_ID(&#039;TEMPDB..##TCurr&#039;, &#039;U&#039;) IS NOT NULL
		DROP TABLE ##TCurr
	IF OBJECT_ID(&#039;TEMPDB..##TTot&#039;, &#039;U&#039;) IS NOT NULL
		DROP TABLE ##TTot
	-- create temp tabs
	CREATE TABLE ##TCurr (TabName VARCHAR(511)	NOT NULL)
	CREATE TABLE ##TTot (
			TabName			VARCHAR(511)	NOT NULL,
			TimeLocked		FLOAT		NULL	DEFAULT 0.0,
			LockStart		DATETIME	NULL,
			LastLockEnded	DATETIME	NULL)
	-- wait for process to start
	WHILE 1=1
	BEGIN
		-- save @CpuUsed
		SELECT @CpuUsed = cpu
		FROM master..sysprocesses
		WHERE spid = @Process_id
		-- wait for 1 second
		WAITFOR DELAY @WaitFor_Delay
		-- if @CpuUsed changed, the processing started
		IF @CpuUsed
		(
			SELECT TOP 1 cpu
			FROM master..sysprocesses
			WHERE spid = @Process_id
		)
			BREAK
	END
	-- processing started
	-- save CPU start time
	INSERT ##TTot
	SELECT TabName=&#039;CPU&#039;, TimeLocked=0, LockStart = GETDATE(), LastLockEnded = GETDATE()
	-- &#039;SEED&#039; the @TickStart
	SET  @TickStart = GETDATE()
	-- accumulate tables lock times
	WHILE 1=1
	BEGIN
		TRUNCATE TABLE ##TCurr
		-- get all currently active perm tables
		SET @SQL = &#039;INSERT ##TCurr
				SELECT DISTINCT SO.Name
				FROM sys.dm_tran_locks TL
				JOIN &#039; + @DBName + &#039;..SYSOBJECTS SO ON SO.ID =TL.resource_associated_entity_id
				WHERE request_session_id = &#039; + CAST(@Process_id AS VARCHAR)
		EXEC sp_executesql @SQL
		-- get all currently active temp tables
		IF @IncludeTempDB = &#039;Y&#039;
		BEGIN
			-- TEMPDB is causing deadlocks and early termination
			INSERT ##TCurr
			SELECT DISTINCT SO.Name
			FROM sys.dm_tran_locks TL
			JOIN TEMPDB..SYSOBJECTS SO ON SO.ID =TL.resource_associated_entity_id
			WHERE request_session_id = @Process_id
		END
		-- insert missing active permanent tables into ##TTot
		INSERT ##TTot (TabName, LockStart)
		SELECT DISTINCT TabName, LockStart = @TickStart
		FROM ##TCurr
		WHERE NOT EXISTS
		(
			SELECT 1 FROM ##TTot WHERE TabName = ##TCurr.TabName
		)
		-- save @CpuUsed
		SELECT @CpuUsed = cpu
		FROM master..sysprocesses
		WHERE spid = @Process_id
		-- wait for 1 second
		WAITFOR DELAY @WaitFor_Delay
		-- if @CpuUsed did not change, the processing stopped
		IF @CpuUsed =
		(
			SELECT TOP 1 cpu
			FROM master..sysprocesses
			WHERE spid = @Process_id
		)
		BEGIN
			-- comes here if CPU did not change for the process
			-- need to count how many time in a row &#039;CPU idle&#039; happenes
			-- because in tests &#039;CPU idle&#039; was for mor than 3 seconds,
			-- but the monitored process was still running
			SET @WaitsCntIdleCPU = @WaitsCntIdleCPU + 1
			IF @WaitsCntIdleCPU &lt; @WaitsTotIdleCPU
			BEGIN
				-- just keep counting &#039;CPU idle&#039; occurances
				CONTINUE
			END
			ELSE
			BEGIN
				-- process did not get CPU for long enough time,
				-- so assume it stopped
				BREAK
			END
		END
		SET @WaitsCntIdleCPU = 0
		SET	@TickEnd = GETDATE()
		SET	@Msec = DATEDIFF(ms, @TickStart, @TickEnd) / 1000.0
		-- keep @TickStart close to @TickEnd, so
		-- SUM will match the real difference between end time and start time
		SET  @TickStart = GETDATE()
		-- update CPU time
		UPDATE ##TTot
			SET TimeLocked = TimeLocked + @Msec,
				LastLockEnded = GETDATE()
			WHERE TabName = &#039;CPU&#039;
		-- update ##TTot for tables that are currently active
		UPDATE ##TTot
			SET TimeLocked = TimeLocked + @Msec,
				LastLockEnded = GETDATE()
			WHERE EXISTS (SELECT 1 FROM ##TCurr WHERE TabName = ##TTot.TabName)
	END
	-- update CPU time
	UPDATE ##TTot
		SET LastLockEnded = GETDATE()
		WHERE TabName = &#039;CPU&#039;
	select * from ##TTot
	order by TimeLocked DESC
	select * from ##TTot
	order by LastLockEnded DESC
END]]></description>
		<content:encoded><![CDATA[<p>As mentioned in the main part, one of the improvements would be is to reduce lock time.<br />
In our shop one of the stored procedures has a complicated query that uses many views and constructs a long XML. The query runs for about 5 minutes and occasionally causes deadlocks elsewhere. We decided to place the data, constructed by views, into temporary tables and to use temporary table instead of views in the complicated query itself. As the result, permanent tables became locked only for about 1-2 minutes, although the whole SP was still running 5 minutes or longer.<br />
Identifying the length of time of tables being locked is not very straight forward, so we created an SP that runs in a separate Query Analyzer session and monitors another process for tables being locked. Here is the code of this monitoring SP:</p>
<p>IF OBJECT_ID(N&#8217;DBO.MonLocks&#8217;, &#8216;P&#8217;) &gt; 0<br />
BEGIN<br />
	DROP PROCEDURE DBO.MonLocks<br />
END<br />
GO</p>
<p>CREATE   PROCEDURE DBO.MonLocks<br />
          (@Process_id as int  = NULL,<br />
          @DBName VARCHAR (63) = &#8216;DailyDB&#8217;,<br />
          @IncludeTempDB CHAR (1) = &#8216;N&#8217;)<br />
AS</p>
<p>/************************************************************************************<br />
PRINT @@SPID</p>
<p>EXEC MonLocks<br />
@Process_id = 187,<br />
@DBName		= &#8216;WeeklyDB&#8217;</p>
<p>	select * from ##TTot<br />
	order by TimeLocked DESC</p>
<p>************************************************************************************/<br />
BEGIN<br />
	SET NOCOUNT ON</p>
<p>	DECLARE @CpuUsed BIGINT,<br />
			@TickStart DATETIME,<br />
			@TickEnd DATETIME,<br />
			@Msec FLOAT,<br />
			@SQL NVARCHAR(MAX),<br />
			@WaitFor_Delay DATETIME = &#8217;00:00:01&#8242;,	&#8211; 1 second<br />
			&#8211; need to count how many time in a row &#8216;CPU idle&#8217; happenes<br />
			&#8211; because in tests &#8216;CPU idle&#8217; was for mor than 3 seconds,<br />
			&#8211; but the monitored process was still running<br />
			@WaitsTotIdleCPU INT = 15,<br />
			@WaitsCntIdleCPU INT</p>
<p>	IF OBJECT_ID(&#8216;TEMPDB..##TCurr&#8217;, &#8216;U&#8217;) IS NOT NULL<br />
		DROP TABLE ##TCurr<br />
	IF OBJECT_ID(&#8216;TEMPDB..##TTot&#8217;, &#8216;U&#8217;) IS NOT NULL<br />
		DROP TABLE ##TTot</p>
<p>	&#8211; create temp tabs<br />
	CREATE TABLE ##TCurr (TabName VARCHAR(511)	NOT NULL)</p>
<p>	CREATE TABLE ##TTot (<br />
			TabName			VARCHAR(511)	NOT NULL,<br />
			TimeLocked		FLOAT		NULL	DEFAULT 0.0,<br />
			LockStart		DATETIME	NULL,<br />
			LastLockEnded	DATETIME	NULL)</p>
<p>	&#8211; wait for process to start<br />
	WHILE 1=1<br />
	BEGIN<br />
		&#8211; save @CpuUsed<br />
		SELECT @CpuUsed = cpu<br />
		FROM master..sysprocesses<br />
		WHERE spid = @Process_id</p>
<p>		&#8211; wait for 1 second<br />
		WAITFOR DELAY @WaitFor_Delay</p>
<p>		&#8211; if @CpuUsed changed, the processing started<br />
		IF @CpuUsed<br />
		(<br />
			SELECT TOP 1 cpu<br />
			FROM master..sysprocesses<br />
			WHERE spid = @Process_id<br />
		)<br />
			BREAK<br />
	END</p>
<p>	&#8211; processing started</p>
<p>	&#8211; save CPU start time<br />
	INSERT ##TTot<br />
	SELECT TabName=&#8217;CPU&#8217;, TimeLocked=0, LockStart = GETDATE(), LastLockEnded = GETDATE()</p>
<p>	&#8211; &#8216;SEED&#8217; the @TickStart<br />
	SET  @TickStart = GETDATE()</p>
<p>	&#8211; accumulate tables lock times<br />
	WHILE 1=1<br />
	BEGIN<br />
		TRUNCATE TABLE ##TCurr</p>
<p>		&#8211; get all currently active perm tables<br />
		SET @SQL = &#8216;INSERT ##TCurr<br />
				SELECT DISTINCT SO.Name<br />
				FROM sys.dm_tran_locks TL<br />
				JOIN &#8216; + @DBName + &#8216;..SYSOBJECTS SO ON SO.ID =TL.resource_associated_entity_id<br />
				WHERE request_session_id = &#8216; + CAST(@Process_id AS VARCHAR)<br />
		EXEC sp_executesql @SQL</p>
<p>		&#8211; get all currently active temp tables<br />
		IF @IncludeTempDB = &#8216;Y&#8217;<br />
		BEGIN<br />
			&#8211; TEMPDB is causing deadlocks and early termination<br />
			INSERT ##TCurr<br />
			SELECT DISTINCT SO.Name<br />
			FROM sys.dm_tran_locks TL<br />
			JOIN TEMPDB..SYSOBJECTS SO ON SO.ID =TL.resource_associated_entity_id<br />
			WHERE request_session_id = @Process_id<br />
		END</p>
<p>		&#8211; insert missing active permanent tables into ##TTot<br />
		INSERT ##TTot (TabName, LockStart)<br />
		SELECT DISTINCT TabName, LockStart = @TickStart<br />
		FROM ##TCurr<br />
		WHERE NOT EXISTS<br />
		(<br />
			SELECT 1 FROM ##TTot WHERE TabName = ##TCurr.TabName<br />
		)</p>
<p>		&#8211; save @CpuUsed<br />
		SELECT @CpuUsed = cpu<br />
		FROM master..sysprocesses<br />
		WHERE spid = @Process_id</p>
<p>		&#8211; wait for 1 second<br />
		WAITFOR DELAY @WaitFor_Delay</p>
<p>		&#8211; if @CpuUsed did not change, the processing stopped<br />
		IF @CpuUsed =<br />
		(<br />
			SELECT TOP 1 cpu<br />
			FROM master..sysprocesses<br />
			WHERE spid = @Process_id<br />
		)<br />
		BEGIN<br />
			&#8211; comes here if CPU did not change for the process<br />
			&#8211; need to count how many time in a row &#8216;CPU idle&#8217; happenes<br />
			&#8211; because in tests &#8216;CPU idle&#8217; was for mor than 3 seconds,<br />
			&#8211; but the monitored process was still running<br />
			SET @WaitsCntIdleCPU = @WaitsCntIdleCPU + 1<br />
			IF @WaitsCntIdleCPU &lt; @WaitsTotIdleCPU<br />
			BEGIN<br />
				&#8211; just keep counting &#039;CPU idle&#039; occurances<br />
				CONTINUE<br />
			END<br />
			ELSE<br />
			BEGIN<br />
				&#8211; process did not get CPU for long enough time,<br />
				&#8211; so assume it stopped<br />
				BREAK<br />
			END<br />
		END</p>
<p>		SET @WaitsCntIdleCPU = 0</p>
<p>		SET	@TickEnd = GETDATE()<br />
		SET	@Msec = DATEDIFF(ms, @TickStart, @TickEnd) / 1000.0<br />
		&#8211; keep @TickStart close to @TickEnd, so<br />
		&#8211; SUM will match the real difference between end time and start time<br />
		SET  @TickStart = GETDATE()</p>
<p>		&#8211; update CPU time<br />
		UPDATE ##TTot<br />
			SET TimeLocked = TimeLocked + @Msec,<br />
				LastLockEnded = GETDATE()<br />
			WHERE TabName = &#039;CPU&#039;</p>
<p>		&#8211; update ##TTot for tables that are currently active<br />
		UPDATE ##TTot<br />
			SET TimeLocked = TimeLocked + @Msec,<br />
				LastLockEnded = GETDATE()<br />
			WHERE EXISTS (SELECT 1 FROM ##TCurr WHERE TabName = ##TTot.TabName)</p>
<p>	END</p>
<p>	&#8211; update CPU time<br />
	UPDATE ##TTot<br />
		SET LastLockEnded = GETDATE()<br />
		WHERE TabName = &#039;CPU&#039;</p>
<p>	select * from ##TTot<br />
	order by TimeLocked DESC</p>
<p>	select * from ##TTot<br />
	order by LastLockEnded DESC<br />
END</p>
]]></content:encoded>
	</item>
</channel>
</rss>
