<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:georss="http://www.georss.org/georss" xmlns:media="http://search.yahoo.com/mrss/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>Simple SQL Server</title>
	<atom:link href="https://simplesqlserver.com/category/sql-server/feed/" rel="self" type="application/rss+xml"/>
	<link>https://simplesqlserver.com</link>
	<description>A basic approach to database administration</description>
	<lastBuildDate>Thu, 10 Apr 2025 02:25:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<site xmlns="com-wordpress:feed-additions:1">51941020</site><cloud domain="simplesqlserver.com" path="/?rsscloud=notify" port="80" protocol="http-post" registerProcedure=""/>
<image>
		<url>https://secure.gravatar.com/blavatar/acf8c83ed045b673ae4ec62f0fc7bc2fa0efd49ff75339eb9b19858bd648113f?s=96&amp;d=https%3A%2F%2Fs2.wp.com%2Fi%2Fwebclip.png</url>
		<title>SQL Server – Simple SQL Server</title>
		<link>https://simplesqlserver.com</link>
	</image>
	<atom:link href="https://simplesqlserver.com/osd.xml" rel="search" title="Simple SQL Server" type="application/opensearchdescription+xml"/>
	<atom:link href="https://simplesqlserver.com/?pushpress=hub" rel="hub"/>
	<item>
		<title>Query to Email….Well Formatted Email</title>
		<link>https://simplesqlserver.com/2025/04/09/query-to-email/</link>
					<comments>https://simplesqlserver.com/2025/04/09/query-to-email/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Thu, 10 Apr 2025 02:25:52 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=48278</guid>

					<description><![CDATA[When I get alerts from SQL Server I want it to do three things for me. Tell me what&#8217;s wrong, show me the data, then tell me how to take action on that data. Unfortunately, SQL doesn&#8217;t have a great way of showing the data unless you want to parse it into text by yourself [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">When I get alerts from SQL Server I want it to do three things for me.  Tell me what&#8217;s wrong, show me the data, then tell me how to take action on that data.  Unfortunately, SQL doesn&#8217;t have a great way of showing the data unless you want to parse it into text by yourself or click on an attachment to view a text document of query results.</p>



<h2>Start with the punchline</h2>



<p class="wp-block-paragraph">Here is an example email I have going out, with the subject &#8220;Backups are not current on &lt;servername&gt;&#8221;:</p>



<figure class="wp-block-image size-large"><a href="https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png"><img width="1024" height="217" data-attachment-id="48295" data-permalink="https://simplesqlserver.com/2025/04/09/query-to-email/backups-are-not-current/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png" data-orig-size="1063,226" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Backups Are Not Current" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=652" src="https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=1024" alt="" class="wp-image-48295" srcset="https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=1024 1024w, https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=150 150w, https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=300 300w, https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=768 768w, https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png 1063w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p class="wp-block-paragraph">I can quickly see that my full backups didn&#8217;t run last night for some reason, and since I don&#8217;t have a job failure email that preceded this I can guess that the job never ran.  On this server we used to do differential backups, but got away from doing them a couple years ago.  The reason for that is top secret, but what is no longer a secret is that these database aren&#8217;t new and don&#8217;t have a recent full or differential backup.</p>



<h2>How I got to that punchline:</h2>



<p class="wp-block-paragraph">First I found the Stack Overflow question <a href="https://stackoverflow.com/questions/7070053/convert-a-sql-query-result-table-to-an-html-table-for-email">Convert a SQL query result table to an HTML table for email</a>, which did the hard part I&#8217;ve never done before.  Getting that to flow well in an easy-to-use format wasn&#8217;t overly difficult, but a bit tedious.  First thing was to put the query to HTML into a proc:</p>



<pre style="font-family:consolas;font-size:12">
<span style="color:blue">CREATE PROC </span><span style="color:black">proc_Query_To_HTML
	@Query </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@OrderBy </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:black">1000</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@HTML </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) </span><span style="color:blue">OUTPUT
AS

</span><span style="color:green">/*
--USAGE: Two calls, one with the optional @OrderBy.  Uses a tiny temp table to make the results easy to read.
--WARNING: Query will be called in the context of the DBA database.  Use fully-qualified names such as master.sys.tables to get data from another database.

DECLARE @HTML nvarchar(max)

IF OBJECT_ID('tempdb..#x') IS NOT NULL DROP TABLE #x

CREATE TABLE #x (mynumba int)
INSERT INTO #x SELECT 1 UNION ALL SELECT 5

EXEC DBA.dbo.proc_Query_To_HTML
	@Query = 'SELECT * FROM #x'
	, @HTML = @HTML OUTPUT 

SELECT @HTML

EXEC DBA.dbo.proc_Query_To_HTML
	@Query = 'SELECT * FROM #x'
	, @OrderBy = 'ORDER BY mynumba DESC'
	, @HTML = @HTML OUTPUT 

SELECT @HTML
*/

</span><span style="color:blue">IF </span><span style="color:black">@OrderBy </span><span style="color:gray">IS NULL </span><span style="color:blue">BEGIN
	SET </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:red">''  
</span><span style="color:blue">END

SET </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:magenta">REPLACE</span><span style="color:gray">(</span><span style="color:black">@OrderBy</span><span style="color:gray">, </span><span style="color:red">''''</span><span style="color:gray">, </span><span style="color:red">''''''</span><span style="color:gray">);

</span><span style="color:blue">DECLARE </span><span style="color:black">@RealQuery </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">MAX</span><span style="color:gray">) = </span><span style="color:magenta">CAST</span><span style="color:gray">(</span><span style="color:red">'' </span><span style="color:blue">as nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)) + </span><span style="color:red">'
DECLARE @headerRow nvarchar(MAX);
DECLARE @cols nvarchar(MAX);  
DECLARE @Message varchar(1000) = '''' 

SELECT * INTO #dynSql FROM (' </span><span style="color:gray">+ </span><span style="color:black">@Query </span><span style="color:gray">+ </span><span style="color:red">') sub;

SELECT @cols = COALESCE(@cols + '', '''''''', '', '''') + ''['' + name + ''] AS ''''td''''''
FROM tempdb.sys.columns 
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;

SET @cols = ''SET @html = CAST(( SELECT '' + @cols + '' FROM #dynSql ' </span><span style="color:gray">+ </span><span style="color:black">@OrderBy </span><span style="color:gray">+ </span><span style="color:red">' FOR XML PATH(''''tr''''), ELEMENTS XSINIL) AS nvarchar(max))''    

EXEC sys.sp_executesql @cols, N''@html nvarchar(MAX) OUTPUT'', @html=@html OUTPUT

SELECT @headerRow = COALESCE(@headerRow + '''', '''') + ''&lt;th&gt;'' + name + ''&lt;/th&gt;'' 
FROM tempdb.sys.columns 
WHERE object_id = object_id(''tempdb..#dynSql'')
ORDER BY column_id;

SET @headerRow = ''&lt;tr&gt;'' + @headerRow + ''&lt;/tr&gt;'';

SET @html = ''&lt;table border="1"&gt;'' + @headerRow + @html + ''&lt;/table&gt;''; 

IF @Message IS NOT NULL BEGIN
	SELECT @html = @message + char(10) + char(10) + @html  
END 
'</span><span style="color:gray">;

</span><span style="color:blue">EXEC </span><span style="color:lime">sys</span><span style="color:gray">.</span><span style="color:maroon">sp_executesql </span><span style="color:black">@RealQuery</span><span style="color:gray">, </span><span style="color:red">N'@html nvarchar(MAX) OUTPUT'</span><span style="color:gray">, </span><span style="color:black">@html</span><span style="color:gray">=</span><span style="color:black">@HTML </span><span style="color:blue">OUTPUT
</span></pre>



<p class="wp-block-paragraph">Now I have something spitting out an HTML table, and I love it.  It&#8217;s a bit of a pain to use, but I&#8217;ll piece together an email alert when I really need it and I can make it look nice.  Ok, that was painful to say, so I made this proc that calls the previous one.</p>



<pre style="font-family:consolas;font-size:12">
<span style="color:blue">CREATE PROCEDURE </span><span style="color:black">proc_Query_To_Email 
	@Subject </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = </span><span style="color:red">'required'
	</span><span style="color:gray">, </span><span style="color:black">@Recipients </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = </span><span style="color:red">'required'
	</span><span style="color:gray">, </span><span style="color:black">@CopyRecipients </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@BlindCopyRecipients </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@From_Name </span><span style="color:blue">varchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@From_Address </span><span style="color:blue">varchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Reply_To </span><span style="color:blue">varchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Reply_To_Same_As_From </span><span style="color:blue">bit </span><span style="color:gray">= </span><span style="color:black">1
	</span><span style="color:gray">, </span><span style="color:black">@BodyIntro </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@BodyAfterQuery1 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@BodyAfterQuery2 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@BodyAfterQuery3 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@BodyAfterQuery4 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@BodyEnding </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Query1 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = </span><span style="color:red">'required'
	</span><span style="color:gray">, </span><span style="color:black">@OrderBy1 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Query2 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@OrderBy2 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Query3 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@OrderBy3 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Query4 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@OrderBy4 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@Query5 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@OrderBy5 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
	, </span><span style="color:black">@ShowOrigin </span><span style="color:blue">bit </span><span style="color:gray">= </span><span style="color:black">1
	</span><span style="color:gray">, </span><span style="color:black">@SendOnlyIfQuery1HasResults </span><span style="color:blue">bit </span><span style="color:gray">= </span><span style="color:black">1
	</span><span style="color:gray">, </span><span style="color:black">@SendOnlyIfQuery2HasResults </span><span style="color:blue">bit </span><span style="color:gray">= </span><span style="color:black">0
	</span><span style="color:gray">, </span><span style="color:black">@EmailProfile </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">) = NULL
</span><span style="color:blue">AS

IF </span><span style="color:black">@Subject </span><span style="color:gray">= </span><span style="color:red">'required' </span><span style="color:gray">OR </span><span style="color:black">@Recipients </span><span style="color:gray">= </span><span style="color:red">'required' </span><span style="color:gray">OR </span><span style="color:black">@Query1 </span><span style="color:gray">= </span><span style="color:red">'required' </span><span style="color:blue">BEGIN
PRINT </span><span style="color:red">'
***IMPORTANT*** The query will run in the scope of the database this proc is created in.  Use qualified names for tables giving the database name such as msdb.dbo.sysjobs.

Required parameters: 
	@Subject
	, @Recipients
	, @Query1

Ideal parameters:
	@Subject
	, @Recipients
	, @BodyIntro - Describe the result set
	, @Query1
	, @OrderBy - Sorted results are usually easier to read
	, @BodyEnding - Let them (or you) know how to react to these results.  If it''s infrequent then you''ll forget.

Basic Usage:
EXEC DBA..proc_Query_To_Email
	@Subject = ''SQL Server not rebooted recently''
	, @Recipients = ''steve.hood@notmydomain.com''
	, @BodyIntro = ''This server wasn''''t rebooted in the last 40 days:''
	, @Query1 = ''SELECT last_reboot = create_date FROM master.sys.databases where name = ''''tempdb'''' AND create_date &lt; GETDATE()-40'' 
	, @OrderBy1 = ''ORDER BY last_reboot'' 
	, @BodyEnding = ''All SQL Servers should be rebooted each month during monthly maintenance.  Please make sure this server is set up properly to do regular maintenance.''

The body of the email will consist of the following, and all are optional unless one of the @SendOnlyIf... variables is set to 1:
	@BodyIntro
	+ @Query1 results 
	+ @BodyAfterQuery1 
	+ @Query2 results 
	+ @BodyEnding
	+ What called this proc if @ShowOrigin = 1

All parts of the body will accept HTML tags.  &lt;br&gt;&lt;br&gt; will separate text into paragraphs.

All parameters - It''s overwhelming, but only @Subject, @Recipients, and @Query1 are required.  @BodyIntro and @BodyEnding are strongly recommended:
	@Subject - Email subject
	, @Recipients - Email recipients, semicolon delimited
	, @CopyRecipients - Email copy recipients, semicolon delimited
	, @BlindCopyRecipients - BCC recipients, semicolon delimited
	, @From_Name - Name of the person the email is coming from (defaults to the SQL Server''s name)  Only works if @From_Address is set.
	, @From_Address - Email address the email is coming from (defaults to the SQL Server profile''s email address)
	, @Reply_To - Who receives the email when you hit reply (defaults to From_Address, which defaults to the profile email address)
	, @Reply_To_Same_As_From - bit - If @Reply_To is left at null then it takes on the value of @From_Address (default is true)
	, @BodyIntro - First words on the email, usually introducing the query results
	, @BodyAfterQuery1 - Typically not used unless there are two queries.  Would introduce second query results
	, @BodyAfterQuery2 - Typically not used unless there are three queries.  Would introduce third query results
	, @BodyAfterQuery3 - Typically not used unless there are four queries.  Would introduce fourth query results
	, @BodyAfterQuery4 - Typically not used unless there are five queries.  Would introduce fifth query results
	, @BodyEnding - Text immediately after queries
	, @Query1 - Query, needs database explicitly set - msdb.dbo.sysjobs instead of sysjobs
	, @OrderBy1 - Query1''s order by clause has to be separate due to the HTML formatter - "ORDER BY name" 
	, @Query2 - Second query, typically not used.  Would be good to have query 1 show an issue and query 2 showing details
	, @OrderBy2 - Query2''s order by clause - "ORDER BY name"
	, @Query3 - Typically not used 
	, @OrderBy3 - Typically not used
	, @Query4 - Typically not used 
	, @OrderBy4 - Typically not used
	, @Query5 - Typically not used 
	, @OrderBy5 - Typically not used
	, @ShowOrigin - bit - End email by showing computer name that called this proc.  If it was called by a SQL Agent job then end the email with what job and job step, too (default is true)
	, @SendOnlyIfQuery1HasResults - bit - If query1 doesn''t return results or fails then don''t send the email (default is true)
	, @SendOnlyIfQuery2HasResults - bit - If query2 doesn''t return results or fails then don''t send the email (default is false)
	, @EmailProfile - If you need to use a non-default email profile then put the name here - Typically not used if there''s a default profile
'
	</span><span style="color:blue">RETURN</span><span style="color:gray">;
</span><span style="color:blue">END

DECLARE </span><span style="color:black">@JobName </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@Body </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@HTML1 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@HTML2 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@HTML3 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@HTML4 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)
	, </span><span style="color:black">@HTML5 </span><span style="color:blue">nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)

</span><span style="color:blue">IF </span><span style="color:black">@Query1 </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
	EXEC </span><span style="color:black">proc_Query_To_HTML 
		@Query </span><span style="color:gray">= </span><span style="color:black">@Query1
		</span><span style="color:gray">, </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:black">@OrderBy1 
		</span><span style="color:gray">, </span><span style="color:black">@HTML </span><span style="color:gray">= </span><span style="color:black">@HTML1 </span><span style="color:blue">OUTPUT
END

IF </span><span style="color:black">@Query2 </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
	EXEC </span><span style="color:black">proc_Query_To_HTML 
		@Query </span><span style="color:gray">= </span><span style="color:black">@Query2
		</span><span style="color:gray">, </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:black">@OrderBy2 
		</span><span style="color:gray">, </span><span style="color:black">@HTML </span><span style="color:gray">= </span><span style="color:black">@HTML2 </span><span style="color:blue">OUTPUT
END

IF </span><span style="color:black">@Query3 </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
	EXEC </span><span style="color:black">proc_Query_To_HTML 
		@Query </span><span style="color:gray">= </span><span style="color:black">@Query3
		</span><span style="color:gray">, </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:black">@OrderBy3 
		</span><span style="color:gray">, </span><span style="color:black">@HTML </span><span style="color:gray">= </span><span style="color:black">@HTML3 </span><span style="color:blue">OUTPUT
END

IF </span><span style="color:black">@Query4 </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
	EXEC </span><span style="color:black">proc_Query_To_HTML 
		@Query </span><span style="color:gray">= </span><span style="color:black">@Query4
		</span><span style="color:gray">, </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:black">@OrderBy4 
		</span><span style="color:gray">, </span><span style="color:black">@HTML </span><span style="color:gray">= </span><span style="color:black">@HTML4 </span><span style="color:blue">OUTPUT
END

IF </span><span style="color:black">@Query5 </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
	EXEC </span><span style="color:black">proc_Query_To_HTML 
		@Query </span><span style="color:gray">= </span><span style="color:black">@Query5
		</span><span style="color:gray">, </span><span style="color:black">@OrderBy </span><span style="color:gray">= </span><span style="color:black">@OrderBy5 
		</span><span style="color:gray">, </span><span style="color:black">@HTML </span><span style="color:gray">= </span><span style="color:black">@HTML5 </span><span style="color:blue">OUTPUT
END

IF </span><span style="color:black">@ShowOrigin </span><span style="color:gray">= </span><span style="color:black">1 </span><span style="color:blue">BEGIN
	SELECT </span><span style="color:black">@JobName </span><span style="color:gray">= </span><span style="color:blue">CASE </span><span style="color:gray">LEFT(</span><span style="color:black">es</span><span style="color:gray">.</span><span style="color:magenta">program_name</span><span style="color:gray">, </span><span style="color:black">29</span><span style="color:gray">)
					</span><span style="color:blue">WHEN </span><span style="color:red">'SQLAgent - TSQL JobStep (Job '
						</span><span style="color:blue">THEN </span><span style="color:red">'SQL Agent Job: ' </span><span style="color:gray">+ (</span><span style="color:blue">SELECT name FROM </span><span style="color:black">msdb</span><span style="color:gray">..</span><span style="color:black">sysjobs sj </span><span style="color:blue">WHERE </span><span style="color:magenta">substring</span><span style="color:gray">(</span><span style="color:black">es</span><span style="color:gray">.</span><span style="color:magenta">program_name</span><span style="color:gray">,</span><span style="color:black">32</span><span style="color:gray">,</span><span style="color:black">32</span><span style="color:gray">)=(</span><span style="color:magenta">substring</span><span style="color:gray">(</span><span style="color:lime">sys</span><span style="color:gray">.</span><span style="color:black">fn_varbintohexstr</span><span style="color:gray">(</span><span style="color:black">sj</span><span style="color:gray">.</span><span style="color:black">job_id</span><span style="color:gray">),</span><span style="color:black">3</span><span style="color:gray">,</span><span style="color:black">100</span><span style="color:gray">))) + </span><span style="color:red">' - ' </span><span style="color:gray">+ </span><span style="color:magenta">SUBSTRING</span><span style="color:gray">(</span><span style="color:black">es</span><span style="color:gray">.</span><span style="color:magenta">program_name</span><span style="color:gray">, </span><span style="color:black">67</span><span style="color:gray">, </span><span style="color:magenta">len</span><span style="color:gray">(</span><span style="color:black">es</span><span style="color:gray">.</span><span style="color:magenta">program_name</span><span style="color:gray">)-</span><span style="color:black">67</span><span style="color:gray">)
					</span><span style="color:blue">ELSE </span><span style="color:gray">NULL
					</span><span style="color:blue">END
	FROM </span><span style="color:lime">sys</span><span style="color:gray">.</span><span style="color:lime">dm_exec_sessions </span><span style="color:black">es
	</span><span style="color:blue">WHERE </span><span style="color:black">es</span><span style="color:gray">.</span><span style="color:magenta">session_id </span><span style="color:gray">= </span><span style="color:magenta">@@SPID
	
	</span><span style="color:blue">IF </span><span style="color:black">@JobName </span><span style="color:gray">IS NULL </span><span style="color:blue">BEGIN </span><span style="color:green">--It wasn't sent by SQL Agent, so find the computer that called this proc.
		</span><span style="color:blue">SELECT </span><span style="color:black">@JobName </span><span style="color:gray">= </span><span style="color:magenta">HOST_NAME</span><span style="color:gray">()
	</span><span style="color:blue">END 

	IF </span><span style="color:black">@JobName </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
		SELECT </span><span style="color:black">@BodyEnding </span><span style="color:gray">= </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:black">@BodyEnding</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) + </span><span style="color:red">'&lt;br&gt;&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:red">'This email was sent by ' </span><span style="color:gray">+ </span><span style="color:black">@JobName </span><span style="color:gray">+ </span><span style="color:red">' on ' </span><span style="color:gray">+ </span><span style="color:magenta">@@SERVERNAME
	</span><span style="color:blue">END
END

IF </span><span style="color:black">@From_Address </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
	IF </span><span style="color:black">@Reply_To </span><span style="color:gray">IS NULL AND </span><span style="color:black">@Reply_To_Same_As_From </span><span style="color:gray">= </span><span style="color:black">1 </span><span style="color:blue">BEGIN
		SELECT </span><span style="color:black">@Reply_To </span><span style="color:gray">= </span><span style="color:black">@From_Address
	</span><span style="color:blue">END

	IF </span><span style="color:black">@From_Name </span><span style="color:gray">IS NOT NULL </span><span style="color:blue">BEGIN
		SELECT </span><span style="color:black">@From_Address </span><span style="color:gray">= </span><span style="color:black">@From_Name </span><span style="color:gray">+ </span><span style="color:red">' &lt;' </span><span style="color:gray">+ </span><span style="color:black">@From_Address </span><span style="color:gray">+ </span><span style="color:red">'&gt;'
	</span><span style="color:blue">END
END

IF </span><span style="color:black">@EmailProfile </span><span style="color:gray">IS NULL AND </span><span style="color:black">0 </span><span style="color:gray">= (</span><span style="color:blue">SELECT </span><span style="color:magenta">COUNT</span><span style="color:gray">(</span><span style="color:black">1</span><span style="color:gray">) </span><span style="color:blue">FROM </span><span style="color:black">msdb</span><span style="color:gray">.</span><span style="color:black">dbo</span><span style="color:gray">.</span><span style="color:black">sysmail_principalprofile </span><span style="color:blue">WHERE </span><span style="color:black">is_default </span><span style="color:gray">= </span><span style="color:black">1</span><span style="color:gray">) </span><span style="color:blue">BEGIN
	PRINT </span><span style="color:red">'@EmailProfile is required because Database Mail does not have a default profile set up.'
</span><span style="color:blue">END ELSE IF </span><span style="color:gray">(</span><span style="color:black">@SendOnlyIfQuery1HasResults </span><span style="color:gray">= </span><span style="color:black">1 </span><span style="color:gray">AND </span><span style="color:black">@HTML1 </span><span style="color:gray">IS NULL) OR (</span><span style="color:black">@SendOnlyIfQuery2HasResults </span><span style="color:gray">= </span><span style="color:black">1 </span><span style="color:gray">AND </span><span style="color:black">@HTML2 </span><span style="color:gray">IS NULL) </span><span style="color:blue">BEGIN
	PRINT </span><span style="color:red">'There are no results to send.'
</span><span style="color:blue">END ELSE BEGIN
	SELECT </span><span style="color:black">@Body </span><span style="color:gray">= </span><span style="color:magenta">CAST</span><span style="color:gray">(</span><span style="color:red">'' </span><span style="color:blue">as nvarchar</span><span style="color:gray">(</span><span style="color:magenta">max</span><span style="color:gray">)) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:black">@BodyIntro </span><span style="color:gray">+ </span><span style="color:red">'&lt;br&gt;'</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:black">@HTML1</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@BodyAfterQuery1</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@HTML2</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@BodyAfterQuery2</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@HTML3</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@BodyAfterQuery3</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@HTML4</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@BodyAfterQuery4</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@HTML5</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">) 
		+ </span><span style="color:magenta">ISNULL</span><span style="color:gray">(</span><span style="color:red">'&lt;br&gt;' </span><span style="color:gray">+ </span><span style="color:black">@BodyEnding</span><span style="color:gray">, </span><span style="color:red">''</span><span style="color:gray">)

	</span><span style="color:blue">EXEC </span><span style="color:black">msdb</span><span style="color:gray">..</span><span style="color:maroon">sp_send_dbmail 
		</span><span style="color:black">@recipients </span><span style="color:gray">= </span><span style="color:black">@Recipients
		</span><span style="color:gray">, </span><span style="color:black">@copy_recipients </span><span style="color:gray">= </span><span style="color:black">@CopyRecipients
		</span><span style="color:gray">, </span><span style="color:black">@blind_copy_recipients </span><span style="color:gray">= </span><span style="color:black">@BlindCopyRecipients
		</span><span style="color:gray">, </span><span style="color:black">@from_address </span><span style="color:gray">= </span><span style="color:black">@From_Address 
		</span><span style="color:gray">, </span><span style="color:black">@reply_to </span><span style="color:gray">= </span><span style="color:black">@Reply_To 
		</span><span style="color:gray">, </span><span style="color:black">@subject </span><span style="color:gray">= </span><span style="color:black">@Subject
		</span><span style="color:gray">, </span><span style="color:black">@body </span><span style="color:gray">= </span><span style="color:black">@Body
		</span><span style="color:gray">, </span><span style="color:black">@body_format </span><span style="color:gray">= </span><span style="color:red">'HTML'
		</span><span style="color:gray">, </span><span style="color:black">@profile_name </span><span style="color:gray">= </span><span style="color:black">@EmailProfile 
</span><span style="color:blue">END
</span></pre>



<h2>How do I use this thing?</h2>



<p class="wp-block-paragraph">Ok, so I know this looks big and scary with lots of parameters.  The three of them that are required to run it are only @Subject, @Recipients, and @Query1.  The six I consider required to make something great also include @OrderBy1 (i.e. make it easy to read the query results), @BodyIntro (i.e. explain why they&#8217;re getting the email and what the query results are before they see them), and @BodyEnding (i.e. explain what action you&#8217;re hoping for the user to take). </p>



<p class="wp-block-paragraph">A couple odd things, because everything about me has some odd things: </p>



<ul class="wp-block-list">
<li>By default, if @Query1 returns no results it prints &#8220;There were no results to send.&#8221; and no email goes out.</li>



<li>@Query1 (and friends) can take normal #temp tables, you do not need ##global temp tables </li>



<li>@OrderBy1 needs to start with &#8220;ORDER BY &#8221; and uses the alias used in the select statement
<ul class="wp-block-list">
<li>Notice my example for basic usage has &#8220;ORDER BY last_reboot&#8221; when the column in the table is &#8220;create_date&#8221;</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">Now, the first one of those odd things is my favorite odd thing ever.  I&#8217;ll do my best to write an efficient query that only returns results when something bad happens, then I&#8217;ll create a SQL Agent job that runs somewhat frequently, and all it contains is a simple call to this proc.  My basic usage example is perfect as an overly generic example I might have run every Friday morning where if TempDB is over 40 days old I&#8217;ll get an email, otherwise I&#8217;ll see the results on my job simply say &#8220;There were no results to send.&#8221;, and I won&#8217;t get an email.  </p>



<pre style="font-family:consolas;font-size:12"><span style="color:blue">EXEC </span><span style="color:black">DBA</span><span style="color:gray">..</span><span style="color:black">proc_Query_To_Email
	@Subject </span><span style="color:gray">= </span><span style="color:red">'SQL Server not rebooted recently'
	</span><span style="color:gray">, </span><span style="color:black">@Recipients </span><span style="color:gray">= </span><span style="color:red">'steve.hood@notmydomain.com'
	</span><span style="color:gray">, </span><span style="color:black">@BodyIntro </span><span style="color:gray">= </span><span style="color:red">'This server wasn''t rebooted in the last 40 days:'
	</span><span style="color:gray">, </span><span style="color:black">@Query1 </span><span style="color:gray">= </span><span style="color:red">'SELECT last_reboot = create_date FROM master.sys.databases where name = ''tempdb'' AND create_date &lt; GETDATE()-40' 
	</span><span style="color:gray">, </span><span style="color:black">@OrderBy1 </span><span style="color:gray">= </span><span style="color:red">'ORDER BY last_reboot' 
	</span><span style="color:gray">, </span><span style="color:black">@BodyEnding </span><span style="color:gray">= </span><span style="color:red">'All SQL Servers should be rebooted each month during monthly maintenance.  Please make sure this server is set up properly to do regular maintenance.'
</span></pre>



<p class="wp-block-paragraph">Now you have more complex needs than that, and so do I.  I have processing queues that should never be more than 30 minutes behind, so I&#8217;ll pass in a query to give me the oldest 10 rows that are over 30 minutes old.  Typically doesn&#8217;t have any results and I don&#8217;t get an email, but when it does it doesn&#8217;t flood my email while showing me what the oldest rows are.  Then I might use @Query2 to give me a total count.</p>



<p class="wp-block-paragraph">@BodyEnding is the big one for me.  It&#8217;s not required to call this, but it&#8217;s practically required to be useful.  In the last example I&#8217;m telling you the processing queue is over 30 minutes behind, and telling you I don&#8217;t think that&#8217;s cool.  @BodyEnding is where I throw out instructions on how to fix it or at least how to start troubleshooting the issue.  Even if this email is only going to me, I write this part like I&#8217;m talking to an intern, because I might be working on this at 3 AM where I&#8217;m slower than an intern or someone else might be handling this when I&#8217;m trying to take a day off.  </p>



<p class="wp-block-paragraph">If someone else asks me to make an alert and doesn&#8217;t give me instructions on how to fix it, @BodyEnding gets to be as sarcastic as I&#8217;m feeling that day.  At one point an alert ended with &#8220;We don&#8217;t know what to do.  Panic!!!&#8221;.  This helped get someone else who was receiving the alerts to give me a small checklist to go through, and now that&#8217;s in @BodyEnding preceded by &#8220;Don&#8217;t panic, we know what to do:&#8221;.</p>



<p class="wp-block-paragraph">An annoyed thinker may point out that I never talked about my screenshot again.  What query was that, and how&#8217;d you do it?  Well, this post has got to be around 2,238 words long, and I thought going into the weeds on that was going too far. </p>



<p class="wp-block-paragraph">A quick thinker may realize that my screenshot of a real error message above didn&#8217;t have anything for @BodyEnding, and that&#8217;s because I&#8217;m not consistent.  Anyways, I hope you enjoyed my first post in about a decade.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2025/04/09/query-to-email/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">48278</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2025/04/backups-are-not-current.png?w=1024"/>
	</item>
		<item>
		<title>Powershell, when were my servers rebooted?</title>
		<link>https://simplesqlserver.com/2016/06/01/powershell-when-were-my-servers-rebooted/</link>
					<comments>https://simplesqlserver.com/2016/06/01/powershell-when-were-my-servers-rebooted/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Wed, 01 Jun 2016 12:00:06 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=43252</guid>

					<description><![CDATA[One of my servers wasn&#8217;t rebooted when it should have been and I never realized it until after the outage was over.  The big deal wasn&#8217;t that it didn&#8217;t reboot, it was that I never realized it.  These are the things that bug me, so I used it as an excuse to learn a bit [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>One of my servers wasn&#8217;t rebooted when it should have been and I never realized it until after the outage was over.  The big deal wasn&#8217;t that it didn&#8217;t reboot, it was that I never realized it.  These are the things that bug me, so I used it as an excuse to learn a bit of PowerShell.</p>
<p>A little backstory to give the details a better chance of making sense to everyone&#8230;  Instead of having a couple overworked sys admins doing windows updates for every server, we each take care of our own with a sys admin checking that all updates were applied.  It&#8217;s the first I&#8217;ve heard of it being done this way, but I like it.</p>
<p>To keep things consistent, the database team has a policy of rebooting all database servers regardless of WSUS requiring it.  One month most of the servers required a reboot and bounced automatically, except for one that didn&#8217;t require a reboot.  After the first 10 or 15 rebooted, I got lazy and started just verifying the servers were up, SQL was responding, all updates were applied, and there was no pending reboot.</p>
<p>It wasn&#8217;t until I was reading the logs the next business day I realized that that server was missing all of the chatter from SQL Services starting up.  Like I said, the biggest issue was me being frustrated that it snuck by me, and I didn&#8217;t want that to happen again.  I wanted my solution to be independent of SQL Server because not all of the computers I do updates on have a SQL engine.</p>
<p>Wanting an excuse to learn PowerShell better, I talked to our Exchange guy (they know their PowerShell) and got a basic script to determine a computer&#8217;s boot time given its name.  I expanded that to be an entire AD group, then a list of AD groups, and finally using wildcards to find AD groups.  Wildcards work best for me because I&#8217;m reorganizing my SUS groups and all of mine have &#8220;SQL&#8221; in the groups name, even for the app servers I&#8217;m responsible for.</p>
<p>That resulted in the following code.  Now I&#8217;m absolutely certain that a PowerShell or AD guru can poke a ton of holes in my logic, but that&#8217;s what we have gurus for!  To be fair, I&#8217;d do the same to their T-SQL.  If any of those gurus are out there reading this DBA&#8217;s blog, the comment section below is how you fix my code and stop me from spreading bad code like a virus.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">Import-Module </span><span style="color:#8a2be2;">ActiveDirectory

</span><span style="color:#006400;">#either method works for getting a list of groups.  You can type in all of your groups or make a query to find them all.  I prefer the query if possible.

#$groups = "Test SQL Servers", "Prod SQL Servers 1", "Prod SQL Servers 2", "SQL Cluster Servers 1", "SQL Cluster Servers 2"

</span><span style="color:#ff4500;">$groups </span><span style="color:#a9a9a9;">= </span><span style="color:blue;">Get-ADGroup </span><span style="color:navy;">-Filter </span>{<span style="color:blue;">name </span><span style="color:navy;">-like </span><span style="color:darkred;">"*SQL *"</span>} <span style="color:#a9a9a9;">| </span><span style="color:blue;">where-object </span>{<span style="color:#ff4500;">$_</span><span style="color:#a9a9a9;">.</span>distinguishedname <span style="color:#a9a9a9;">-like </span><span style="color:darkred;">"*OU=SUS Group*"</span>}

<span style="color:#00008b;">ForEach </span>(<span style="color:#ff4500;">$group </span><span style="color:#00008b;">in </span><span style="color:#ff4500;">$groups</span>) {
    <span style="color:#ff4500;">$computerlist </span><span style="color:#a9a9a9;">= </span><span style="color:blue;">Get-ADGroupMember </span><span style="color:#ff4500;">$group </span><span style="color:navy;">-Recursive </span><span style="color:#a9a9a9;">| </span><span style="color:blue;">SELECT </span><span style="color:#8a2be2;">name 

    </span><span style="color:#006400;">#If there were any computers in the list, do this.  It skips empty groups.
    </span><span style="color:#00008b;">if </span>(<span style="color:#ff4500;">$computerlist</span>) {
        <span style="color:#ff4500;">$computerlistcount </span><span style="color:#a9a9a9;">= </span><span style="color:#ff4500;">$computerlist</span><span style="color:#a9a9a9;">.</span>Count

        <span style="color:#006400;">#It returns an object instead of an array if there was only one, so count would be null
        </span><span style="color:#00008b;">if </span>(<span style="color:#a9a9a9;">!</span><span style="color:#ff4500;">$computerlistcount</span>) {
            <span style="color:#ff4500;">$computerlistcount </span><span style="color:#a9a9a9;">= </span><span style="color:purple;">1
        </span>}

        <span style="color:blue;">Write-Host </span><span style="color:darkred;">""
        </span><span style="color:blue;">Write-Host </span><span style="color:darkred;">"</span><span style="color:#ff4500;">$computerlistcount </span><span style="color:darkred;">server(s) in" </span><span style="color:#ff4500;">$group</span><span style="color:#a9a9a9;">.</span>name
    }

    <span style="color:#00008b;">ForEach </span>(<span style="color:#ff4500;">$computer </span><span style="color:#00008b;">in </span><span style="color:#ff4500;">$computerlist</span>) {
        <span style="color:#00008b;">try </span>{
            <span style="color:blue;">Get-WmiObject </span><span style="color:#8a2be2;">win32_operatingsystem </span><span style="color:navy;">-ComputerName </span><span style="color:#ff4500;">$computer</span><span style="color:#a9a9a9;">.</span>name <span style="color:navy;">-ErrorAction </span><span style="color:#8a2be2;">Stop </span><span style="color:#a9a9a9;">| </span><span style="color:blue;">select </span><span style="color:#8a2be2;">csname</span><span style="color:#a9a9a9;">, </span>@{LABEL<span style="color:#a9a9a9;">=</span><span style="color:darkred;">'LastBootUpTime'</span>;EXPRESSION<span style="color:#a9a9a9;">=</span>{<span style="color:#ff4500;">$_</span><span style="color:#a9a9a9;">.</span>ConverttoDateTime(<span style="color:#ff4500;">$_</span><span style="color:#a9a9a9;">.</span>lastbootuptime)}}
        }
        <span style="color:#00008b;">catch </span>{
            <span style="color:#006400;">#This logic is lacking.  I don't care what error you got, just say you couldn't connect and we'll move on.
            #For this script, it probably means the server is in the middle of a reboot
            </span><span style="color:blue;">Write-Host </span><span style="color:darkred;">"Could not connect to" </span><span style="color:#ff4500;">$computer</span><span style="color:#a9a9a9;">.</span>name
            <span style="color:#00008b;">Continue
        </span>}
    }
}
 
</pre>
<p>There are relatively equivalent things you can do in SQL Server, but that&#8217;s assuming you&#8217;re running a SQL Server engine on every server you care about.  Even from the viewpoint of a DBA running the SUS updates on my own servers, this doesn&#8217;t hold up.  I still care about my monitoring application server and several others.</p>
<p>That being said, here&#8217;s what SQL can do for you:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span>create_date <span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases </span><span style="color:blue;">WHERE </span>database_id <span style="color:gray;">= </span>2

<span style="color:blue;">exec </span><span style="color:maroon;">xp_cmdshell </span><span style="color:red;">'wmic os get lastbootuptime'
</span></pre>
<p>There&#8217;s also some more bad news with these beyond requiring the SQL Server engine.</p>
<p>The create date of TempDB is useful, but it&#8217;s created on startup of SQL Services, not the computer.  This means if you restart SQL Services then you get a new date, even if you specifically state that you want to restart every computer during your SUS outage whether it&#8217;s required or not.</p>
<p>The WMIC call gets around that and tells you when the server came up, but it&#8217;s relying on xp_cmdshell.  This extended procedure has a bad rep for being a security loophole, even though by default to run the thing you need enough permissions to be able to turn it on.  Because of that, it&#8217;s turned off in many environments and flipping it on for a second can be frowned upon.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/06/01/powershell-when-were-my-servers-rebooted/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">43252</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>File Growths</title>
		<link>https://simplesqlserver.com/2016/03/29/file-growths/</link>
					<comments>https://simplesqlserver.com/2016/03/29/file-growths/#respond</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Tue, 29 Mar 2016 11:00:49 +0000</pubDate>
				<category><![CDATA[Entry Level]]></category>
		<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Instant File Initialization]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=42246</guid>

					<description><![CDATA[I hinted at file growths in my previous posts about shrinking data and log files.  Then I talked about growing log files in my post Database Log VLFs.  However, there are still some unanswered questions about growing data files. Should I use autogrowth?  What should I set it to grow by? Why do manual growths, [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I hinted at file growths in my previous posts about shrinking data and log files.  Then I talked about growing log files in my post Database Log VLFs.  However, there are still some unanswered questions about growing data files.</p>
<p>Should I use autogrowth?  What should I set it to grow by?</p>
<p>Why do manual growths, and how do I know how much to grow by or when a growth is needed?</p>
<p>Can I and should I use Instant File Initialization?  If I can and should, how do I even know if it&#8217;s turned on?</p>
<h2>Leave Autogrowth On?</h2>
<p>Yes, absolutely.  This isn&#8217;t a question.</p>
<p>This could be worded as &#8220;Do you want a slight pause or do you want your application to fail?&#8221;  The answer is a little more obvious when you word it that way.  The only time I&#8217;ve seen an appropriate answer of &#8220;No&#8221; is when you have multiple data files in a filegroup and only want one to grow.</p>
<p>To answer how long this pause will be you have to ask these two questions.  Are you able to use Instant File Initialization?  How much are you growing by if you can&#8217;t use Instant File Initialization?  I&#8217;ll discuss Instant File Initialization later in this post, but it&#8217;s basically the difference between &#8220;this space is yours immediately&#8221; instead of writing zeros SQL Server doesn&#8217;t care about to be paranoid about security.</p>
<p>There are competing factors as to how much you want to autogrowth to be.  The only real consensus is that the defaults are horrible.  You need to balance out how long of a pause it will take for a growth, how long of a pause your users will tolerate, how fast is your database growing, how much fragmentation smaller growths will cause, and how much fragmentation you will tolerate.</p>
<p>By default, data files grow at 1 MB and log files grow at 10%.  I covered log file growths rather well in my post <a href="https://simplesqlserver.com/2016/02/08/database-log-vlfs/" target="_blank">Database Log VLFs</a>, so we&#8217;ll focus on the data files here.  Think about it, if you have a 100 GB database that got there by the defaults, it got there in 102,400 pieces.  Not good.</p>
<p>If the defaults are horrible, what&#8217;s good?  Tiny is bad, huge is bad, and percentage-based growth just lets you automatically drift from tiny to huge.  Do your balancing act I talked about above.  If growing by 64 MB will keep you good for several weeks, set it to 64 MB.  If 1 GB goes by rather quickly, you can&#8217;t use Instant File Initialization, and the users can tolerate the pause it takes to grow by 1 GB, then this might be the best growth rate for you.  No single answer is right, but at least you have a better idea of what the variables are.</p>
<p>There are also cases where there are no good answers.  Say you have an app wouldn&#8217;t be uncommon to grow by a couple GB a day, you can&#8217;t use Instant File Initialization, and pauses of more than a second wouldn&#8217;t go over very well.  You don&#8217;t want the fragmentation of setting the growth to 64 MB, but you don&#8217;t want the pause of setting it to 1 GB.  Maybe you&#8217;ll settle for 256 MB, but you&#8217;re not happy about it.  Manual growths are the real answer, but you want a safety net that won&#8217;t kill you with pauses or fragmentation.  Not everyone is in that situation, but some of us are.</p>
<p>If I&#8217;m going to suggest leaving Autogrowth on and suggest that you be aware of the size of these growths, then the least I can do is give you a script for that.  This will tell you the size of every database file and the autogrowth rate.  I&#8217;ll often use it to tweak the growth rates, so I script that change out in the results as well.  If you read the post <a href="https://simplesqlserver.com/2016/02/08/database-log-vlfs/" target="_blank">Database Log VLFs</a> then it will make more sense why I use 1,000 MB here instead of 1,024 MB; it wasn&#8217;t a typo.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DECLARE </span>@FilterByDB <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>100<span style="color:gray;">)
	, </span>@Cmd <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>4000<span style="color:gray;">)

</span><span style="color:blue;">SELECT </span>@FilterByDB <span style="color:gray;">= </span><span style="color:red;">''
</span><span style="color:green;">--SELECT @FilterByDB = 'tempdb'

</span><span style="color:blue;">IF </span><span style="color:magenta;">Object_ID</span><span style="color:gray;">(</span><span style="color:red;">'TempDB..##FileSize'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>##FileSize
<span style="color:blue;">END

CREATE TABLE </span>##FileSize
<span style="color:gray;">(	  </span>DB <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>FileLogicalName <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>FilePhysicalName <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>512<span style="color:gray;">)
	, </span><span style="color:blue;">FileGroup VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>Used_MB <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>Free_MB <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>Size_MB <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>GrowthRate <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
	, </span>MaxSize <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>128<span style="color:gray;">)
)


</span><span style="color:blue;">SELECT </span>@Cmd <span style="color:gray;">=  </span><span style="color:red;">N'use [?]; 

IF ''?'' like ''' </span><span style="color:gray;">+ </span>@FilterByDB <span style="color:gray;">+ </span><span style="color:red;">'%'' OR ''' </span><span style="color:gray;">+ </span>@FilterByDB <span style="color:gray;">+ </span><span style="color:red;">''' = '''' BEGIN 
INSERT INTO ##FileSize
SELECT DB = db_name()
	, FileLogicalName = f.name
	, FilePhysicalName = f.physical_name
	, FileGroup = ISNULL(g.name, f.Type_Desc) 
	, Used_MB = REPLACE(CONVERT(varchar(100), (CAST((FileProperty(replace(f.name, '''', ''''''''), ''SpaceUsed'')) / 128 AS money)), 1), ''.00'', '''')
	, Free_MB = REPLACE(CONVERT(varchar(100), (CAST((f.size - FileProperty(f.name, ''SpaceUsed'')) / 128 AS money)), 1), ''.00'', '''')
	, Size_MB = REPLACE(CONVERT(varchar(100), (CAST((f.size) / 128 AS money)), 1), ''.00'', '''')
	, GrowthRate = Case Is_Percent_Growth
					WHEN 1 THEN '''' + Cast(Growth as VarChar(100)) + ''%''
					ELSE REPLACE(CONVERT(varchar(100), (CAST(Growth/128 AS money)), 1), ''.00'', '''') + '' MB''
					END 
	, MaxSize = Case Max_Size
					WHEN -1 THEN ''---''
					WHEN 268435456 THEN ''---''
					ELSE REPLACE(CONVERT(varchar(100), (CAST(Max_Size/128 AS money)), 1), ''.00'', '''') + '' MB''
					END
FROM sys.database_files f
	LEFT JOIN sys.filegroups g on f.data_space_id = g.data_space_id

END
' 
</span><span style="color:blue;">IF </span>@FilterByDB <span style="color:gray;">&lt;&gt; </span><span style="color:red;">'' </span><span style="color:gray;">AND </span>100 <span style="color:gray;">&lt; (</span><span style="color:blue;">SELECT </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">) </span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases</span><span style="color:gray;">) </span><span style="color:blue;">BEGIN
	SELECT </span>@Cmd <span style="color:gray;">= </span><span style="color:magenta;">REPLACE</span><span style="color:gray;">(</span>@Cmd<span style="color:gray;">, </span><span style="color:red;">'?'</span><span style="color:gray;">, </span>@FilterByDB<span style="color:gray;">)

	</span><span style="color:blue;">EXEC </span><span style="color:gray;">(</span>@Cmd<span style="color:gray;">)
</span><span style="color:blue;">END ELSE BEGIN
	exec </span><span style="color:maroon;">sp_MSforeachdb </span>@Cmd 
<span style="color:blue;">END

SELECT </span><span style="color:gray;">*
	, </span>PctUsed <span style="color:gray;">= </span><span style="color:magenta;">Cast</span><span style="color:gray;">(</span>100 <span style="color:gray;">* </span><span style="color:magenta;">replace</span><span style="color:gray;">(</span>Used_MB<span style="color:gray;">, </span><span style="color:red;">','</span><span style="color:gray;">, </span><span style="color:red;">''</span><span style="color:gray;">) / (</span><span style="color:magenta;">Cast</span><span style="color:gray;">(</span><span style="color:magenta;">replace</span><span style="color:gray;">(</span>Size_MB<span style="color:gray;">,</span><span style="color:red;">','</span><span style="color:gray;">, </span><span style="color:red;">''</span><span style="color:gray;">) </span><span style="color:blue;">as Dec</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>2<span style="color:gray;">)) + </span>.01<span style="color:gray;">) </span><span style="color:blue;">as Dec</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>2<span style="color:gray;">))
	, </span>SetGrowth64MB <span style="color:gray;">= </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>db <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>FileLogicalName <span style="color:gray;">+ </span><span style="color:red;">''', /*Size = MB,*/ FILEGROWTH = 64MB )'
	</span><span style="color:gray;">, </span>SetGrowthTo256MB <span style="color:gray;">= </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>db <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>FileLogicalName <span style="color:gray;">+ </span><span style="color:red;">''', /*Size = MB,*/ FILEGROWTH = 256MB )'
	</span><span style="color:gray;">, </span>SetGrowthTo1000MB <span style="color:gray;">= </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>db <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>FileLogicalName <span style="color:gray;">+ </span><span style="color:red;">''', /*Size = MB,*/ FILEGROWTH = 1000MB )'
</span><span style="color:blue;">FROM </span>##FileSize
<span style="color:blue;">ORDER BY </span>1<span style="color:gray;">, </span>4<span style="color:gray;">, </span><span style="color:green;">/*cast(replace(size_mb, ',', '') as int) DESC,*/ </span>FilePhysicalName

<span style="color:blue;">DROP TABLE </span>##FileSize
</pre>
<h2>Manual Growths</h2>
<p>If you want to be extreme about it, autogrowth is like asking someone about wearing a seatbelt.  Do you want it to hurt a little or a lot if you &#8220;crash&#8221; by running out of allocated space?  Well, you want it to hurt&#8230;wait&#8230;why do I have to crash?</p>
<p>This may seem over-the-top, but some of the applications may have databases like the last one I talked about in the autogrowth section.  Typically we&#8217;d prefer not to have autogrowth handling everything to avoid the pause while it grows, but there are cases like that where it&#8217;s more than a slight preference to avoid the pause.</p>
<p>So the autogrowths are our seatbelts we&#8217;re putting on just in case while catching everything with manual growths is our real goal of avoiding that &#8220;crash&#8221;.  However, how are you supposed to know when to grow a file or how much to grow it?  Seeing how large your files are now doesn&#8217;t tell you how much they&#8217;ll grow in the next couple months, they could be static or growing exponentially.</p>
<p>The partial answer is to say how much data was used before, how much data is being used now, and predict how much data will be used in the future using that trend.  A better answer is to say what the peak usage was over a week and compare that to past weeks, which would account for things that cause spikes in file usage such as index rebuilds or data loads.</p>
<p>If you have monitoring software, it may have that answer just waiting for you to query it.  That&#8217;s the situation I find myself in right now, and I wrote <a href="https://simplesqlserver.com/2016/03/21/database-growth-trends-idera-diagnostic-manager" target="_blank">Database Growth Trends &#8211; Idera Diagnostic Manager</a> talking about how I handle it right now.  However, I used to work with monitoring software that pretty much only told me OS-level info, so we watched this data manually as was discussed in my post <a href="https://simplesqlserver.com/2013/09/10/monitoring-database-and-table-sizes/" target="_blank">Monitoring Database and Table Sizes</a>.</p>
<p>To be clear, many databases are fine with autogrowth handling everything.  Do what&#8217;s best for you for each individual database.  I personally have a mix of databases where some I&#8217;d be upset if autogrowth kicked in because it would cause timeouts that the app handles extremely poorly, and others, such as my DBA database, where a pause would only affect my collection jobs or a query I&#8217;m personally running.  Autogrowth is not inherently evil, so only be paranoid where paranoia is called for.</p>
<h2>Performing Manual Growths</h2>
<p>Manually growing the file is just setting a new initial size.  To do that in SSMS, right-click on a database, properties, files, then change the initial size to how large you want it to be.</p>
<p>I&#8217;m not a big fan of having SSMS do things for me, so I&#8217;ll use &#8220;Script Action to New Query Window&#8221; and run it form there.  This will give me a script like this:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">USE </span>[master]
<span style="color:blue;">GO
ALTER DATABASE </span>[DBA] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'DBA_Data'</span><span style="color:gray;">, </span>SIZE <span style="color:gray;">= </span>256000KB <span style="color:gray;">)
</span><span style="color:blue;">GO
</span></pre>
<p>One thing I wish I knew about years before I did is that the size in KB also accepts sizes in MB.  I write just about everything in MB, so it&#8217;s more natural for me to write that script with Size = 250MB instead.  I know I&#8217;m being petty, but I&#8217;m petty and comfortable.</p>
<p>This script could take a couple milliseconds or several seconds, even for 250MB.  It all depends on if you have Instant File Initialization turned on and if you&#8217;re able to use it for this growth.</p>
<h2>Instant File Initialization &#8211; What is it?</h2>
<p>Short answer, this makes growths exponentially quicker by opening an often-acceptable small security hole.</p>
<p>Of course, I always have a long answer&#8230;</p>
<p>By default, all space used by SQL Server is zeroed out before control is handed over.  This makes sure that it&#8217;s clean space for two basic reasons.  First, the database requires that space to be clean because of how it plans to use it.  Second, we don&#8217;t want to risk the possibility of special commands being able to pull back unencrypted sensitive data that used to be there.</p>
<p>Now the first reason we don&#8217;t have much control or say over at this point in the conversation.  If you&#8217;re growing a log file then it needs to be zeroed out, and SQL Server will zero out each <a href="https://simplesqlserver.com/2016/02/08/database-log-vlfs/" target="_blank">VLF</a> when it&#8217;s marked for reuse, too.  For data files, if you&#8217;re using Transparent Data Encryption (TDE) then the way SQL Server stores the data also requires the space to be zeroed out.  These things are outside of the conversation on file growths, it&#8217;s just what we&#8217;ll have to deal with at this point and SQL Server won&#8217;t use IFI even if it&#8217;s turned on.</p>
<p>The one thing that will be pulled into this discussion is the security aspect of using uninitialized space.  Kimberly Tripp demonstrates this the best in her post <a href="http://www.sqlskills.com/blogs/kimberly/instant-initialization-what-why-and-how/" target="_blank">Instant Initialization &#8211; Why, Why, and How?</a> in the &#8220;Why isn&#8217;t this on by default?&#8221; section.</p>
<p>I&#8217;ll try to make it short and sweet if you don&#8217;t want to read yet another post (I recommend you read it). You can ask SQL Server to read specific pages off the disk.  If SQL Server never actually used that page then it contains what was there when it was given to SQL Server.  Zero initialized disks are very boring info, just &#8220;0000000000&#8230;.&#8221;.  Not much fun.</p>
<p>Uninitialized space is more fun (depending on your meaning of the word) and could contain old data.  If you had unencrypted sensitive data sitting there before, it could contain enough to steal someone&#8217;s identity.  You never know.  These commands will only work if you have the permissions to run them, but who has those permissions and how much do you trust them?  Most organizations say it&#8217;s fine, but some won&#8217;t.</p>
<h2>Instant File Initialization &#8211; How do I use it?</h2>
<p>This is an odd feature that you can&#8217;t just see in a table in SQL Server.  The easiest way is to run an xp_cmdshell command and look for it, but you can optionally turn on trace flags, grow or create a database, turn off the trace flags, then look in your error log.</p>
<p>Paul Randal gets into both methods in his post <a href="http://www.sqlskills.com/blogs/paul/how-to-tell-if-you-have-instant-initialization-enabled/" target="_blank">How to tell if you have instant file initialization enabled?</a>.  I don&#8217;t have any issues with xp_cmdshell, so I&#8217;ll let you go to Paul&#8217;s blog if your company insists on the more difficult method.</p>
<p>As for me, I&#8217;ll run the query below as a multiserver query in SSMS and look at the results.  If that sounds foreign to you, check out my <a href="https://simplesqlserver.com/2016/03/14/multiserver-queries" target="_blank">Multiserver Queries</a> post, it&#8217;s a very simple and useful technique for specific tasks such as this one.  In fact, I wrote that post so I could link to it in this post.</p>
<p>However, even if IFI is turned on, it&#8217;s only going to work for data files that aren&#8217;t encrypted with TDE.  Logs and TDE data files need the zeros.  Also, if any database on your instance is using TDE then so is TempDB, which means its data files can&#8217;t use IFI either.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">CREATE TABLE </span>#Output
<span style="color:gray;">(
	</span>DataOut <span style="color:blue;">VarChar</span><span style="color:gray;">(</span>2000<span style="color:gray;">)
)

</span><span style="color:blue;">DECLARE </span>@XP_CmdShell_Enabled <span style="color:blue;">int

SELECT </span>@XP_CmdShell_Enabled <span style="color:gray;">= </span><span style="color:magenta;">CONVERT</span><span style="color:gray;">(</span><span style="color:blue;">INT</span><span style="color:gray;">, </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:blue;">value</span><span style="color:gray;">, </span>value_in_use<span style="color:gray;">))
</span><span style="color:blue;">FROM master</span><span style="color:gray;">.</span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">configurations
</span><span style="color:blue;">WHERE </span>name <span style="color:gray;">= </span><span style="color:red;">'xp_cmdshell'

</span><span style="color:blue;">IF </span>@XP_CmdShell_Enabled <span style="color:gray;">= </span>0 <span style="color:blue;">BEGIN
	EXEC </span><span style="color:maroon;">sp_configure </span><span style="color:red;">'show advanced options'</span><span style="color:gray;">, </span>1
	<span style="color:blue;">RECONFIGURE WITH OVERRIDE 
	EXEC </span><span style="color:maroon;">sp_configure </span><span style="color:red;">'xp_cmdshell'</span><span style="color:gray;">, </span>1
	<span style="color:blue;">RECONFIGURE WITH OVERRIDE 
END 

INSERT INTO </span>#Output 
<span style="color:blue;">EXEC </span><span style="color:maroon;">xp_cmdshell </span><span style="color:red;">'whoami /priv'

</span><span style="color:blue;">IF </span>@XP_CmdShell_Enabled <span style="color:gray;">= </span>0 <span style="color:blue;">BEGIN
	EXEC </span><span style="color:maroon;">sp_configure </span><span style="color:red;">'xp_cmdshell'</span><span style="color:gray;">, </span>0
	<span style="color:blue;">RECONFIGURE WITH OVERRIDE 
END 

SELECT </span>IFI_Enabled <span style="color:gray;">= </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">) 
</span><span style="color:blue;">FROM </span>#Output 
<span style="color:blue;">WHERE </span>DataOut <span style="color:gray;">LIKE </span><span style="color:red;">'%SeManageVolumePrivilege%Enabled%'

</span><span style="color:blue;">DROP TABLE </span>#Output</pre>
<p>If IFI is turned off and it&#8217;s proper to turn it on for this server, here are your steps to turn it on:</p>
<ol>
<li>Open Administrative Tools / Local Security Policy</li>
<li>Local Policies</li>
<li>User Rights Assignment</li>
<li>Open &#8220;Perform volume maintenance tasks&#8221;</li>
<li>Add the user for the SQL Server service account</li>
</ol>
<p>If IFI can&#8217;t be used, here are your steps (which are also good even if IFI is being used):</p>
<ol>
<li>Monitor database sizes for manual growths</li>
<li>Grow files manually</li>
</ol>
<p>The SQL Server service account, if you don&#8217;t know which account it is, can be found in Administrative Tools / Services / SQL Server (InstanceName) under &#8220;Log On As&#8221;.  There are easier ways find the service account name in bulk if you&#8217;re doing this a lot, such as a slight variation of the query above.</p>
<h2>xp_cmdshell Rant</h2>
<p>Now, to be clear on xp_cmdshell which I used in the script above&#8230;. I do believe in security, but I don&#8217;t believe disabling this feature is the security you&#8217;re looking for.  When it&#8217;s set up properly, disabling xp_cmdshell is like locking the door to your data center&#8217;s supply closet with a keyless lock instead of limiting access to the data center.</p>
<p>I do have issue with granting execute permissions on this to non-sysadmin accounts.  I also have issue with granting sysadmin to anyone who asks, including vendors who insist that&#8217;s the only way their app will work.  This means the only users that can use it are the very, very few with sysadmin.  Also, any sysadmin can turn it on anyways, so the only extra security you get is a message in the error log saying it was turned on, but not even who did it.</p>
<p>My recommendation is that if you need it, leave it on.  If you don&#8217;t, leave it off.  If you have a one-time script that needs it, such as checking for IFI, turn it on just long enough to do what you need to do then automate turning it off.  The script follows this recommendation; it&#8217;s left on if it was already on, but turns it off if it was off.</p>
<p>It&#8217;s time for me to admit I&#8217;m going off on a tangent, so I&#8217;ll pass you off to Sean McCown&#8217;s (<a href="http://www.midnightdba.com" target="_blank">b</a>|<a href="https://twitter.com/KenpoDBA" target="_blank">t</a>) <a href="http://www.midnightdba.com/DBARant/security-theater/" target="_blank">Security Theater</a> post if you&#8217;re too fired up to stop now.</p>
<h2>In Summary</h2>
<p>Autogrowth &#8211; Yes.  Just as a fail-safe if performance is important to you.  Limit the size to limit the performance impact, but don&#8217;t cause undue fragmentation.</p>
<p>Manual Growth &#8211; Yes.  Trend how fast you&#8217;re growing so you know how to stay ahead of it.</p>
<p>Instant File Initialization &#8211; Usually.  Understand the limitations and security risks, and turn it on if it&#8217;s the right thing to do.</p>
<h2>Entry-Level Content Challenge</h2>
<p>This is my fourth post in Tim Ford&#8217;s <a href="http://thesqlagentman.com/2016/01/entry-level-content/" target="_blank"><span style="color:#0066cc;">Entry Level Content Challenge</span></a>.  I went through much of my career with people saying to manually grow files, but it was left at that.  I had no clue how to see how much space was used without clicking through the GUI, and passed it off as being ridiculous.  Now I found ways to make it easy, and I&#8217;m betting there&#8217;s a lot of people out there doing too much work or skipping this simply for not knowing how.</p>
<p>Read over Tim&#8217;s challenge and consider joining in.  It&#8217;s a great way to help people get past barriers you had in the past while learning how to clear them a little more efficiently yourself.  Anyone who&#8217;s reading this post already knows how to clear some barriers that others are viewing as an impassible brick wall, so you can make a difference for them.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/03/29/file-growths/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">42246</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Database Growth Trends – Idera Diagnostic Manager</title>
		<link>https://simplesqlserver.com/2016/03/21/database-growth-trends-idera-diagnostic-manager/</link>
					<comments>https://simplesqlserver.com/2016/03/21/database-growth-trends-idera-diagnostic-manager/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 21 Mar 2016 10:00:53 +0000</pubDate>
				<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[Idera]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=41985</guid>

					<description><![CDATA[How fast are your databases growing?  Should I panic about a 1 TB database that has 100 GB free on disk?  Am I safe with a 100 GB database that also has 100 GB free?  Based on those sizes&#8230;I have no idea. It&#8217;s possible that the 1 TB database is purging data as fast as [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>How fast are your databases growing?  Should I panic about a 1 TB database that has 100 GB free on disk?  Am I safe with a 100 GB database that also has 100 GB free?  Based on those sizes&#8230;I have no idea.</p>
<p>It&#8217;s possible that the 1 TB database is purging data as fast as it takes it in, so it&#8217;s been at 1 TB for a long time and may never use the 100 GB it has free.  Looking at the 100 GB database, it may have been a new 1 MB database created two months ago and grew 1 MB at a time to get to where it&#8217;s at now. (Bonus points if you panicked reading that last sentence.)</p>
<p>The thing to look for is how big that database was before.  What did you write down for how much space it used last week, last month, and several months ago?  Do you remember all those stock broker commercials saying &#8220;Past performance doesn&#8217;t predict future results&#8221; which is supposed to lead you on to think &#8220;Yeah, but it&#8217;s the best indicator I have.&#8221;  The same goes for database growths.  So, you did write down what it was before, right?</p>
<h2>My Default Monitoring Message</h2>
<p>I&#8217;ll copy/paste this section on multiple posts, so feel free to skip it if you&#8217;ve seen it before.</p>
<p>The biggest issue is the word &#8220;before&#8221;.  If you weren&#8217;t watching your servers before then you have nothing to compare the current state of your server to.  Also, if you&#8217;re watching it consistently then you&#8217;ll be able to make an apples to apples comparison.</p>
<p>Your production servers should have some kind of monitoring software in place, and the monitoring software is already capturing this type of information to help make all the pretty charts and graphs you see through the GUI.  You need this software to run so you can get alerted when things go wrong, and pulling this data will just make it more valuable.</p>
<p>Monitoring software doesn&#8217;t perform magic, so you can pull this data on your own if you don&#8217;t have any.  However, that&#8217;s taking on a lot of responsibility, time, and effort.  If something is collecting the data already, take advantage of that.</p>
<p>For me, I use Idera Diagnostic Manager, so my queries are going to look at the data collected using this tool.  Other monitoring tools are going to collect the same basic data in a database you can read.</p>
<p>If anyone from one of these companies wants to provide me with scripts that fill the same need, I&#8217;ll make a write up on them and give you credit for being awesome supporting your product like that.  I can only write against what I can test against, it&#8217;s nothing personal.</p>
<h2>What I&#8217;m Watching</h2>
<p>I want to know when my files will hit an autogrowth and when they&#8217;ll run out of space.</p>
<p>Running out of space is pretty obvious.  If you&#8217;re out of space, every command that needs more space will fail, and users will run to the supply closet for torches and pitchforks.  It&#8217;s a bad day for the DBA.</p>
<p>Autogrowth is less of an issue and less obvious.  That&#8217;s not saying it&#8217;s not an issues, especially for larger databases that can&#8217;t <a href="http://blogs.msdn.com/b/sql_pfe_blog/archive/2009/12/23/how-and-why-to-enable-instant-file-initialization.aspx" target="_blank">take advantage of IFI</a>.  The command that needs more space, and any that pile up behind it, will wait patiently (barring any timeouts) for the growth to take place.  However, SQL Server can be more patient than your users or applications.</p>
<p>That being said, if I knew I&#8217;d need more space than is allocated in the database using a simple script then I could do it when no one was waiting for the space.  No complaints, just make the files larger and move on with my life.  Oh, happy days.</p>
<p>To get all of this information I need to know a couple things:</p>
<ul>
<li>How much space is used in each filegroup</li>
<li>How much space is allocated to each filegroup</li>
<li>How much can each filegroup grow according to disk free space and growth rates</li>
</ul>
<h2>Idera DM Captures That</h2>
<p>Well, Idera does a pretty good job.  It captures the data size (used), data expansion (growth by growth rate and disk free space), and data file size (allocated).  The problem is that it captures this stuff on the database level, not the filegroup level.  This means that it&#8217;s possible for a filegroup to run out of space while the database as a whole has tons of free space.</p>
<p>I&#8217;m currently working in an environment where all data files for a single database are on the same drive, so it&#8217;s less concerning to me.  It is something to keep in mind, and may be very important to your environment.  If this is critical to you, I touch on how to capture this data yourself below.</p>
<p>If separating filegroups isn&#8217;t an issue for you (it often isn&#8217;t) then all you have to do to trend growths with Idera is to write a very long script that hits SQLdmRepository..DatabaseSize a couple times.  My script goes a little like this:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:green;">/*
MaxUsed is always from a week long range.  The reason is that this captures any spikes in usage such as off-hours index maintenance.
*/

</span><span style="color:gray;">;</span><span style="color:blue;">WITH </span>MostRecent <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>X<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">, </span>X<span style="color:gray;">.</span>UTCCollectionDateTime
		<span style="color:gray;">, </span>S<span style="color:gray;">.</span>InstanceName
		<span style="color:gray;">, </span>D<span style="color:gray;">.</span>DatabaseName
		<span style="color:gray;">, </span>DataFileSizeMB <span style="color:gray;">= </span>DataFileSizeInKilobytes<span style="color:gray;">/</span>1024
		<span style="color:gray;">, </span>DataFilePotentialSizeMB <span style="color:gray;">= (</span>X<span style="color:gray;">.</span>DataFileSizeInKilobytes <span style="color:gray;">+ </span>X<span style="color:gray;">.</span>DataExpansionInKilobytes<span style="color:gray;">)/</span>1024
	<span style="color:blue;">FROM </span><span style="color:gray;">(
			</span><span style="color:blue;">SELECT </span>DS<span style="color:gray;">.</span>DatabaseID
				<span style="color:gray;">, </span>RowNum <span style="color:gray;">= </span><span style="color:magenta;">DENSE_RANK</span><span style="color:gray;">() </span><span style="color:blue;">OVER </span><span style="color:gray;">(</span><span style="color:blue;">PARTITION BY </span>DS<span style="color:gray;">.</span>DatabaseID <span style="color:blue;">ORDER BY </span>UTCCollectionDateTime <span style="color:blue;">DESC</span><span style="color:gray;">)
				, </span>UTCCollectionDateTime
				<span style="color:gray;">, </span>DataFileSizeInKilobytes
				<span style="color:gray;">, </span>DataExpansionInKilobytes
			<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>DatabaseSize DS
			<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">&gt; </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>7 
		<span style="color:gray;">) </span>X
		<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>SQLServerDatabaseNames D <span style="color:blue;">ON </span>X<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>D<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>MonitoredSQLServers S <span style="color:blue;">ON </span>S<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>D<span style="color:gray;">.</span>SQLServerID 
	<span style="color:blue;">WHERE </span>RowNum <span style="color:gray;">= </span>1
<span style="color:gray;">)
, </span>ThisWeek <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>DS<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">, </span>MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>DataSizeInKilobytes <span style="color:gray;">+ </span>TextSizeInKilobytes <span style="color:gray;">+ </span>IndexSizeInKilobytes<span style="color:gray;">)/</span>1024
	<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>DatabaseSize DS
	<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">&gt; </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>7 
	<span style="color:blue;">GROUP BY </span>DS<span style="color:gray;">.</span>DatabaseID 
<span style="color:gray;">)
, </span>OneMonth <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>DS<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">, </span>MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>DataSizeInKilobytes <span style="color:gray;">+ </span>TextSizeInKilobytes <span style="color:gray;">+ </span>IndexSizeInKilobytes<span style="color:gray;">)/</span>1024
	<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>DatabaseSize DS
	<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>37 <span style="color:gray;">AND </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>30 
	<span style="color:blue;">GROUP BY </span>DS<span style="color:gray;">.</span>DatabaseID 
<span style="color:gray;">)
, </span>ThreeMonth <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>DS<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">, </span>MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>DataSizeInKilobytes <span style="color:gray;">+ </span>TextSizeInKilobytes <span style="color:gray;">+ </span>IndexSizeInKilobytes<span style="color:gray;">)/</span>1024
	<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>DatabaseSize DS
	<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>97 <span style="color:gray;">AND </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>90 
	<span style="color:blue;">GROUP BY </span>DS<span style="color:gray;">.</span>DatabaseID 
<span style="color:gray;">)
, </span>SixMonth <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>DS<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">, </span>MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>DataSizeInKilobytes <span style="color:gray;">+ </span>TextSizeInKilobytes <span style="color:gray;">+ </span>IndexSizeInKilobytes<span style="color:gray;">)/</span>1024
	<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>DatabaseSize DS
	<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>187 <span style="color:gray;">AND </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>180 
	<span style="color:blue;">GROUP BY </span>DS<span style="color:gray;">.</span>DatabaseID 
<span style="color:gray;">)
, </span>OneYear <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>DS<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">, </span>MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>DataSizeInKilobytes <span style="color:gray;">+ </span>TextSizeInKilobytes <span style="color:gray;">+ </span>IndexSizeInKilobytes<span style="color:gray;">)/</span>1024
	<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>DatabaseSize DS
	<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>365 <span style="color:gray;">AND </span><span style="color:magenta;">GETUTCDATE</span><span style="color:gray;">()-</span>358 
	<span style="color:blue;">GROUP BY </span>DS<span style="color:gray;">.</span>DatabaseID 
<span style="color:gray;">)
, </span>PreResults <span style="color:blue;">AS 
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>MR<span style="color:gray;">.</span>InstanceName
		<span style="color:gray;">, </span>MR<span style="color:gray;">.</span>DatabaseName 
		<span style="color:gray;">, </span>MR<span style="color:gray;">.</span>DataFileSizeMB
		<span style="color:gray;">, </span>ThisWeek_MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:blue;">AS INT</span><span style="color:gray;">)
		, </span>OneMonth_MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>M1<span style="color:gray;">.</span>MaxUsedMB <span style="color:blue;">AS INT</span><span style="color:gray;">)
		, </span>ThreeMonth_MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>M3<span style="color:gray;">.</span>MaxUsedMB <span style="color:blue;">AS INT</span><span style="color:gray;">)
		, </span>SixMonth_MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>M6<span style="color:gray;">.</span>MaxUsedMB <span style="color:blue;">AS INT</span><span style="color:gray;">)
		, </span>OneYear_MaxUsedMB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>M12<span style="color:gray;">.</span>MaxUsedMB <span style="color:blue;">AS INT</span><span style="color:gray;">)
		, </span>NextGrowthInDays_OneMonth <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFileSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M1<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>30 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)
		, </span>NextGrowthInDays_ThreeMonth <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFileSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M3<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>90 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)  
		, </span>NextGrowthInDays_SixMonth <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFileSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M6<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>180 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)  
		, </span>NextGrowthInDays_OneYear <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFileSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M12<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>360 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)  
		, </span>OutOfSpaceInDays_OneMonth <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFilePotentialSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M1<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>30 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)  
		, </span>OutOfSpaceInDays_ThreeMonth <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFilePotentialSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M3<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>90 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)  
		, </span>OutOfSpaceInDays_SixMonth <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFilePotentialSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M6<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>180 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">)  
		, </span>OutOfSpaceInDays_OneYear <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>MR<span style="color:gray;">.</span>DataFilePotentialSizeMB<span style="color:gray;">-</span>W<span style="color:gray;">.</span>MaxUsedMB<span style="color:gray;">)/(</span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">- </span>M12<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">+ </span>.0001<span style="color:gray;">)*</span>360 <span style="color:blue;">AS BIGINT</span><span style="color:gray;">) 
	</span><span style="color:blue;">FROM </span>MostRecent MR
		<span style="color:gray;">INNER JOIN </span>ThisWeek W <span style="color:blue;">ON </span>MR<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>W<span style="color:gray;">.</span>DatabaseID
		<span style="color:gray;">LEFT JOIN </span>OneMonth M1 <span style="color:blue;">ON </span>MR<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>M1<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">AND </span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">&gt; </span>M1<span style="color:gray;">.</span>MaxUsedMB
		<span style="color:gray;">LEFT JOIN </span>ThreeMonth M3 <span style="color:blue;">ON </span>MR<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>M3<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">AND </span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">&gt; </span>M3<span style="color:gray;">.</span>MaxUsedMB
		<span style="color:gray;">LEFT JOIN </span>SixMonth M6 <span style="color:blue;">ON </span>MR<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>M6<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">AND </span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">&gt; </span>M6<span style="color:gray;">.</span>MaxUsedMB
		<span style="color:gray;">LEFT JOIN </span>OneYear M12 <span style="color:blue;">ON </span>MR<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>M12<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">AND </span>W<span style="color:gray;">.</span>MaxUsedMB <span style="color:gray;">&gt; </span>M12<span style="color:gray;">.</span>MaxUsedMB
<span style="color:gray;">)
</span><span style="color:blue;">SELECT </span>InstanceName
	<span style="color:gray;">, </span>DatabaseName 
	<span style="color:gray;">, </span>NextGrowthInDays_Min <span style="color:gray;">= </span><span style="color:blue;">CASE WHEN </span>NextGrowthInDays_OneMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>NextGrowthInDays_ThreeMonth <span style="color:gray;">, </span>1000000<span style="color:gray;">)
										AND </span>NextGrowthInDays_OneMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>NextGrowthInDays_SixMonth<span style="color:gray;">, </span>1000000<span style="color:gray;">) 
										AND </span>NextGrowthInDays_OneMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>NextGrowthInDays_OneYear<span style="color:gray;">, </span>1000000<span style="color:gray;">)
									</span><span style="color:blue;">THEN </span>NextGrowthInDays_OneMonth 
								<span style="color:blue;">WHEN </span>NextGrowthInDays_ThreeMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>NextGrowthInDays_SixMonth<span style="color:gray;">, </span>1000000<span style="color:gray;">)
										AND </span>NextGrowthInDays_ThreeMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>NextGrowthInDays_OneYear<span style="color:gray;">, </span>1000000<span style="color:gray;">)
									</span><span style="color:blue;">THEN </span>NextGrowthInDays_ThreeMonth
								<span style="color:blue;">WHEN </span>NextGrowthInDays_SixMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>NextGrowthInDays_OneYear<span style="color:gray;">, </span>1000000<span style="color:gray;">)
									</span><span style="color:blue;">THEN </span>NextGrowthInDays_SixMonth
								<span style="color:blue;">ELSE </span>NextGrowthInDays_OneYear
							<span style="color:blue;">END
	</span><span style="color:gray;">, </span>OutOfSpaceInDays_Min <span style="color:gray;">= </span><span style="color:blue;">CASE WHEN </span>OutOfSpaceInDays_OneMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>OutOfSpaceInDays_ThreeMonth<span style="color:gray;">, </span>1000000<span style="color:gray;">)
										AND </span>OutOfSpaceInDays_OneMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>OutOfSpaceInDays_SixMonth<span style="color:gray;">, </span>1000000<span style="color:gray;">)
										AND </span>OutOfSpaceInDays_OneMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>OutOfSpaceInDays_OneYear<span style="color:gray;">, </span>1000000<span style="color:gray;">)
									</span><span style="color:blue;">THEN </span>OutOfSpaceInDays_OneMonth 
								<span style="color:blue;">WHEN </span>OutOfSpaceInDays_ThreeMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>OutOfSpaceInDays_SixMonth<span style="color:gray;">, </span>1000000<span style="color:gray;">)
										AND </span>OutOfSpaceInDays_ThreeMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>OutOfSpaceInDays_OneYear<span style="color:gray;">, </span>1000000<span style="color:gray;">)
									</span><span style="color:blue;">THEN </span>OutOfSpaceInDays_ThreeMonth
								<span style="color:blue;">WHEN </span>OutOfSpaceInDays_SixMonth <span style="color:gray;">&lt;= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>OutOfSpaceInDays_OneYear<span style="color:gray;">, </span>1000000<span style="color:gray;">)
									</span><span style="color:blue;">THEN </span>OutOfSpaceInDays_SixMonth
								<span style="color:blue;">ELSE </span>OutOfSpaceInDays_OneYear
							<span style="color:blue;">END
	</span><span style="color:gray;">, </span>DataFileSizeMB
	<span style="color:gray;">, </span>ThisWeek_MaxUsedMB
	<span style="color:gray;">, </span>OneMonth_MaxUsedMB 
	<span style="color:gray;">, </span>ThreeMonth_MaxUsedMB 
	<span style="color:gray;">, </span>SixMonth_MaxUsedMB
	<span style="color:gray;">, </span>OneYear_MaxUsedMB
	<span style="color:gray;">, </span>NextGrowthInDays_OneMonth 
	<span style="color:gray;">, </span>NextGrowthInDays_ThreeMonth 
	<span style="color:gray;">, </span>NextGrowthInDays_SixMonth 
	<span style="color:gray;">, </span>NextGrowthInDays_OneYear 
	<span style="color:gray;">, </span>OutOfSpaceInDays_OneMonth
	<span style="color:gray;">, </span>OutOfSpaceInDays_ThreeMonth
	<span style="color:gray;">, </span>OutOfSpaceInDays_SixMonth
	<span style="color:gray;">, </span>OutOfSpaceInDays_OneYear 
<span style="color:blue;">FROM </span>PreResults
<span style="color:blue;">WHERE </span>OutOfSpaceInDays_OneMonth <span style="color:gray;">&lt; </span>100
	<span style="color:gray;">OR </span>OutOfSpaceInDays_ThreeMonth <span style="color:gray;">&lt; </span>100
	<span style="color:gray;">OR </span>OutOfSpaceInDays_SixMonth <span style="color:gray;">&lt; </span>100
	<span style="color:gray;">OR </span>OutOfSpaceInDays_OneYear <span style="color:gray;">&lt; </span>100
	<span style="color:gray;">OR </span>NextGrowthInDays_OneMonth <span style="color:gray;">&lt; </span>15
	<span style="color:gray;">OR </span>NextGrowthInDays_ThreeMonth <span style="color:gray;">&lt; </span>15
	<span style="color:gray;">OR </span>NextGrowthInDays_SixMonth <span style="color:gray;">&lt; </span>15
	<span style="color:gray;">OR </span>NextGrowthInDays_OneYear <span style="color:gray;">&lt; </span>15
<span style="color:blue;">ORDER BY </span>DataFileSizeMB <span style="color:blue;">DESC
</span></pre>
<p>What this does is look at the trends based on what the database used 30, 90, 180, and 360 days ago to try to predict the future.  By &#8220;predict the future&#8221; I mean it&#8217;s not perfect, and can&#8217;t be perfect.  However, it gives you very good data with a few false positives and very few false negatives.</p>
<p>Speaking of this not being perfect, it double-dips.  If there are two databases growing on the same drive with 10 GB free, it says that each one has 10 GB of growth available.  I was not able to rewrite this to take that into account while also accounting for databases with multiple files in a single filegroup spread across multiple drives.</p>
<p>Now I&#8217;m two weeks ahead of projected file growths and over three months ahead of projected out-of-space errors.</p>
<p>This is very basic information, and I&#8217;d have trouble imagining any SQL monitoring software not capturing this in a table you can query.</p>
<h2>If I Didn&#8217;t Have Idera&#8230;</h2>
<p>If I didn&#8217;t have Idera, or any other monitoring software I could query, I&#8217;d be going out at least once a day and pulling the allocated and used space from every file on every server.  I may want to do it more often to find peak usage, such as when online index rebuilds are using twice their typical space over the weekends; I take advantage of Idera capturing this hourly.  I&#8217;ll capture it using something like this:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span>@SQL <span style="color:gray;">= 
</span><span style="color:red;">'USE [?]

INSERT INTO DBA..FileSizes (DateAdded, Database_ID, File_ID, Data_Space_ID, SizeMB, UsedMB)
	select GETDATE() 
		, Database_ID = DB_ID()
		, f.File_ID
		, f.data_space_id
		, SizeMB = ((f.size*8/1024)) 
		, UsedMB = (FileProperty(f.name, ''SpaceUsed'')*8/1024) 
	from sys.database_files f
	'

</span><span style="color:blue;">exec </span><span style="color:maroon;">sp_MSforeachdb </span>@SQL
</pre>
<p>Then I&#8217;d need to see how much free space I have on each drive, so I&#8217;d go to Ed Wagner&#8217;s article <a href="http://www.sqlservercentral.com/articles/Drive+space/134523/" target="_blank">Time and Space: How to Monitor Drive Space in SQL Server</a>.  Knowing that we use MountPoints in places, I&#8217;d have to change his script to use &#8220;volume&#8221; instead of &#8220;logicaldisk&#8221; (also mentioned in his comments).  This minor change is about the best case scenario you&#8217;ll run across.</p>
<p>Remember, the scripts you find on the internet are typically designed to work perfect on the AUTHOR&#8217;s system, not yours.  It&#8217;s not Ed&#8217;s fault they don&#8217;t use MountPoints, in fact he should probably be congratulated for not making his environment more complex than it needs to be.  He should also be congratulated on not making a change to his article that he can&#8217;t properly test.  At least you know you have to be extra careful testing this change instead of being led to believe the author did something they couldn&#8217;t do properly.</p>
<p>After getting the drive&#8217;s free space, I&#8217;d have to look at the file growth rates to see how large my files could grow.  Keep in mind that growth in sys.database_files could be either a percentage or the number of 8kb pages the file will grow by, depending on the value in is_percent_growth.  Static growths are easy, just divide by 128 and that&#8217;s how many MBs it will grow by.  It&#8217;s easier (and better for your server) to change percentage growths to MB than it is to look up the logarithmic functions used to calculate compound interest that also apply here.</p>
<p>Now that you have the free space on the drive and the growth rates on the files, you can calculate the maximum size the file can grow to.  Use this with trends on the used space to calculate when that size won&#8217;t be enough for you.</p>
<p>Make sure you test your results, you&#8217;ll have to support it.</p>
<h2>So Your Database Is Growing&#8230;</h2>
<p>Databases get larger, it&#8217;s what they do.  A normal DBA then goes through and makes sure that there&#8217;s room for that growth.  A better DBA will do that, too, but then follow it up by figuring out why the database is growing and try to justify it.</p>
<p>I have seen way too many databases out there that don&#8217;t purge old data that&#8217;s useless to the company.  Sometimes the purge was never written, sometimes the code to do that is never being called for several somewhat humorous reasons.  Any way you look at it, the data is growing and it&#8217;s easy to fall into the trap of just adding space every month, forever.</p>
<p>The way I handle this is by watching my tables and seeing how they&#8217;re growing.  These are treated just like databases where the largest one isn&#8217;t necessarily the growth issue, and looking at historical data to see the growth trend is a better solution.</p>
<p>In the past I wrote a post called <a href="https://simplesqlserver.com/2013/09/10/monitoring-database-and-table-sizes/" target="_blank">Monitoring Database and Table Sizes</a> to get this info yourself.  In the future, I&#8217;ll write Table Growth Trends &#8211; Idera Diagnostic Manager.  At the time of this writing, I&#8217;m planning on the Table Growth post coming out three weeks after this one.  Until then, I put this script up on my <a href="https://simplesqlserver.com/ideradmscripts/" target="_blank">Idera Diagnostic Manager Scripts</a> page, which is where I keep these posts organized and where I put my scripts to wait for me to blog about them.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/03/21/database-growth-trends-idera-diagnostic-manager/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">41985</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Multiserver Queries</title>
		<link>https://simplesqlserver.com/2016/03/14/multiserver-queries/</link>
					<comments>https://simplesqlserver.com/2016/03/14/multiserver-queries/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 14 Mar 2016 10:00:27 +0000</pubDate>
				<category><![CDATA[Entry Level]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=42411</guid>

					<description><![CDATA[Setting up registered servers and practical uses of multiserver queries in SSMS.]]></description>
										<content:encoded><![CDATA[<p>You can run queries against multiple servers at once, and it&#8217;s quite useful for a number of reasons.  I use it to check settings, verify backups and DBCC ran recently on unmonitored servers, make sure all servers rebooted during the maintenance window, and many other reasons.</p>
<p>This is all done through registering servers on SQL Server and opening a new query for the group.  I&#8217;ll walk you through that then run a couple queries that I find useful.</p>
<p>Unfortunately, this method is so easy that you&#8217;ll only ever have to see it once and won&#8217;t be back to visit this blog post again.  Hopefully the scripts below will inspire a return visit.</p>
<h2>Setting It Up</h2>
<p>Registered servers are local to your SSMS install.  You&#8217;re not changing server settings by setting this up, and this will be lost if you reinstall SSMS without exporting your settings.</p>
<p>First, open the Registered Servers pane by going to View / Registered Servers.</p>
<p><img data-attachment-id="42431" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/view-registered-servers/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg" data-orig-size="369,132" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="View Registered Servers" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg?w=369" class="alignnone size-full wp-image-42431" src="https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg" alt="View Registered Servers" width="369" height="132" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg 369w, https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg?w=150&amp;h=54 150w, https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg?w=300&amp;h=107 300w" sizes="(max-width: 369px) 100vw, 369px" /></p>
<p>Add a new server group and call it anything you want.  I&#8217;ll call mine blog because that&#8217;s what I&#8217;m using it for.  I love the idea of having Prod and Non-Prod groups, especially since I can run a query against all my Non-Prod servers that aren&#8217;t monitored to verify backups and DBCC checks are being done.</p>
<p>It&#8217;s important to note at this point that you can have a server in more than one group and groups can be nested.  So in my prod group I have groups for each data center, then the servers are at that level.  I could also have the same servers in functional groups, such as Finance, IT, and Why_Me.</p>
<p><img data-attachment-id="42422" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/create-registered-servers-group/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg" data-orig-size="411,262" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Create Registered Servers Group" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg?w=411" class="alignnone size-full wp-image-42422" src="https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg" alt="Create Registered Servers Group" width="411" height="262" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg 411w, https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg?w=150&amp;h=96 150w, https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg?w=300&amp;h=191 300w" sizes="(max-width: 411px) 100vw, 411px" /></p>
<p>&nbsp;</p>
<p>Right-click and do a New Server Registration, and the options should be pretty natural to you.  For this example, I used aliases for &#8220;Registered Server Name&#8221;, but I stick to the default when doing this for myself.</p>
<p>At the most basic level, it should look like this.</p>
<p><img loading="lazy" data-attachment-id="42426" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/registered-server-group/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/registered-server-group.jpg" data-orig-size="177,138" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Registered Server Group" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/registered-server-group.jpg?w=177" class="alignnone size-full wp-image-42426" src="https://simplesqlserver.com/wp-content/uploads/2016/02/registered-server-group.jpg" alt="Registered Server Group" width="177" height="138" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/registered-server-group.jpg 177w, https://simplesqlserver.com/wp-content/uploads/2016/02/registered-server-group.jpg?w=150&amp;h=117 150w" sizes="(max-width: 177px) 100vw, 177px" /></p>
<p>That&#8217;s it, you&#8217;re set up.</p>
<h2>Running Queries</h2>
<p>This is easier than the setup.</p>
<p>Right-click on a group and click on New Query.</p>
<p><img loading="lazy" data-attachment-id="42465" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/multiserver-new-query/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg" data-orig-size="368,336" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Multiserver New Query" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg?w=368" class="alignnone size-full wp-image-42465" src="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg" alt="Multiserver New Query" width="368" height="336" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg 368w, https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg?w=150&amp;h=137 150w, https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg?w=300&amp;h=274 300w" sizes="(max-width: 368px) 100vw, 368px" /></p>
<p>It opens a new query window with the only oddity being instead of a yellowish bar saying &#8220;Connected. (1/1)&#8221;, now you have a pink bar saying &#8220;Connected. (2/2)&#8221; along with the group name.</p>
<p><img loading="lazy" data-attachment-id="42471" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/multiserver-connected/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-connected.jpg" data-orig-size="197,26" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Multiserver Connected" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-connected.jpg?w=197" class="alignnone size-full wp-image-42471" src="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-connected.jpg" alt="Multiserver Connected" width="197" height="26" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-connected.jpg 197w, https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-connected.jpg?w=150&amp;h=20 150w" sizes="(max-width: 197px) 100vw, 197px" /></p>
<p>This will be connected to all servers directly in the group and in groups nested within that group.  There will be a long delay if one of the servers isn&#8217;t available, so it&#8217;s worth while to keep these groups cleaned up.</p>
<p>Now you run a query.  Here&#8217;s how the results are grouped by default.  Although they showed up in order for me, that was a coincidence.  They&#8217;ll actually be in the order they came back, so a server that returned results in 1 second will come before a server that returned results in 2 seconds.</p>
<p><img loading="lazy" data-attachment-id="42476" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/multiserver-results/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-results.jpg" data-orig-size="220,415" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Multiserver Results" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-results.jpg?w=220" class="alignnone size-full wp-image-42476" src="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-results.jpg" alt="Multiserver Results" width="220" height="415" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-results.jpg 220w, https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-results.jpg?w=80&amp;h=150 80w" sizes="(max-width: 220px) 100vw, 220px" /></p>
<p>You can go to Tools / Options and change a couple things, but the defaults tend to work great.</p>
<p><img loading="lazy" data-attachment-id="42424" data-permalink="https://simplesqlserver.com/2016/03/14/multiserver-queries/multiserver-query-options/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg" data-orig-size="757,440" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Multiserver Query Options" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg?w=652" class="alignnone size-full wp-image-42424" src="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg" alt="Multiserver Query Options" width="757" height="440" srcset="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg 757w, https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg?w=150&amp;h=87 150w, https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg?w=300&amp;h=174 300w" sizes="(max-width: 757px) 100vw, 757px" /></p>
<p>Now that it&#8217;s set up, right-click on your group and go to Tasks / Export&#8230; to save this off somewhere.  The only place this is saved right now is on your workstation, and those words will make any DBA cringe.</p>
<h2>Practical Uses</h2>
<p>Doing &#8220;SELECT 1&#8221; like I did above is always a blast, but it&#8217;s not all that productive.  As always, I encourage you to play around with it yourself, but here&#8217;s some things to get you started.</p>
<h3>Server (services) last started</h3>
<p>When I do Windows Updates I reboot all the database servers every month, even if the updates don&#8217;t say the needed the reboot at that time.  However, can be difficult to tell which servers I rebooted and which ones just aren&#8217;t begging for a reboot.  Since TempDB is recreated each time services start, I check to make sure that happened during the WSUS window.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span>create_date <span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases </span><span style="color:blue;">WHERE </span>database_id <span style="color:gray;">= </span>2
</pre>
<p>If I was doing this for hundreds of servers, I&#8217;d simply change the query to say AND create_date &lt; GetDate()-1 then only my problem servers would return results.</p>
<h3>DBCC and Backups up-to-date</h3>
<p>My non-prod servers aren&#8217;t monitored, so things go missed sometimes.  Backup jobs may be failing and I don&#8217;t know until I have time to check everything on all my non-prod servers.  Unfortunately, that doesn&#8217;t happen very often.  As with everything else in life, I cheat as much as possible to make it easier.  This time by running this as a multiserver query:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:green;">--Leaving some overlap to avoid false alarms due to timing issues
--We want to know if there hasn't been:
--	Full backup in the last 8 days (should happen every 7)
--	Full or diff backup in the last 2 days (should happen every 1)
--  Log backup in the last 4 hours (should happen every 1 or more often)
--  DBCC CheckDB run successfully in the last 8 days (should happen every 7)

</span><span style="color:blue;">SET NOCOUNT ON

DECLARE </span>@FullDays <span style="color:blue;">Int
SELECT </span>@FullDays <span style="color:gray;">= </span>8

<span style="color:blue;">DECLARE </span>@FullDiffDays <span style="color:blue;">Int
SELECT </span>@FullDiffDays <span style="color:gray;">= </span>2

<span style="color:blue;">DECLARE </span>@LogHours <span style="color:blue;">Int
SELECT </span>@LogHours <span style="color:gray;">= </span>4

<span style="color:blue;">DECLARE </span>@DBCCDays <span style="color:blue;">Int
SELECT </span>@DBCCDays <span style="color:gray;">= </span>8

<span style="color:blue;">DECLARE </span>@SQLText <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>4000<span style="color:gray;">)

</span><span style="color:blue;">IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#Results'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#Results
<span style="color:blue;">END

CREATE TABLE </span>#Results 
<span style="color:gray;">(
	</span>ResultText <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>2000<span style="color:gray;">)
)

</span><span style="color:blue;">IF </span><span style="color:magenta;">Object_ID</span><span style="color:gray;">(</span><span style="color:red;">'TempDB..#DBCC_Temp'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#DBCC_Temp
<span style="color:blue;">END

CREATE TABLE </span>#DBCC_Temp 
<span style="color:gray;">(
	</span>ParentObject <span style="color:blue;">VARCHAR</span><span style="color:gray;">(</span>255<span style="color:gray;">),
	</span>[Object] <span style="color:blue;">VARCHAR</span><span style="color:gray;">(</span>255<span style="color:gray;">),
	</span>Field <span style="color:blue;">VARCHAR</span><span style="color:gray;">(</span>255<span style="color:gray;">),
	</span>[Value] <span style="color:blue;">VARCHAR</span><span style="color:gray;">(</span>255<span style="color:gray;">)
)

</span><span style="color:green;">--When was the last full backup?
</span><span style="color:blue;">INSERT INTO </span>#Results <span style="color:gray;">(</span>ResultText<span style="color:gray;">)
</span><span style="color:blue;">SELECT </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'ComputerNamePhysicalNetBIOS'</span><span style="color:gray;">) </span><span style="color:blue;">as sysname</span><span style="color:gray;">) 
	+ </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">NULLIF</span><span style="color:gray;">(</span><span style="color:red;">'\' </span><span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'InstanceName'</span><span style="color:gray;">) </span><span style="color:blue;">as sysname</span><span style="color:gray;">),</span><span style="color:red;">'\MSSQLSERVER'</span><span style="color:gray;">),</span><span style="color:red;">''</span><span style="color:gray;">) 
	+ </span><span style="color:red;">'.' 
	</span><span style="color:gray;">+ </span>d<span style="color:gray;">.</span>name 
	<span style="color:gray;">+ </span><span style="color:red;">' hasn''t had a full backup in over ' 
	</span><span style="color:gray;">+ </span><span style="color:magenta;">Cast</span><span style="color:gray;">(</span>@FullDays <span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>2<span style="color:gray;">)) 
	+ </span><span style="color:red;">' day(s).  Last full backup was ' 
	</span><span style="color:gray;">+ </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:red;">'on ' </span><span style="color:gray;">+ </span><span style="color:magenta;">Convert</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>16<span style="color:gray;">), </span><span style="color:magenta;">max</span><span style="color:gray;">(</span>b<span style="color:gray;">.</span>backup_finish_date<span style="color:gray;">), </span>120<span style="color:gray;">), </span><span style="color:red;">'NEVER'</span><span style="color:gray;">) + </span><span style="color:red;">'.'
</span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases </span>d 
	<span style="color:gray;">LEFT JOIN </span>msdb<span style="color:gray;">..</span>backupset b <span style="color:blue;">ON </span>d<span style="color:gray;">.</span>name <span style="color:gray;">= </span>b<span style="color:gray;">.</span><span style="color:blue;">database_name </span><span style="color:gray;">AND </span>b<span style="color:gray;">.</span><span style="color:blue;">type </span><span style="color:gray;">= </span><span style="color:red;">'D'
</span><span style="color:blue;">WHERE </span>d<span style="color:gray;">.</span>name <span style="color:gray;">&lt;&gt; </span><span style="color:red;">'tempdb'
	</span><span style="color:gray;">AND </span>d<span style="color:gray;">.</span><span style="color:blue;">state </span><span style="color:gray;">= </span>0
<span style="color:blue;">GROUP BY </span>d<span style="color:gray;">.</span>name 
<span style="color:blue;">HAVING </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">max</span><span style="color:gray;">(</span>b<span style="color:gray;">.</span>backup_finish_date<span style="color:gray;">), </span><span style="color:red;">'2000-01-01'</span><span style="color:gray;">) &lt; </span><span style="color:magenta;">GetDate</span><span style="color:gray;">()-</span>@FullDays 

<span style="color:green;">--When was the last full or differential backup?
</span><span style="color:blue;">INSERT INTO </span>#Results <span style="color:gray;">(</span>ResultText<span style="color:gray;">)
</span><span style="color:blue;">SELECT </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'ComputerNamePhysicalNetBIOS'</span><span style="color:gray;">) </span><span style="color:blue;">as sysname</span><span style="color:gray;">) 
	+ </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">NULLIF</span><span style="color:gray;">(</span><span style="color:red;">'\' </span><span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'InstanceName'</span><span style="color:gray;">) </span><span style="color:blue;">as sysname</span><span style="color:gray;">),</span><span style="color:red;">'\MSSQLSERVER'</span><span style="color:gray;">),</span><span style="color:red;">''</span><span style="color:gray;">) 
	+ </span><span style="color:red;">'.' 
	</span><span style="color:gray;">+ </span>d<span style="color:gray;">.</span>name 
	<span style="color:gray;">+ </span><span style="color:red;">' hasn''t had a full or diff backup in over ' 
	</span><span style="color:gray;">+ </span><span style="color:magenta;">Cast</span><span style="color:gray;">(</span>@FullDiffDays <span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>2<span style="color:gray;">)) 
	+ </span><span style="color:red;">' day(s).  Last full or diff backup was ' 
	</span><span style="color:gray;">+ </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:red;">'on ' </span><span style="color:gray;">+ </span><span style="color:magenta;">Convert</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>16<span style="color:gray;">), </span><span style="color:magenta;">max</span><span style="color:gray;">(</span>b<span style="color:gray;">.</span>backup_finish_date<span style="color:gray;">), </span>120<span style="color:gray;">), </span><span style="color:red;">'NEVER'</span><span style="color:gray;">) + </span><span style="color:red;">'.'
</span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases </span>d
	<span style="color:gray;">LEFT JOIN </span>msdb<span style="color:gray;">..</span>backupset b <span style="color:blue;">ON </span>d<span style="color:gray;">.</span>name <span style="color:gray;">= </span>b<span style="color:gray;">.</span><span style="color:blue;">database_name </span><span style="color:gray;">AND </span>b<span style="color:gray;">.</span><span style="color:blue;">type </span><span style="color:gray;">IN (</span><span style="color:red;">'D'</span><span style="color:gray;">, </span><span style="color:red;">'I'</span><span style="color:gray;">)
</span><span style="color:blue;">WHERE </span>d<span style="color:gray;">.</span>name <span style="color:gray;">&lt;&gt; </span><span style="color:red;">'tempdb'
	</span><span style="color:gray;">AND </span>d<span style="color:gray;">.</span><span style="color:blue;">state </span><span style="color:gray;">= </span>0
<span style="color:blue;">GROUP BY </span>d<span style="color:gray;">.</span>name  
<span style="color:blue;">HAVING </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">max</span><span style="color:gray;">(</span>b<span style="color:gray;">.</span>backup_finish_date<span style="color:gray;">), </span><span style="color:red;">'2000-01-01'</span><span style="color:gray;">) &lt; </span><span style="color:magenta;">GetDate</span><span style="color:gray;">()-</span>@FullDiffDays 

<span style="color:green;">--When was the last log backup?
</span><span style="color:blue;">INSERT INTO </span>#Results <span style="color:gray;">(</span>ResultText<span style="color:gray;">)
</span><span style="color:blue;">SELECT </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'ComputerNamePhysicalNetBIOS'</span><span style="color:gray;">) </span><span style="color:blue;">as sysname</span><span style="color:gray;">) 
	+ </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">NULLIF</span><span style="color:gray;">(</span><span style="color:red;">'\' </span><span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'InstanceName'</span><span style="color:gray;">) </span><span style="color:blue;">as sysname</span><span style="color:gray;">),</span><span style="color:red;">'\MSSQLSERVER'</span><span style="color:gray;">),</span><span style="color:red;">''</span><span style="color:gray;">) 
	+ </span><span style="color:red;">'.' 
	</span><span style="color:gray;">+ </span>d<span style="color:gray;">.</span>name 
	<span style="color:gray;">+ </span><span style="color:red;">' hasn''t had a log backup in over ' 
	</span><span style="color:gray;">+ </span><span style="color:magenta;">Cast</span><span style="color:gray;">(</span>@LogHours <span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>3<span style="color:gray;">)) 
	+ </span><span style="color:red;">' hour(s).  Last log backup was ' 
	</span><span style="color:gray;">+ </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:red;">'on ' </span><span style="color:gray;">+ </span><span style="color:magenta;">Convert</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>16<span style="color:gray;">), </span><span style="color:magenta;">max</span><span style="color:gray;">(</span>b<span style="color:gray;">.</span>backup_finish_date<span style="color:gray;">), </span>120<span style="color:gray;">), </span><span style="color:red;">'NEVER'</span><span style="color:gray;">) + </span><span style="color:red;">'.'
</span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases </span>d
	<span style="color:gray;">LEFT JOIN </span>msdb<span style="color:gray;">..</span>backupset b <span style="color:blue;">ON </span>d<span style="color:gray;">.</span>name <span style="color:gray;">= </span>b<span style="color:gray;">.</span><span style="color:blue;">database_name </span><span style="color:gray;">AND </span>b<span style="color:gray;">.</span><span style="color:blue;">type </span><span style="color:gray;">= </span><span style="color:red;">'L'
</span><span style="color:blue;">WHERE </span>d<span style="color:gray;">.</span>name <span style="color:gray;">NOT IN (</span><span style="color:red;">'tempdb'</span><span style="color:gray;">, </span><span style="color:red;">'model'</span><span style="color:gray;">)</span><span style="color:green;">--Model is by default in full, rarely backed up, and not modified enough to complain about.
	</span><span style="color:gray;">AND </span>d<span style="color:gray;">.</span>recovery_model_desc <span style="color:gray;">&lt;&gt; </span><span style="color:red;">'SIMPLE'
	</span><span style="color:gray;">AND </span>d<span style="color:gray;">.</span><span style="color:blue;">state </span><span style="color:gray;">= </span>0
<span style="color:blue;">GROUP BY </span>d<span style="color:gray;">.</span>name  
<span style="color:blue;">HAVING </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">max</span><span style="color:gray;">(</span>b<span style="color:gray;">.</span>backup_finish_date<span style="color:gray;">), </span><span style="color:red;">'2000-01-01'</span><span style="color:gray;">) &lt; </span><span style="color:magenta;">DateAdd</span><span style="color:gray;">(</span><span style="color:magenta;">Hour</span><span style="color:gray;">, -</span>1 <span style="color:gray;">* </span>@LogHours<span style="color:gray;">, </span><span style="color:magenta;">GetDate</span><span style="color:gray;">()) 

</span><span style="color:blue;">SELECT </span>@SQLText <span style="color:gray;">= </span><span style="color:red;">N'use [?]; 

DECLARE @DBName sysname
DECLARE @HasRetried bit 

SELECT @DBName = CAST(''?'' as VarChar(256)) 
SELECT @HasRetried = 0

TRUNCATE TABLE #DBCC_Temp

IF EXISTS (SELECT d.state FROM sys.databases d WHERE d.name = @DBName AND d.State = 0 /*Online*/ AND d.is_read_only = 0 AND d.create_date &lt; GetDate()-7) AND ''tempdb'' &lt;&gt; @DBName BEGIN
	INSERT INTO #DBCC_Temp
	EXECUTE(''DBCC PAGE (['' + @DBName + ''], 1, 9, 3)WITH TABLERESULTS, NO_INFOMSGS'')

	INSERT INTO #Results (ResultText)
	SELECT CAST(serverproperty(''ComputerNamePhysicalNetBIOS'') as sysname) 
		+ ISNULL(NULLIF(''\'' + CAST(serverproperty(''InstanceName'') as sysname),''\MSSQLSERVER''),'''') 
		+ ''.''
		+ @DBName
		+ '' hasn''''t had a successful DBCC check in the last ' </span><span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>@DBCCDays <span style="color:blue;">as VARCHAR</span><span style="color:gray;">(</span>2<span style="color:gray;">)) + </span><span style="color:red;">' day(s).  Last successful check was ''
		+ ISNULL(NULLIF(MAX([Value]),''1900-01-01 00:00:00.000''), ''NEVER'')
	FROM #DBCC_Temp
	WHERE Field = ''dbi_dbccLastKnownGood''
	HAVING MAX([Value]) &lt; GetDate()-' </span><span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>@DBCCDays <span style="color:blue;">as VARCHAR</span><span style="color:gray;">(</span>2<span style="color:gray;">)) + </span><span style="color:red;">'
END
'

</span><span style="color:blue;">exec </span><span style="color:maroon;">sp_MSforeachdb </span>@SQLText 

<span style="color:blue;">SELECT </span>ResultText 
<span style="color:blue;">FROM </span>#Results

<span style="color:blue;">DROP TABLE </span>#Results
<span style="color:blue;">DROP TABLE </span>#DBCC_Temp
</pre>
<p>If I get any results from this, I have something to fix.  This also works for daily full backups because it does diff or full, but you may want to change it around to fit your maintenance schedules.</p>
<h3>Version Check</h3>
<p>If you&#8217;re evaluating what version all of your servers are on so you can make plans to patch them, just run this:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span><span style="color:magenta;">@@VERSION
</span></pre>
<p>You have everything you need to compare it to <a href="http://sqlserverbuilds.blogspot.com/" target="_blank">SQLServerBuilds.BlogSpot.com</a>.  The only thing is that I tend to copy/paste the results into Excel so I can sort them there.</p>
<h2>More Uses</h2>
<p>I use registered servers and multiserver queries for more than this.  In fact, I wrote this post so I could link to it in a future post on Instant File Initialization.  Once I have that one here, I&#8217;ll have four examples of uses.</p>
<p>Other readers and I would benefit from hearing more uses of this feature.  Let us hear the best uses you have, preferably with a script.</p>
<h2>Entry-Level Content Challenge</h2>
<p>This is my third post in Tim Ford&#8217;s <a href="http://thesqlagentman.com/2016/01/entry-level-content/" target="_blank">Entry Level Content Challenge</a>.  I was tempted to write a post and just tell people to run a script as a multiserver query.  After all, it&#8217;s very simple after you see it for the first time.  However, you have to see it for the first time before you get to that point.</p>
<p>This is the exact purpose of his challenge.  Read over it and consider joining in.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/03/14/multiserver-queries/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">42411</post-id>
		<media:thumbnail url="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg"/>
		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-new-query.jpg">
			<media:title type="html">Multiserver New Query</media:title>
		</media:content>

		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/view-registered-servers1.jpg">
			<media:title type="html">View Registered Servers</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/create-registered-servers-group.jpg">
			<media:title type="html">Create Registered Servers Group</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/registered-server-group.jpg">
			<media:title type="html">Registered Server Group</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-connected.jpg">
			<media:title type="html">Multiserver Connected</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-results.jpg">
			<media:title type="html">Multiserver Results</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/02/multiserver-query-options.jpg">
			<media:title type="html">Multiserver Query Options</media:title>
		</media:content>
	</item>
		<item>
		<title>Recompile Monitoring using XEvents</title>
		<link>https://simplesqlserver.com/2016/03/07/recompile-monitoring-using-xevents/</link>
					<comments>https://simplesqlserver.com/2016/03/07/recompile-monitoring-using-xevents/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 07 Mar 2016 12:00:58 +0000</pubDate>
				<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[Tuning]]></category>
		<category><![CDATA[XEvents]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=42127</guid>

					<description><![CDATA[How recompiles can hurt you, and how you can hunt them down using sql_statement_recompile.]]></description>
										<content:encoded><![CDATA[<p>Recompiles can be a hidden bottleneck on your server, and it may not be too obvious.  I should know, it happened to me.  Unfortunately for me, that was before I was a proponent of tracing or using XEvents to watch for recompiles.  Fortunately for you, the experience changed me, and I enjoy sharing my experiences.</p>
<p>What happened to me was a proc that was called WAY too often was taking too long cumulatively.  I knew the true root cause of my issue was in the application which was calling the proc once per row, often for about hundred rows, when loading a pretty important page on a web application.  However, I just found out the service desk has been getting complaints for a while, and asking the development team to help me rewrite how the page works ended in an answer that didn&#8217;t even sound like now.  Well, I guess &#8220;no&#8221; does kinda sound like &#8220;now&#8221;&#8230;</p>
<p>The proc was pretty simple, taking on average about 0.150 seconds to run.  Sounds great until you remember that it&#8217;s being called about a hundred times for a page load on a web application.  100 * 0.15 = 15 seconds for the page to load, not counting anything else running.  Users were threatening to not be users anymore even though they were happy with most other aspects.</p>
<p>I got into it, reworded the query a little, created a perfect index for it, and it got it down to 0.100 seconds to run.  This was a large concession on my part because I HATE perfect indexes for a single query, and feel strongly that indexes should focus more on being reusable by many queries.  However, even that only got the web page to load in 10 seconds.</p>
<p>Traces of the proc confused me.  The statements were running in 0.010 seconds, and there was a 0.090 second gap between runs.  That gap was 9 of my 10 seconds.  At 10 seconds users avoid a website, but at 1 second users have to be prompted to say it&#8217;s not as fast as they&#8217;d like.</p>
<p>It took me longer than I&#8217;d like to admit to figure out that every run was kicking off a recompile that took 0.090 seconds.  This is the point where I started tracing for the recompile reason, and found out it was because statistics were being updated.  However, there were no stats in my database that were updated in the last 15 minutes, so I would have never guessed this was the reason without tracing on this event.</p>
<p>This proc loaded data into a temp table, then did some processing on that temp table, and I figured out that there were enough rows being loaded into the temp table to update the stats on it.  Removing the temp table was a disaster, it raised my execution time by 0.010 seconds.  Yeah, &#8220;disaster&#8221; can be relative&#8230;</p>
<p>Then I came up with a very odd solution that I&#8217;ve never seen or heard of before that point, and I haven&#8217;t used much since that point.  The temp table was swapped out for a table variable.  No stats to be updated, so no recompile.  Although it assumed there would only be one record in the table variable (it always does), the execution plan was the same.  The only difference was the total 0.100 second duration was dropped to 0.010 after losing the 0.090 recompile.</p>
<p>User experience was now a web page that loaded up in just over 1 second, with most of it still being this query.  There wasn&#8217;t anything more I could do except wait until the page could be rewritten by development where I could rewrite it to get all the results at once.  That process took time, and customers were not impatiently watching to see when it would happen.</p>
<p>I wrote this talking about the users, and users care about duration.  However, that&#8217;s not the whole story.  The 0.090 recompile was pure CPU time, it happening 100 times per page call added up to 9 seconds of pure CPU time, and the users having to use this page hundreds of times a day added up to a noticeable impact on the server performance when it went away.  Now I admit that the users only used this when they HAD to instead of when they should have, so it was balanced out a little by the users opening the page about twice as often.  Even then, it was still obvious that it was gone when comparing long-term CPU usage.</p>
<h2>The Management Response</h2>
<p>Side note for all of you managers out there&#8230;  My manager pulled me aside the next week saying &#8220;step into my office for a second.&#8221;.  I admit, that statement always makes you nervous, even when you KNOW everything is going great.  He proceeded to tell me that before I started with the company they pulled developers and a DBA into a task force that spent tons of money to improve performance, upgrading servers, upgrading the SAN, upping the WAN speed, and looking into the database.  Then he said that I did more in one day than they did in 6 months without spending any money, and that I should take my wife out for a nice dinner and bring him the receipt.</p>
<p>Now I know that the hardware upgrades helped out, that I only helped a single process that day, and many other things that could belittle what happened.  I also know that spending $100 was very minor in terms of the effect that change made for the company.  That&#8217;s not how I saw things.</p>
<p>What I saw was a response that stuck with me, a lot.  Management stepped back to recognize a difference someone made, and made sure they knew they were appreciated.  Then took it a step further bringing the family into it, having my wife feeling proud and appreciated at the same time.</p>
<p>I know the constraints management is under and all the things they can&#8217;t do.  That being said, the only thing that frustrates me about this situation is that I don&#8217;t see others in management taking the same approach.  I swore from that point that if I ever became a manager my one odd request would be that I would want a very small budget in writing to use at my digression.  Small things to show appreciation go a long way.</p>
<h2>My Challenge</h2>
<p>The challenging part for me, and where I felt I didn&#8217;t do as well as I should have done, is that this flew under my radar.  I was tracing for anything that took over 1 second duration and was looking at the most expensive queries all the time.  However, this one took 0.150 seconds and never accumulated time in the plan cache due to the recompiles.</p>
<p>Here&#8217;s the thing, I pride myself on being specialized in database performance, so I feel I should have seen this before having someone in customer service casually mention it to me in conversation.  It wasn&#8217;t even a request to look into it, just mentioning he just got off a call with the customer about the page, and that the page never worked right with no one being able to fix it.</p>
<p>After finding and resolving the root cause of the proc, I also wanted to find and resolve how this flew under my radar.  This came up to two primary responses.  First, the page was taking too long as a whole, so I requested stats on a per-page basis &#8211; how long it took to load each time and how long cumulatively.  Second, I started watching for recompiles from time to time.  Either one of these would have led me to this issue well before that point.</p>
<p>This post is not about monitoring performance on a website, so, although it&#8217;s a very important thing to do, I won&#8217;t be mentioning that part again.</p>
<h2>Monitoring Recompiles</h2>
<p>This post is about recompiles, how they can drag you down, and how you can find out about them.  I covered how they drug me down, and in past posts I casually mentioned you should use server-side traces or XEvents to monitor them.  Then I left it up to you to figure out how.  Lets try a different approach today.</p>
<p>Recompiles aren&#8217;t something I&#8217;m watching for constantly, so I&#8217;m not going to run any kind of monitoring constantly for them.  What I&#8217;ll do is run an XEvent session for 24 hours and analyze what it picked up.  If it didn&#8217;t recompile often that day it almost definitely does not belong on my priority list, so this works for me.</p>
<p>Here&#8217;s my session.  It starts immediately, writes to five 10MB rollover files, and will not start the next time your services restart.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">IF </span><span style="color:gray;">EXISTS (</span><span style="color:blue;">SELECT </span>1 <span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">server_event_sessions </span><span style="color:blue;">WHERE </span>name <span style="color:gray;">= </span><span style="color:red;">'Recompiles'</span><span style="color:gray;">)
	</span><span style="color:blue;">DROP EVENT SESSION </span>[Recompiles] <span style="color:blue;">ON SERVER</span><span style="color:gray;">;
</span><span style="color:blue;">GO

CREATE EVENT SESSION </span>[Recompiles] <span style="color:blue;">ON SERVER 
ADD EVENT </span>sqlserver<span style="color:gray;">.</span>sql_statement_recompile
<span style="color:gray;">(	</span><span style="color:blue;">SET </span>collect_object_name<span style="color:gray;">=(</span>1<span style="color:gray;">)
		, </span>collect_statement<span style="color:gray;">=(</span>1<span style="color:gray;">)
	</span><span style="color:blue;">ACTION
		</span><span style="color:gray;">(</span>sqlserver<span style="color:gray;">.</span>database_id
			<span style="color:gray;">, </span>sqlserver<span style="color:gray;">.</span><span style="color:blue;">database_name
			</span><span style="color:gray;">, </span>sqlserver<span style="color:gray;">.</span>session_id
			<span style="color:gray;">, </span>sqlserver<span style="color:gray;">.</span>sql_text
			<span style="color:gray;">, </span>sqlserver<span style="color:gray;">.</span>username<span style="color:gray;">))
	</span><span style="color:blue;">ADD TARGET </span>package0<span style="color:gray;">.</span>event_file
		<span style="color:gray;">(</span><span style="color:blue;">SET filename</span><span style="color:gray;">=</span><span style="color:red;">N'Recompiles'
			</span><span style="color:gray;">, </span>max_file_size<span style="color:gray;">=(</span>10<span style="color:gray;">)
)
</span><span style="color:blue;">WITH 
	</span><span style="color:gray;">(</span>MAX_MEMORY<span style="color:gray;">=</span>4096 KB
		<span style="color:gray;">, </span>EVENT_RETENTION_MODE<span style="color:gray;">=</span>ALLOW_MULTIPLE_EVENT_LOSS
		<span style="color:gray;">, </span>MAX_DISPATCH_LATENCY<span style="color:gray;">=</span>30 SECONDS
		<span style="color:gray;">, </span>MAX_EVENT_SIZE<span style="color:gray;">=</span>0 KB
		<span style="color:gray;">, </span>MEMORY_PARTITION_MODE<span style="color:gray;">=</span><span style="color:blue;">NONE
		</span><span style="color:gray;">, </span>TRACK_CAUSALITY<span style="color:gray;">=</span><span style="color:blue;">OFF
		</span><span style="color:gray;">, </span>STARTUP_STATE<span style="color:gray;">=</span><span style="color:blue;">OFF</span><span style="color:gray;">)
</span><span style="color:blue;">GO

ALTER EVENT SESSION </span>Recompiles
<span style="color:blue;">ON SERVER
STATE </span><span style="color:gray;">= </span><span style="color:blue;">START</span><span style="color:gray;">;
</span></pre>
<h2>Querying the Recompiles XEvent Session</h2>
<p>Then I&#8217;ll use this query to look at my session.  It&#8217;s a little awkward in two ways.</p>
<p>First, I only leave the session running for a day, but this script needs it to be running to read from it.  The only reason for that is it pulls the file path from the running sessions.  You can get around this by putting the path into the script manually.  As for me, I usually query it and figure out my next steps while it&#8217;s running, then I stop the session.</p>
<p>Second, and this is quite humorous to me, it recompiles due to my temp tables every time it runs.  It&#8217;s a great example of what to ignore because it&#8217;s a low-cost recompile that happens on a seldom-used ad-hoc query where performance is not a major priority.  However, because temp table definitions are cached for proc plans, this goes away if you turn this script into a proc.  I laugh every time&#8230;</p>
<p>When you get the results in the #Queries temp table in the end they&#8217;re what you&#8217;re used to with it just being a table and all the XML out of the way.  The query at the end of the script views them all, but I&#8217;ll play around with it doing aggregations and more.  I encourage you to do the same.  This is also why it drops the table if it exists in the beginning instead of dropping them at the end.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DECLARE 
	</span>@SessionName <span style="color:blue;">SysName 
	</span><span style="color:gray;">, </span>@TopCount <span style="color:blue;">Int </span><span style="color:gray;">= </span>1000
	
<span style="color:blue;">SELECT </span>@SessionName <span style="color:gray;">= </span><span style="color:red;">'Recompile'

</span><span style="color:green;">--SELECT @SessionName = 'system_health'
/* 
SELECT * FROM sys.traces

SELECT  Session_Name = s.name, s.blocked_event_fire_time, s.dropped_buffer_count, s.dropped_event_count, s.pending_buffers
FROM sys.dm_xe_session_targets t
	INNER JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address
WHERE target_name = 'event_file'
--*/

</span><span style="color:blue;">IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#Events'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#Events
<span style="color:blue;">END

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#Queries'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#Queries 
<span style="color:blue;">END

DECLARE </span>@Target_File <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">)
	, </span>@Target_Dir <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">)
	, </span>@Target_File_WildCard <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">)

</span><span style="color:blue;">SELECT </span>@Target_File <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>t<span style="color:gray;">.</span>target_data <span style="color:blue;">as XML</span><span style="color:gray;">).</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'EventFileTarget[1]/File[1]/@name'</span><span style="color:gray;">, </span><span style="color:red;">'NVARCHAR(256)'</span><span style="color:gray;">)
</span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_xe_session_targets </span>t
	<span style="color:gray;">INNER JOIN </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_xe_sessions </span>s <span style="color:blue;">ON </span>s<span style="color:gray;">.</span><span style="color:blue;">address </span><span style="color:gray;">= </span>t<span style="color:gray;">.</span>event_session_address
<span style="color:blue;">WHERE </span>s<span style="color:gray;">.</span>name <span style="color:gray;">= </span>@SessionName
	<span style="color:gray;">AND </span>t<span style="color:gray;">.</span>target_name <span style="color:gray;">= </span><span style="color:red;">'event_file'

</span><span style="color:blue;">SELECT </span>@Target_Dir <span style="color:gray;">= LEFT(</span>@Target_File<span style="color:gray;">, </span><span style="color:magenta;">Len</span><span style="color:gray;">(</span>@Target_File<span style="color:gray;">) - </span><span style="color:magenta;">CHARINDEX</span><span style="color:gray;">(</span><span style="color:red;">'\'</span><span style="color:gray;">, </span><span style="color:magenta;">REVERSE</span><span style="color:gray;">(</span>@Target_File<span style="color:gray;">))) 

</span><span style="color:blue;">SELECT </span>@Target_File_WildCard <span style="color:gray;">= </span>@Target_Dir <span style="color:gray;">+ </span><span style="color:red;">'\'  </span><span style="color:gray;">+ </span>@SessionName <span style="color:gray;">+ </span><span style="color:red;">'_*.xel'

</span><span style="color:green;">--SELECT @Target_File_WildCard
</span><span style="color:blue;">CREATE TABLE </span>#Events 
<span style="color:gray;">(
	</span>event_data_XML <span style="color:blue;">XML
</span><span style="color:gray;">)

</span><span style="color:blue;">INSERT INTO </span>#Events 
<span style="color:blue;">SELECT TOP </span><span style="color:gray;">(</span>@TopCount<span style="color:gray;">) </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>event_data <span style="color:blue;">AS XML</span><span style="color:gray;">) </span><span style="color:blue;">AS </span>event_data_XML
<span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span>fn_xe_file_target_read_file<span style="color:gray;">(</span>@Target_File_WildCard<span style="color:gray;">, null, null, null) </span><span style="color:blue;">AS </span>F
<span style="color:blue;">ORDER BY </span><span style="color:magenta;">File_name </span><span style="color:blue;">DESC
	</span><span style="color:gray;">, </span>file_offset <span style="color:blue;">DESC 

SELECT  </span>EventType <span style="color:gray;">= </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'(event/@name)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(50)'</span><span style="color:gray;">)
	, </span>ObjectName <span style="color:gray;">= </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/data  [@name=''object_name'']/value)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'sysname'</span><span style="color:gray;">)
	, </span>ObjectType <span style="color:gray;">= </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/data  [@name=''object_type'']/text)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'sysname'</span><span style="color:gray;">)
	, </span>UserName <span style="color:gray;">= </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/action  [@name=''username'']/value)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'sysname'</span><span style="color:gray;">)
	, </span>Statement_Text <span style="color:gray;">= </span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span><span style="color:magenta;">ISNULL</span><span style="color:gray;">(</span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/data  [@name=''statement'']/value)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'NVARCHAR(4000)'</span><span style="color:gray;">), </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/data  [@name=''batch_text''     ]/value)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'NVARCHAR(4000)'</span><span style="color:gray;">)), </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/data[@name=''wait_type'']/text)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'NVARCHAR(60)'</span><span style="color:gray;">))
	, </span>Recompile_Cause <span style="color:gray;">= </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/data  [@name=''recompile_cause'']/text)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'sysname'</span><span style="color:gray;">)
	, </span><span style="color:blue;">TimeStamp </span><span style="color:gray;">= </span><span style="color:magenta;">DateAdd</span><span style="color:gray;">(</span><span style="color:magenta;">Hour</span><span style="color:gray;">, </span><span style="color:magenta;">DateDiff</span><span style="color:gray;">(</span><span style="color:magenta;">Hour</span><span style="color:gray;">, </span><span style="color:magenta;">GetUTCDate</span><span style="color:gray;">(), </span><span style="color:magenta;">GetDate</span><span style="color:gray;">()) , </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'(event/@timestamp)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(50)'</span><span style="color:gray;">) </span><span style="color:blue;">as DateTime2</span><span style="color:gray;">))
	, </span>SPID <span style="color:gray;">= </span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/action  [@name=''session_id'']/value)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'BIGINT'</span><span style="color:gray;">)
	, </span><span style="color:blue;">Database_Name </span><span style="color:gray;">= </span><span style="color:magenta;">DB_Name</span><span style="color:gray;">(</span>event_data_XML<span style="color:gray;">.</span><span style="color:blue;">value </span><span style="color:gray;">(</span><span style="color:red;">'(/event/action  [@name=''database_id'']/value)[1]'</span><span style="color:gray;">, </span><span style="color:red;">'BIGINT'</span><span style="color:gray;">))
	, </span>EventDetails <span style="color:gray;">= </span>event_data_XML 
<span style="color:blue;">INTO </span>#Queries
<span style="color:blue;">FROM </span>#Events

<span style="color:blue;">SELECT </span>q<span style="color:gray;">.</span>EventType
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span>ObjectType
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span>ObjectName
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span>Statement_Text
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span>Recompile_Cause
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span><span style="color:blue;">TimeStamp
	</span><span style="color:gray;">, </span>q<span style="color:gray;">.</span>SPID
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span>UserName
	<span style="color:gray;">, </span>q<span style="color:gray;">.</span><span style="color:blue;">Database_Name
	</span><span style="color:gray;">, </span>q<span style="color:gray;">.</span>EventDetails
<span style="color:blue;">FROM </span>#Queries q
<span style="color:blue;">ORDER BY TimeStamp DESC 
</span></pre>
<h2>Hope It Helps</h2>
<p>Let me know how this helps.  If anything can be done to make my approach or my scripts better, I&#8217;m always open to improvement.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/03/07/recompile-monitoring-using-xevents/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">42127</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>I/O Baseline Comparison – Idera Diagnostic Manager</title>
		<link>https://simplesqlserver.com/2016/02/29/io-baseline-comparison-idera-diagnostic-manager/</link>
					<comments>https://simplesqlserver.com/2016/02/29/io-baseline-comparison-idera-diagnostic-manager/#respond</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 29 Feb 2016 12:00:19 +0000</pubDate>
				<category><![CDATA[Idera]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=41844</guid>

					<description><![CDATA[What servers need the most attention when it comes to I/O?  When I increase the memory on a server what effect does it have on I/O?  What was it before the change and what was it afterwards? These are tough questions, and I have a script that I hope will help you out. Most monitoring [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>What servers need the most attention when it comes to I/O?  When I increase the memory on a server what effect does it have on I/O?  What was it before the change and what was it afterwards?</p>
<p>These are tough questions, and I have a script that I hope will help you out.</p>
<p>Most monitoring tools, including Idera, are great at looking at what a single server is doing right now, but not so much with comparing performance and loads between multiple larger windows of time and seeing which servers are doing the most work.  So I wrote a script to see how much I/O load I&#8217;m doing on each server and compare that to how much I did in that same time window in previous weeks.</p>
<p>Your servers will vary, a lot.  They&#8217;ll vary from my servers, and they&#8217;ll vary from each other as well.  Depending on the workload, specifically the difference between OLTP and OLAP, higher I/O may be expected and additional memory will have less of an impact.  For OLAP, if you have 32 GB of memory and read a 64 GB table followed by a different 92 GB table then it&#8217;s all physical reads.  If you up your memory to 48 GB, they&#8217;re still all physical reads.  The cost to up this server (or VM) is the same as the cost to upgrade an OLTP server from 32 GB to 48 GB, but the story will end quite a bit differently there.</p>
<p>The problem is that this change, like any other change, should be measured before, measured afterwards, then have the costs of the change analyzed and justified.  The results you&#8217;ll see below coupled with my <a href="https://simplesqlserver.com/2016/02/22/wait-stats-baseline-comparison-idera-diagnostic-manager/" target="_blank"><span style="color:#0066cc;">Wait Stats Baseline Comparison using Idera</span></a> post will help you figure out where to make a change like that and how to measure it.</p>
<p>There are other reasons you&#8217;d want to look at this data as well.  Even a new index that dramatically dropped the physical reads for one query could have the opposite effect on your server as a whole.  The point is, even something very straightforward needs to be verified that it&#8217;s doing what you expect it to.  Looking at the details you see in the canned reports is great, but so is the ability to stand back and look at the big picture that I&#8217;ll get into here.</p>
<p>The solutions I&#8217;ve put into place using this type of query to varied from query tuning, timing of larger processes, adding memory like I mentioned above, buying faster disks, and many other options including acknowledging we were doing a ton of reads and leaving it be.</p>
<h2>My Default Monitoring Message</h2>
<p>I&#8217;ll copy/paste this section on multiple posts, so feel free to skip it:</p>
<p>The biggest issue is the word &#8220;before&#8221;.  If you weren&#8217;t watching your servers before then you have nothing to compare the current state of your server to.  Also, if you&#8217;re watching it consistently then you&#8217;ll be able to make an apples to apples comparison.</p>
<p>Your production servers should have some kind of monitoring software in place, and the monitoring software is capturing this type of information to help make all the pretty charts and graphs you see through the GUI.  You need this software to run so you can get alerted when things go wrong, but pulling this data will just make in more valuable.</p>
<p>Monitoring software doesn&#8217;t perform magic, so you can pull this data on your own if you don&#8217;t have any.  However, that&#8217;s taking on a lot of responsibility, time, and effort.  If something is collecting the data already, take advantage of that.</p>
<p>For me, I use Idera Diagnostic Manager, so my queries are going to look at the data collected using this tool.  Other monitoring tools are going to collect the same basic data in a database you can read.</p>
<p>If anyone from one of these companies wants to provide me with scripts that fill the same need, I&#8217;ll tack them on here and give you credit for writing them.  I can only write against what I can test against, it&#8217;s nothing personal.</p>
<h2>I/O Differences Script</h2>
<p>What I look for are the true apples-to-apples comparisons, so I&#8217;m typically looking at a longer time range (I like 24 hours) during peak hours (12:00 to 21:00 UTC on weekdays for me, yours will differ, even server-to-server) comparing this week to the last couple weeks.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DECLARE </span>@StartTime <span style="color:blue;">DateTime
	</span><span style="color:gray;">, </span>@EndTime <span style="color:blue;">DateTime
	</span><span style="color:gray;">, </span>@InstanceName <span style="color:blue;">sysname
	</span><span style="color:gray;">, </span>@Weekdays <span style="color:blue;">bit
	</span><span style="color:gray;">, </span>@BusinessHours <span style="color:blue;">bit
		
SET </span>@EndTime <span style="color:gray;">= </span><span style="color:magenta;">GetUTCDate</span><span style="color:gray;">()
</span><span style="color:blue;">SET </span>@StartTime <span style="color:gray;">= </span><span style="color:magenta;">DateAdd</span><span style="color:gray;">(</span><span style="color:magenta;">Hour</span><span style="color:gray;">, -</span>24<span style="color:gray;">, </span>@EndTime<span style="color:gray;">)
</span><span style="color:blue;">SET </span>@InstanceName <span style="color:gray;">= NULL </span><span style="color:green;">--Do 'Server\Instance' for individual server
</span><span style="color:blue;">SET </span>@Weekdays <span style="color:gray;">= </span>1
<span style="color:blue;">SET </span>@BusinessHours <span style="color:gray;">= </span>1

<span style="color:blue;">SELECT </span>S<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>StartTime <span style="color:gray;">= </span>@StartTime 
	<span style="color:gray;">, </span>EndTime <span style="color:gray;">= </span>@EndTime
	<span style="color:gray;">, </span>Reads_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>ReadAhead_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>ReadAheadPages<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>Writes_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageWrites<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>Lookups_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>PctPhysical <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) / </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) * </span>100 <span style="color:blue;">as DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>AvgPLE <span style="color:gray;">= </span><span style="color:magenta;">Avg</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLifeExpectancy<span style="color:gray;">)
	, </span>AvgCache_MB <span style="color:gray;">= </span><span style="color:magenta;">AVG</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>BufferCacheSizeInKilobytes<span style="color:gray;">)/</span>1024
<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>MonitoredSQLServers S
	<span style="color:gray;">INNER JOIN </span>SQLdmRepository<span style="color:gray;">..</span>ServerStatistics St <span style="color:blue;">ON </span>S<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>St<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">AND </span>@EndTime 
	<span style="color:gray;">AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:blue;">WEEKDAY</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>2 <span style="color:gray;">and </span>6 <span style="color:gray;">or </span>@Weekdays <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:magenta;">HOUR</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>12 <span style="color:gray;">and </span>21 <span style="color:gray;">OR </span>@BusinessHours <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>S<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>@InstanceName<span style="color:gray;">) OR </span>@InstanceName <span style="color:gray;">IS NULL)
</span><span style="color:blue;">GROUP BY </span>S<span style="color:gray;">.</span>InstanceName
<span style="color:blue;">ORDER BY </span>4 <span style="color:blue;">DESC

SELECT </span>@StartTime <span style="color:gray;">= </span>@StartTime <span style="color:gray;">- </span>7 
	<span style="color:gray;">, </span>@EndTime <span style="color:gray;">= </span>@EndTime <span style="color:gray;">- </span>7

<span style="color:blue;">SELECT </span>S<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>StartTime <span style="color:gray;">= </span>@StartTime 
	<span style="color:gray;">, </span>EndTime <span style="color:gray;">= </span>@EndTime
	<span style="color:gray;">, </span>Reads_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>ReadAhead_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>ReadAheadPages<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>Writes_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageWrites<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>Lookups_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>PctPhysical <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) / </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) * </span>100 <span style="color:blue;">as DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>AvgPLE <span style="color:gray;">= </span><span style="color:magenta;">Avg</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLifeExpectancy<span style="color:gray;">)
	, </span>AvgCache_MB <span style="color:gray;">= </span><span style="color:magenta;">AVG</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>BufferCacheSizeInKilobytes<span style="color:gray;">)/</span>1024
<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>MonitoredSQLServers S
	<span style="color:gray;">INNER JOIN </span>SQLdmRepository<span style="color:gray;">..</span>ServerStatistics St <span style="color:blue;">ON </span>S<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>St<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">AND </span>@EndTime 
	<span style="color:gray;">AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:blue;">WEEKDAY</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>2 <span style="color:gray;">and </span>6 <span style="color:gray;">or </span>@Weekdays <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:magenta;">HOUR</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>12 <span style="color:gray;">and </span>21 <span style="color:gray;">OR </span>@BusinessHours <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>S<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>@InstanceName<span style="color:gray;">) OR </span>@InstanceName <span style="color:gray;">IS NULL)
</span><span style="color:blue;">GROUP BY </span>S<span style="color:gray;">.</span>InstanceName
<span style="color:blue;">ORDER BY </span>4 <span style="color:blue;">DESC

SELECT </span>@StartTime <span style="color:gray;">= </span>@StartTime <span style="color:gray;">- </span>7 
	<span style="color:gray;">, </span>@EndTime <span style="color:gray;">= </span>@EndTime <span style="color:gray;">- </span>7

<span style="color:blue;">SELECT </span>S<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>StartTime <span style="color:gray;">= </span>@StartTime 
	<span style="color:gray;">, </span>EndTime <span style="color:gray;">= </span>@EndTime
	<span style="color:gray;">, </span>Reads_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>ReadAhead_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>ReadAheadPages<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>Writes_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageWrites<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>Lookups_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>PctPhysical <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) / </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) * </span>100 <span style="color:blue;">as DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>AvgPLE <span style="color:gray;">= </span><span style="color:magenta;">Avg</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLifeExpectancy<span style="color:gray;">)
	, </span>AvgCache_MB <span style="color:gray;">= </span><span style="color:magenta;">AVG</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>BufferCacheSizeInKilobytes<span style="color:gray;">)/</span>1024
<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>MonitoredSQLServers S
	<span style="color:gray;">INNER JOIN </span>SQLdmRepository<span style="color:gray;">..</span>ServerStatistics St <span style="color:blue;">ON </span>S<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>St<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">AND </span>@EndTime 
	<span style="color:gray;">AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:blue;">WEEKDAY</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>2 <span style="color:gray;">and </span>6 <span style="color:gray;">or </span>@Weekdays <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:magenta;">HOUR</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>12 <span style="color:gray;">and </span>21 <span style="color:gray;">OR </span>@BusinessHours <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>S<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>@InstanceName<span style="color:gray;">) OR </span>@InstanceName <span style="color:gray;">IS NULL)
</span><span style="color:blue;">GROUP BY </span>S<span style="color:gray;">.</span>InstanceName
<span style="color:blue;">ORDER BY </span>4 <span style="color:blue;">DESC


SELECT </span>@StartTime <span style="color:gray;">= </span>@StartTime <span style="color:gray;">- </span>7 
	<span style="color:gray;">, </span>@EndTime <span style="color:gray;">= </span>@EndTime <span style="color:gray;">- </span>7

<span style="color:blue;">SELECT </span>S<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>StartTime <span style="color:gray;">= </span>@StartTime 
	<span style="color:gray;">, </span>EndTime <span style="color:gray;">= </span>@EndTime
	<span style="color:gray;">, </span>Reads_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>ReadAhead_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>ReadAheadPages<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>Writes_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageWrites<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>Lookups_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>PctPhysical <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageReads<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) / </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLookups<span style="color:gray;">)/</span>128<span style="color:gray;">/</span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) * </span>100 <span style="color:blue;">as DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
	, </span>AvgPLE <span style="color:gray;">= </span><span style="color:magenta;">Avg</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>PageLifeExpectancy<span style="color:gray;">)
	, </span>AvgCache_MB <span style="color:gray;">= </span><span style="color:magenta;">AVG</span><span style="color:gray;">(</span>St<span style="color:gray;">.</span>BufferCacheSizeInKilobytes<span style="color:gray;">)/</span>1024
<span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">..</span>MonitoredSQLServers S
	<span style="color:gray;">INNER JOIN </span>SQLdmRepository<span style="color:gray;">..</span>ServerStatistics St <span style="color:blue;">ON </span>S<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>St<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">AND </span>@EndTime 
	<span style="color:gray;">AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:blue;">WEEKDAY</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>2 <span style="color:gray;">and </span>6 <span style="color:gray;">or </span>@Weekdays <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">DATEPART</span><span style="color:gray;">(</span><span style="color:magenta;">HOUR</span><span style="color:gray;">, </span>UTCCollectionDateTime<span style="color:gray;">) BETWEEN </span>12 <span style="color:gray;">and </span>21 <span style="color:gray;">OR </span>@BusinessHours <span style="color:gray;">= </span>0<span style="color:gray;">)
	AND (</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>S<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>@InstanceName<span style="color:gray;">) OR </span>@InstanceName <span style="color:gray;">IS NULL)
</span><span style="color:blue;">GROUP BY </span>S<span style="color:gray;">.</span>InstanceName
<span style="color:blue;">ORDER BY </span>4 <span style="color:blue;">DESC
</span></pre>
<h2>The Results</h2>
<p>This is, by default, going to show you your biggest I/O users that are being monitored.  It&#8217;s great to know, but it&#8217;s not the whole picture.  Some servers are going to be almost pure OLTP, others are going to be almost pure OLAP, and most will be some kind of mix.  The more OLAP you do, the more physical reads (Reads_GB and ReadAhead_GB) should be expected.  It&#8217;s really not a bad thing, it&#8217;s how your databases are used.</p>
<p>Then there&#8217;s the realistic version of the users&#8217; expectation (not always the version they tell you) to consider.  Are the queries coming back as fast as they should?  Large annual reports can take a bit, but a website that takes more than a couple seconds isn&#8217;t going to be used.</p>
<p>This report shows the load you&#8217;re throwing at the disks, but user expectations are measured in seconds, not gigabytes.  Yeah, more gigabytes means more seconds, but it&#8217;s not like there&#8217;s a solid ratio.</p>
<p>That being said, the true value of this information is when you combine it with the type of server it is, the reasonable expectations of the users, the amount of time the server is waiting on I/O, and so much more.  To start you out, look at the <a href="https://simplesqlserver.com/2016/02/22/wait-stats-baseline-comparison-idera-diagnostic-manager/" target="_blank">Wait Stats Baseline Comparison using Idera</a>, too.</p>
<p>If there are OLTP servers that stand out as doing a ton of physical reads and the wait stats show that it&#8217;s an issue as well, look into more memory and more time for tuning.  Take it a step further and use all of the trending info (not just this report) to split out I/O load and plan for faster disks.  There&#8217;s a lot of potential to use this info to do a lot of good.</p>
<h2>My Idera Scripts</h2>
<p>I keep all the scripts that query Idera Diagnostic Manager in one place, on my <a href="https://simplesqlserver.com/ideradmscripts/" target="_blank"><span style="color:#0066cc;">Idera Diagnostic Manager Scripts</span></a> page.  Before this script had it&#8217;s own post, it was posted there.  As I make posts for each script, that page will live on as a directory of what scripts I have to get more use out of this product.</p>
<p>This is the first time I&#8217;m doing something like this, but I&#8217;ll eventually make other pages that pull together topics like this as well.  It feels good to find a great way to take blogging to the next level like this.</p>
<p>Speaking of taking it to the next level, let me know your thoughts on this script, others you&#8217;d like to see, and anything else in the comments below.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/02/29/io-baseline-comparison-idera-diagnostic-manager/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">41844</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Wait Stats Baseline Comparison – Idera Diagnostic Manager</title>
		<link>https://simplesqlserver.com/2016/02/22/wait-stats-baseline-comparison-idera-diagnostic-manager/</link>
					<comments>https://simplesqlserver.com/2016/02/22/wait-stats-baseline-comparison-idera-diagnostic-manager/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 22 Feb 2016 12:00:59 +0000</pubDate>
				<category><![CDATA[Idera]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=41873</guid>

					<description><![CDATA[Database servers have to wait on different resources, and these waits are huge to the performance of SQL Server.  Sometimes something changes without our knowledge and is running differently than normal, this is the first place you look to see what&#8217;s going on.  On the other side, you may have made a change that could [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Database servers have to wait on different resources, and these waits are huge to the performance of SQL Server.  Sometimes something changes without our knowledge and is running differently than normal, this is the first place you look to see what&#8217;s going on.  On the other side, you may have made a change that could have been anything from increasing memory, tuning a query, moving to different hardware, changing indexes, etc..  All of these things should reduce waits, but how effective were they?</p>
<p>The trick is to know what things looked like before so you have something to compare it to.  If you know what it looked like before then you&#8217;ll know how much of a difference you made.</p>
<h2>My Default Monitoring Message</h2>
<p>I&#8217;ll copy/paste this section on multiple posts, so feel free to skip it if you&#8217;ve seen it before.</p>
<p>The biggest issue is the word &#8220;before&#8221;.  If you weren&#8217;t watching your servers before then you have nothing to compare the current state of your server to.  Also, if you&#8217;re watching it consistently then you&#8217;ll be able to make an apples to apples comparison.</p>
<p>Your production servers should have some kind of monitoring software in place, and the monitoring software is already capturing this type of information to help make all the pretty charts and graphs you see through the GUI.  You need this software to run so you can get alerted when things go wrong, and pulling this data will just make it more valuable.</p>
<p>Monitoring software doesn&#8217;t perform magic, so you can pull this data on your own if you don&#8217;t have any.  However, that&#8217;s taking on a lot of responsibility, time, and effort.  If something is collecting the data already, take advantage of that.</p>
<p>For me, I use Idera Diagnostic Manager, so my queries are going to look at the data collected using this tool.  Other monitoring tools are going to collect the same basic data in a database you can read.</p>
<p>If anyone from one of these companies wants to provide me with scripts that fill the same need, I&#8217;ll make a write up on them and give you credit for being awesome supporting your product like that.  I can only write against what I can test against, it&#8217;s nothing personal.</p>
<h2>The Script</h2>
<p>There are 3 parameters at the start, and that&#8217;s typically all I ever change.  A couple things stick out with these parameters.  The times are in UTC, so I default to GetUTCDate().  I want the time range to be at least an hour so I&#8217;m not letting a single query that ran skew the results too much.  Then I&#8217;m also converting my instance name to upper case because Idera DM may very well be your only database that&#8217;s case sensitive, so I cheat by putting everything in upper case.</p>
<p>My upper case trick isn&#8217;t a recommended trick.  It causes scans and possible bad joins.  In this case I know it&#8217;s against small tables and that I don&#8217;t have two servers with the same name using different cases.  It&#8217;s safe here, but not everywhere.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DECLARE </span>@StartTime <span style="color:blue;">DateTime
	</span><span style="color:gray;">, </span>@EndTime <span style="color:blue;">DateTime
	</span><span style="color:gray;">, </span>@InstanceName <span style="color:blue;">sysname
		
SET </span>@EndTime <span style="color:gray;">= </span><span style="color:magenta;">GetUTCDate</span><span style="color:gray;">()
</span><span style="color:blue;">SET </span>@StartTime <span style="color:gray;">= </span><span style="color:magenta;">DateAdd</span><span style="color:gray;">(</span><span style="color:magenta;">Hour</span><span style="color:gray;">, -</span>24<span style="color:gray;">, </span>@EndTime<span style="color:gray;">)
</span><span style="color:blue;">SET </span>@InstanceName <span style="color:gray;">= </span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span><span style="color:red;">'Server\Instance'</span><span style="color:gray;">)

</span><span style="color:blue;">IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#IgnoredWaits'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
    DROP TABLE </span>#IgnoredWaits
<span style="color:blue;">END

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#Now'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
    DROP TABLE </span>#Now
<span style="color:blue;">END

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#Yesterday'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
    DROP TABLE </span>#Yesterday
<span style="color:blue;">END

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#LastWeek'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
    DROP TABLE </span>#LastWeek
<span style="color:blue;">END

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#TwoWeeks'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
    DROP TABLE </span>#TwoWeeks
<span style="color:blue;">END

CREATE TABLE </span>#IgnoredWaits <span style="color:gray;">(</span>Wait_Type <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>60<span style="color:gray;">) </span><span style="color:blue;">PRIMARY KEY</span><span style="color:gray;">) 

</span><span style="color:green;">--Values taken from Paul Randal of SQLskills
--http://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/
</span><span style="color:blue;">INSERT INTO </span>#IgnoredWaits 
<span style="color:blue;">SELECT </span><span style="color:red;">N'BROKER_EVENTHANDLER'            
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'BROKER_RECEIVE_WAITFOR'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'BROKER_TASK_STOP'               
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'BROKER_TO_FLUSH'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'BROKER_TRANSMITTER'             
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'CHECKPOINT_QUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'CHKPT'                          
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'CLR_AUTO_EVENT'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'CLR_MANUAL_EVENT'               
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'CLR_SEMAPHORE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'DBMIRROR_DBM_EVENT'             
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'DBMIRROR_EVENTS_QUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'DBMIRROR_WORKER_QUEUE'          
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'DBMIRRORING_CMD'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'DIRTY_PAGE_POLL'                
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'DISPATCHER_QUEUE_SEMAPHORE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'EXECSYNC'                       
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'FSAGENT'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'FT_IFTS_SCHEDULER_IDLE_WAIT'    
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'FT_IFTSHC_MUTEX'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'HADR_CLUSAPI_CALL'              
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'HADR_FILESTREAM_IOMGR_IOCOMPLETION'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'HADR_LOGCAPTURE_WAIT'           
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'HADR_NOTIFICATION_DEQUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'HADR_TIMER_TASK'                
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'HADR_WORK_QUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'KSOURCE_WAKEUP'                 
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'LAZYWRITER_SLEEP'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'LOGMGR_QUEUE'                   
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'ONDEMAND_TASK_QUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'PWAIT_ALL_COMPONENTS_INITIALIZED'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'QDS_SHUTDOWN_QUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'REQUEST_FOR_DEADLOCK_SEARCH'    
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'RESOURCE_QUEUE'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SERVER_IDLE_CHECK'              
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_BPOOL_FLUSH'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_DBSTARTUP'                
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_DCOMSTARTUP'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_MASTERDBREADY'            
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_MASTERMDREADY'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_MASTERUPGRADED'           
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_MSDBSTARTUP'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_SYSTEMTASK'               
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_TASK'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SLEEP_TEMPDBSTARTUP'            
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SNI_HTTP_ACCEPT'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SP_SERVER_DIAGNOSTICS_SLEEP'    
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SQLTRACE_BUFFER_FLUSH'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'SQLTRACE_WAIT_ENTRIES'          
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'WAIT_FOR_RESULTS'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'WAITFOR'                        
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'WAITFOR_TASKSHUTDOWN'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'WAIT_XTP_HOST_WAIT'             
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'WAIT_XTP_CKPT_CLOSE'            
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'XE_DISPATCHER_JOIN'
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'XE_DISPATCHER_WAIT'             
</span><span style="color:blue;">UNION SELECT </span><span style="color:red;">N'XE_TIMER_EVENT'

</span><span style="color:blue;">SELECT </span>s<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">, </span>WaitTime_Minutes <span style="color:gray;">= </span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>WaitTimeInMilliseconds<span style="color:gray;">)/</span>1000<span style="color:gray;">/</span>60
	<span style="color:gray;">, </span>dStart <span style="color:gray;">= </span><span style="color:magenta;">Min</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
	, </span>dEnd <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
</span><span style="color:blue;">INTO </span>#Now
<span style="color:blue;">FROM </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatisticsDetails wsd
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitTypes t <span style="color:blue;">ON </span>wsd<span style="color:gray;">.</span>WaitTypeID <span style="color:gray;">= </span>t<span style="color:gray;">.</span>WaitTypeID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatistics ws <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>WaitStatisticsID <span style="color:gray;">= </span>wsd<span style="color:gray;">.</span>WaitStatisticsID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>MonitoredSQLServers s <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>s<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>ws<span style="color:gray;">.</span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">and </span>@EndTime
	<span style="color:gray;">AND	</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span>@InstanceName
	<span style="color:gray;">AND </span>t<span style="color:gray;">.</span>WaitType <span style="color:gray;">NOT IN (</span><span style="color:blue;">SELECT </span>Wait_Type <span style="color:magenta;">COLLATE </span>SQL_Latin1_General_CP1_CI_AS <span style="color:blue;">FROM </span>#IgnoredWaits<span style="color:gray;">)
	</span><span style="color:green;">--AND t.WaitType LIKE 'PAGEIO%'
</span><span style="color:blue;">GROUP BY </span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType

<span style="color:blue;">SELECT </span>@StartTime <span style="color:gray;">= </span>@StartTime <span style="color:gray;">- </span>1
	<span style="color:gray;">, </span>@EndTime <span style="color:gray;">= </span>@EndTime <span style="color:gray;">- </span>1

<span style="color:blue;">SELECT </span>s<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">, </span>WaitTime_Minutes <span style="color:gray;">= </span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>WaitTimeInMilliseconds<span style="color:gray;">)/</span>1000<span style="color:gray;">/</span>60
	<span style="color:gray;">, </span>dStart <span style="color:gray;">= </span><span style="color:magenta;">Min</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
	, </span>dEnd <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
</span><span style="color:blue;">INTO </span>#Yesterday
<span style="color:blue;">FROM </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatisticsDetails wsd
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitTypes t <span style="color:blue;">ON </span>wsd<span style="color:gray;">.</span>WaitTypeID <span style="color:gray;">= </span>t<span style="color:gray;">.</span>WaitTypeID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatistics ws <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>WaitStatisticsID <span style="color:gray;">= </span>wsd<span style="color:gray;">.</span>WaitStatisticsID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>MonitoredSQLServers s <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>s<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>ws<span style="color:gray;">.</span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">and </span>@EndTime
	<span style="color:gray;">AND	</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span>@InstanceName	
	<span style="color:gray;">AND </span>t<span style="color:gray;">.</span>WaitType <span style="color:gray;">NOT IN (</span><span style="color:blue;">SELECT </span>Wait_Type <span style="color:magenta;">COLLATE </span>SQL_Latin1_General_CP1_CI_AS <span style="color:blue;">FROM </span>#IgnoredWaits<span style="color:gray;">)
	</span><span style="color:green;">--AND t.WaitType LIKE 'PAGEIO%'
</span><span style="color:blue;">GROUP BY </span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType

<span style="color:blue;">SELECT </span>@StartTime <span style="color:gray;">= </span>@StartTime <span style="color:gray;">- </span>6
	<span style="color:gray;">, </span>@EndTime <span style="color:gray;">= </span>@EndTime <span style="color:gray;">- </span>6

<span style="color:blue;">SELECT </span>s<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">, </span>WaitTime_Minutes <span style="color:gray;">= </span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>WaitTimeInMilliseconds<span style="color:gray;">)/</span>1000<span style="color:gray;">/</span>60
	<span style="color:gray;">, </span>dStart <span style="color:gray;">= </span><span style="color:magenta;">Min</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
	, </span>dEnd <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
</span><span style="color:blue;">INTO </span>#LastWeek
<span style="color:blue;">FROM </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatisticsDetails wsd
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitTypes t <span style="color:blue;">ON </span>wsd<span style="color:gray;">.</span>WaitTypeID <span style="color:gray;">= </span>t<span style="color:gray;">.</span>WaitTypeID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatistics ws <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>WaitStatisticsID <span style="color:gray;">= </span>wsd<span style="color:gray;">.</span>WaitStatisticsID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>MonitoredSQLServers s <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>s<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>ws<span style="color:gray;">.</span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">and </span>@EndTime
	<span style="color:gray;">AND	</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span>@InstanceName	
	<span style="color:gray;">AND </span>t<span style="color:gray;">.</span>WaitType <span style="color:gray;">NOT IN (</span><span style="color:blue;">SELECT </span>Wait_Type <span style="color:magenta;">COLLATE </span>SQL_Latin1_General_CP1_CI_AS <span style="color:blue;">FROM </span>#IgnoredWaits<span style="color:gray;">)
	</span><span style="color:green;">--AND t.WaitType LIKE 'PAGEIO%'
</span><span style="color:blue;">GROUP BY </span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType

<span style="color:blue;">SELECT </span>@StartTime <span style="color:gray;">= </span>@StartTime <span style="color:gray;">- </span>7
	<span style="color:gray;">, </span>@EndTime <span style="color:gray;">= </span>@EndTime <span style="color:gray;">- </span>7

<span style="color:blue;">SELECT </span>s<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">, </span>WaitTime_Minutes <span style="color:gray;">= </span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>WaitTimeInMilliseconds<span style="color:gray;">)/</span>1000<span style="color:gray;">/</span>60
	<span style="color:gray;">, </span>dStart <span style="color:gray;">= </span><span style="color:magenta;">Min</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
	, </span>dEnd <span style="color:gray;">= </span><span style="color:magenta;">Max</span><span style="color:gray;">(</span>UTCCollectionDateTime<span style="color:gray;">)
</span><span style="color:blue;">INTO </span>#TwoWeeks
<span style="color:blue;">FROM </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatisticsDetails wsd
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitTypes t <span style="color:blue;">ON </span>wsd<span style="color:gray;">.</span>WaitTypeID <span style="color:gray;">= </span>t<span style="color:gray;">.</span>WaitTypeID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>WaitStatistics ws <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>WaitStatisticsID <span style="color:gray;">= </span>wsd<span style="color:gray;">.</span>WaitStatisticsID
	<span style="color:gray;">INNER JOIN </span>SQLdmrepository<span style="color:gray;">..</span>MonitoredSQLServers s <span style="color:blue;">ON </span>ws<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>s<span style="color:gray;">.</span>SQLServerID
<span style="color:blue;">WHERE </span>ws<span style="color:gray;">.</span>UTCCollectionDateTime <span style="color:gray;">BETWEEN </span>@StartTime <span style="color:gray;">and </span>@EndTime
	<span style="color:gray;">AND	</span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span>@InstanceName	
	<span style="color:gray;">AND </span>t<span style="color:gray;">.</span>WaitType <span style="color:gray;">NOT IN (</span><span style="color:blue;">SELECT </span>Wait_Type <span style="color:magenta;">COLLATE </span>SQL_Latin1_General_CP1_CI_AS <span style="color:blue;">FROM </span>#IgnoredWaits<span style="color:gray;">)
	</span><span style="color:green;">--AND t.WaitType LIKE 'PAGEIO%'
</span><span style="color:blue;">GROUP BY </span>s<span style="color:gray;">.</span>InstanceName<span style="color:gray;">, </span>t<span style="color:gray;">.</span>WaitType

<span style="color:blue;">SELECT TOP </span>10 n<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>n<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">, </span>Now_Minutes <span style="color:gray;">= </span>n<span style="color:gray;">.</span>WaitTime_Minutes
	<span style="color:gray;">, </span>Yesterday_Minutes <span style="color:gray;">= </span>d1<span style="color:gray;">.</span>WaitTime_Minutes
	<span style="color:gray;">, </span>OneWeek_Minutes <span style="color:gray;">= </span>d7<span style="color:gray;">.</span>WaitTime_Minutes
	<span style="color:gray;">, </span>TwoWeeks_Minutes <span style="color:gray;">= </span>d14<span style="color:gray;">.</span>WaitTime_Minutes  
<span style="color:blue;">FROM </span>#Now n
	<span style="color:gray;">LEFT JOIN </span>#Yesterday d1 <span style="color:blue;">ON </span>n<span style="color:gray;">.</span>WaitType <span style="color:gray;">= </span>d1<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">LEFT JOIN </span>#LastWeek d7 <span style="color:blue;">ON </span>n<span style="color:gray;">.</span>WaitType <span style="color:gray;">= </span>d7<span style="color:gray;">.</span>WaitType
	<span style="color:gray;">LEFT JOIN </span>#TwoWeeks d14 <span style="color:blue;">ON </span>n<span style="color:gray;">.</span>WaitType <span style="color:gray;">= </span>d14<span style="color:gray;">.</span>WaitType 
<span style="color:blue;">ORDER BY </span>n<span style="color:gray;">.</span>WaitTime_Minutes <span style="color:blue;">DESC
</span></pre>
<h2>The Results</h2>
<p>The results pretty much speak for themselves.  What were the top 10 things you waited on recently and how much did you wait on them in the past.  It&#8217;s great to have a view of it where you&#8217;re comparing totals side-by-side.  Seeing the same thing in charts just doesn&#8217;t have the same effect for me.</p>
<p>These numbers are going to have natural variances to them.  Did a user run a couple large queries during one interval but not another?  Was the shared storage under different loads?  Really anything can come up.  The longer the period the more you smooth out these numbers.</p>
<p>So, the real question is what do these waits mean?  I&#8217;m not going to pretend like I can cover all the wait stats, their meanings, and how to affect them in this post.  What I can say is that there are good resources out there for you.</p>
<p>Paul Randal (<a href="http://www.sqlskills.com/blogs/paul/" target="_blank">b</a>|<a href="http://www.twitter.com/paulrandal" target="_blank">t</a>) has the best source I know of on his post <a href="http://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/" target="_blank">Wait Statistics, or please tell me where it hurts</a>.</p>
<h2>CXPACKET</h2>
<p>One wait type I like to talk about, and discredit a bit, is CXPACKET.  This wait type is just saying you used parallelism, nothing more.  I first covered this topic in my post <a href="https://simplesqlserver.com/2014/03/11/worrying-about-cxpacket/" target="_blank">Why worry about CXPACKET</a>.</p>
<p>Think of it in terms of a manager who delegates a large project to multiple employees.  As the employees are hard at work accomplishing the task, the manager goes about their day and records their time as CXPACKET.  The task is typically completed in less overall duration because it was a group effort, but the total amount of work done goes up some because there&#8217;s overhead and small duplicated tasks when you delegate to a group.</p>
<p>That less overall duration could make or break your application, so the idea of having CXPACKET isn&#8217;t necessarily bad (setting MAXDOP = 1 is rarely a good idea).  However, a task being so large that it needed to be delegated to multiple employees may mean it&#8217;s not efficient, making query and index tuning a good approach.  Also, your manager may be too eager to delegate their tasks, so possibly adjust your Cost Threshold for Parallelism as suggested in Jeremiah Peschka&#8217;s (<a href="https://facility9.com/" target="_blank">b</a>|<a href="https://twitter.com/peschkaj" target="_blank">t</a>) post <a href="https://www.brentozar.com/archive/2013/09/five-sql-server-settings-to-change/" target="_blank">Five SQL Server Settings to Change</a>.</p>
<p>As I said before, I can&#8217;t cover all the wait types here, so go to Paul Randal&#8217;s post I suggested above for a better list.</p>
<h2>What I Skipped</h2>
<p>This is looking specifically at WaitTimeInMilliseconds, which is one of two technically correct things to do.  It does not look at ResourceWaitTimeInMilliseconds, which is the other technically correct thing to do.  I wrote it one way and was happy with the results so I didn&#8217;t change it or clutter it with returning both.</p>
<p>Here&#8217;s the difference so you can decide if you want to change it or talk me into changing mine.  It&#8217;s a conversation I&#8217;d enjoy&#8230;I&#8217;m strange like that.</p>
<p>ResourceWaitTimeInMilliseconds is how long you&#8217;re waiting on your actual resource.  If you&#8217;re waiting for a lock, this is how long you waited for that lock to be made so you could get back in line for CPU time.</p>
<p>WaitTimeInMilliseconds also includes the time from when you get in line for CPU time until you&#8217;re using the CPU.  This additional time is called your signal wait time.</p>
<p>The reason I like using WaitTimeInMilliseconds is that the reason you gave up the CPU and had to get back in line is due to the fact you weren&#8217;t ready due to what you were waiting on.  In that way, it&#8217;s the whole picture.</p>
<p>The argument going the other way is that you were only waiting for the resource.  It&#8217;s not the resource&#8217;s fault there was a line all queued up at the CPU when it got there.  Why should I tack on extra time on a non-CPU wait for that?</p>
<p>I&#8217;m undecided on this part, but having the info both ways allows you to figure out the signal wait time on your entire instance and see if you have too much CPU contention.  There&#8217;s no arguing about that being a good thing to know.</p>
<p>Steal my query, make it your own, and write that part the way that works best for you.</p>
<h2>When Idera Finds Out What I&#8217;m Doing&#8230;.</h2>
<p>Someone asked me for queries pulling from Diagnostic Manager, and I posted on Twitter when I made my <a href="https://simplesqlserver.com/ideradmscripts/" target="_blank">Idera Diagnostic Manager Scripts</a> page.  The outcry and rage from this was&#8230;very positive.</p>
<p>Vicky Harp (<a href="https://twitter.com/vickyharp" target="_blank">t</a>), who is now Idera&#8217;s Corporate Strategist wrote pieces of Diagnostic Manager and its schema, took time out of her day to meet with me and talk about the details of querying the data they collect.</p>
<p>Scott Stone (<a href="https://twitter.com/s_r_stone" target="_blank">t</a>) who is Diagnostic Manager&#8217;s Product Manager also jumped in very quickly to see if I&#8217;d like to be a beta tester for them, bringing real power users into the loop to make their products better.</p>
<p>This is the level of support you want from your monitoring software.</p>
<p>While I don&#8217;t officially endorse or recommend any specific software at this time, I do recommend you look for responses like this.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/02/22/wait-stats-baseline-comparison-idera-diagnostic-manager/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">41873</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Shrinking Database Log Files</title>
		<link>https://simplesqlserver.com/2016/02/15/shrinking-database-log-files/</link>
					<comments>https://simplesqlserver.com/2016/02/15/shrinking-database-log-files/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 15 Feb 2016 12:00:35 +0000</pubDate>
				<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[entrylevel]]></category>
		<category><![CDATA[Logs]]></category>
		<category><![CDATA[VLF]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40677</guid>

					<description><![CDATA[When should you shrink your log files and what does it mean to performance?]]></description>
										<content:encoded><![CDATA[<p>Files in SQL Server need to grow as the database grows, and in very specific circumstances need to be shrunk as well with additional maintenance.  There is quite a bit different between log files and data files, so I created a separate post for <a href="https://simplesqlserver.com/2016/01/19/shrinking-database-data-files" target="_blank">Shrinking Database Data Files</a>.</p>
<h2>When should you shrink log files</h2>
<p>First, if a large process that will never happen again caused your log file to grow to a ridiculous size then it&#8217;s reasonable to shrink the file down to about 150% of the most space you expect to use in the next year.  Cleaning this up will also cut down on restore times.</p>
<p>Second, if your log file grew in small increments then you&#8217;re left with a large number of VLFs like I talked about in file growths.  The only way to get rid of these is to shrink your log files down then have them grow in larger increments.  When I do this, I&#8217;m also growing them manually immediately afterwards in the same script.</p>
<h2>What happens when you shrink log files</h2>
<p>The log file will find a group of empty VLFs and delete them.  The end, your file is shrunk.</p>
<p>Of course there&#8217;s a little more to it than that.  The parts I&#8217;m most worried about are why did you need that space in the first place, why do you need the space freed up, and what happens when you need it again?</p>
<p>To understand why you needed it we need to talk about what a log file really is for a second.  It is every change made to your database since the last point-in-time where it didn&#8217;t need all the changes saved off.  This is typically either the oldest open transaction (so it can roll back any change if the transaction fails) or, if your database is in <a href="https://technet.microsoft.com/en-us/library/aa173531(v=sql.80).aspx" target="_blank"><span style="color:#0066cc;">full or bulk-logged recovery</span></a>, the last time you ran a log backup   It can also be due to other functionality such as database mirroring, but you typically have an experienced DBA on hand for that.</p>
<h2>Why is this common to do?</h2>
<p>There is typically at least once in every large database&#8217;s life where the log needs to be shrunk for one of a couple reasons.</p>
<p>Going off of the two reasons above, the first two are because a large process ran that won&#8217;t run again.  The third reason I go over here is because of small VLFs.</p>
<h4>1. Log backups weren&#8217;t set up</h4>
<p>The first large process is when a database is set up in full or bulk-logged recovery, but the log backups weren&#8217;t set up until the drive filled.  This is because the log needs to be backed up before it can be flushed, which means it will grow until it runs out of space.  It&#8217;s common to miss this when the backup plan needs you to specify which databases to back up, as opposed to doing all databases.  Once you take the first log backup it leaves you with a huge log file where you don&#8217;t expect to hit that size again, and it&#8217;s ok to shrink it.</p>
<h4>2. Runaway transaction</h4>
<p>The second large process is when someone runs something that didn&#8217;t go as planned.  A transaction, even an implied transaction for a single statement, stayed open forever, or close enough to forever to cause us a headache.  This caused a ton of excessive growth in the log files, and that space will only ever be used when someone says &#8220;oops&#8221; again.  In this case, evaluate if you&#8217;ll need that space for another process before you shrink because someone will say &#8220;oops&#8221; again someday.  If the drive is dedicated to log files and you only have one large database, consider shrinking it just enough to clear the warnings in your monitoring software.</p>
<h4>3. VLF cleanup</h4>
<p>The final reason is because you have too many VLFs.  When a log file is created or grows it splits up the new space into smaller chunks called Virtual Log Files that SQL Server will cycle through when writing logs.  If the database is growing in small increments it creates tons of these, slowing SQL Server down.  The point here is that you resolve the issue by shrinking the log (delete smaller VLFs), then grow the log to the desired size in larger chunks (create bigger VLFs).  The details on finding what size your VLFs are, what size new VLFs will be, and just about everything else VLF related is in my post <a href="https://simplesqlserver.com/2016/02/08/database-log-vlfs" target="_blank">Database Log VLFs</a>.</p>
<h2>What happens when log files grow?</h2>
<p>If you&#8217;re going to shrink a log file then you&#8217;re increasing the chances that it will have to grow again, so we need to talk about growth for a second.</p>
<p>When a data file grows it can often take advantage of instant file initialization where it just reserves unallocated space on the disk, making it a quick process.  Without this being turned on or if you&#8217;re using Transparent Data Encryption, SQL Server would have to zero out the disks to get rid of data that used to be there.  Kimberly Tripp (<a href="http://www.sqlskills.com/blogs/kimberly" target="_blank">b</a>|<a href="http://twitter.com/KimberlyLTripp" target="_blank">t</a>) goes into a lot more details than this in her post <a href="http://www.sqlskills.com/blogs/kimberly/instant-initialization-what-why-and-how/" target="_blank">Instant Initializations &#8211; What, Why and How?</a>.</p>
<p>Log files can&#8217;t use this feature.  They write those 0&#8217;s every time the log grows, and SQL Server is patient enough to wait for that to happen.  Here&#8217;s how that went over the last time I overheard one of my users &#8220;discussing&#8221; this with my biggest server.</p>
<blockquote><p>SQL Server: My log&#8217;s full, hold on while I get some more space.</p>
<p>User: I don&#8217;t care, I just want my query to finish.</p>
<p>SQL Server: I&#8217;m told to grab 8,000 MB at a time, please be patient.</p>
<p>User: Not a problem, there&#8217;s 8,000 MB right there, please hurry.</p>
<p>SQL Server: I only use clean space, hold on while I ask the OS to write about 8,388,608,000 zeroes for me.  This isn&#8217;t a problem, I&#8217;m patient.</p>
<p>User: What?  NO!  Well, at least that gives me enough time to open a ticket for the DBA.</p>
<p>Ticketing System: Hello again, I haven&#8217;t seen you for several minutes.</p></blockquote>
<p>The moral of this story is that you don&#8217;t want autogrowth to happen.  You want it turned on for emergencies and one-time growths, but it&#8217;s better to grow manually in a maintenance window.  Here&#8217;s how that maintenance window conversation goes:</p>
<blockquote><p>DBA: I see you&#8217;ve hit 90% utilization on your log a couple times, how about we add 8,000 MB.</p>
<p>SQL Server: Oh, I didn&#8217;t notice, but I can add that space now.  I only use clean space, hold on while I ask the OS to write about 8,388,608,000 zeroes for me.  This isn&#8217;t a problem, I&#8217;m patient.</p>
<p>User: Zzzzzzzz&#8230;.</p>
<p>DBA: There&#8217;s no rush, everyone can use the existing log space while you work on that.</p>
<p>Ticketing System: Zzzzzzz&#8230;..</p></blockquote>
<p>The 8,000 MB I used here isn&#8217;t a made-up number, it&#8217;s a best practice due to VLF sizes on larger databases.  Most DBAs set autogrowth to less than 8,000 because they&#8217;d rather deal with smaller VLFs than irate users, and I&#8217;d recommend 1,000 MB for autogrowth on larger databases.  To be fair, that number drops to 500 MB starting in SQL 2012, but that&#8217;s still a lot of waiting during peak hours.</p>
<p>On smaller databases you aren&#8217;t going to want to grow log files by 8,000 MB off hours, or even 1,000 MB for autogrowth.  The growth should be something rather significant for the database yet size appropriate, and I typically like to do close to 50% of the current file size.  So, if I have a 500 MB log, I&#8217;ll set it to 250 MB autogrowth and do manual growths of the same size.  For smaller databases where 50% log growth would be less than 100 MB, I&#8217;m not always worried about manually growing the log files since there are diminishing returns on my time and effort.</p>
<p>My post <a href="https://simplesqlserver.com/2016/02/01/database-log-vlfs" target="_blank"><span style="color:#0066cc;">Database Log VLFs</span></a> gets into the details of why those are best practices and what it means to you.</p>
<h2>Entry-Level Posts</h2>
<p>Tim Ford (<a href="http://thesqlagentman.com/" target="_blank">b</a>|<a href="https://twitter.com/sqlagentman" target="_blank">t</a>) noticed how we moved to advanced topics and asked us to remember where we came from in his <a href="http://thesqlagentman.com/2016/01/entry-level-content/" target="_blank">Entry-Level Content Challenge</a>.  This is my second post to take on his challenge, and I hope others will take him up on this!</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/02/15/shrinking-database-log-files/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40677</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Database Log VLFs</title>
		<link>https://simplesqlserver.com/2016/02/08/database-log-vlfs/</link>
					<comments>https://simplesqlserver.com/2016/02/08/database-log-vlfs/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 08 Feb 2016 12:00:53 +0000</pubDate>
				<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Tuning]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[Transacton Log]]></category>
		<category><![CDATA[VLF]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40924</guid>

					<description><![CDATA[Transaction logs are split into VLFs which affects performance.  How large are yours and what does that mean for you?]]></description>
										<content:encoded><![CDATA[<p>Virtual Log Files (VLFs) split a physical database log file into smaller segments, which are required for how log files work in the background.  These are created automatically, but automatically doesn&#8217;t always mean perfect.  Here&#8217;s a practical view of what you need to know and how you can set them up properly.</p>
<h2>What VLFs Do</h2>
<p>My initial view of how a transaction log worked was that each change was written to a log file, and, as the documentation of those changes was no longer required, it was deleted from the log file.  I think this is a common way to view it, and it&#8217;s close to the truth.  However, it&#8217;s not close enough for a conversation on VLFs.</p>
<p>What really happens is that changes are written to the first VLF, which is just a segment of the log file.  When that VLF fills up, it moves on to the next VLF in a systematic order.</p>
<p>Each change made to the database is assigned a Log Sequence Number (LSN), and SQL Server keeps track of the oldest LSN it still needs for any purpose.  This purpose can be many things, but is typically the oldest LSN of the following:</p>
<ul>
<li>Oldest LSN on a log backup (full or bulk-logged recovery)</li>
<li>LSN at the start of the oldest active transaction</li>
<li>LSN last replicated when using certain types of replication</li>
</ul>
<p>Here&#8217;s my extremely complicated script to tell you what that reason is for your database:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span>name
	<span style="color:gray;">, </span>log_reuse_wait_desc 
<span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases
</span></pre>
<p>If the last LSN in a VLF is older than what you need to keep, that VLF can be reused.  This makes is very efficient because this becomes something like a set-based operation for SQL Server, the entire VLF is cleared and marked for reuse at once instead of line-by-line.</p>
<p>To help you picture how the VLFs work, someone at Microsoft drew you a picture on <a href="https://msdn.microsoft.com/en-us/library/ms179355(v=SQL.105).aspx" target="_blank">Transaction Log Physical Architecture</a>.  Then you can look at the results of <a href="http://sqlblog.com/blogs/kalen_delaney/archive/2009/12/21/exploring-the-transaction-log-structure.aspx" target="_blank">DBCC LogInfo</a>, and it will make a lot more sense when you see a VLF on each line there along with its status.</p>
<h2>Only One Log File, Dedicated Drive</h2>
<p>Adding more than one log file on a single database isn&#8217;t going to help your performance.  SQL Server is writing to a single VLF at a time, and that single VLF is part of a single log file.  If you have multiple log files then you&#8217;re writing to one while the other sits idle.  Once you fill up the last VLF on the first file it starts writing to the second file while the first sits idle.  If these are on separate drives that means each drive has to be able to handle the I/O, but it&#8217;s hit or miss if you&#8217;re using the I/O which leads to wasted resources and inconsistent performance.</p>
<p>As for how you&#8217;re writing to the log, most of the work done is writing to the tail end of it so spinning disks do really well just keeping the head in one place and writing to the file.  That is unless you have other types of files on this disk so the head writes a little to the log, jumps over to write a little to a data file, writes a bit more to the log, then jumps over to read a couple indexes.  This is a lot of where the recommendation to keep your data and log files on separate disks come from, but there is more to it than I&#8217;ll get into here.</p>
<p>However, we&#8217;re going to use RAID for redundancy making the drives too large for just a log, then we&#8217;ll put it on a SAN with a write cache, and do so many other things to make it more complicated.  If a server is extremely stressed and highly critical, look into dedicated spindles, SSDs, or other options.  On general shared disk arrays, it&#8217;s nice if you can have an array dedicated to just log files, even if it&#8217;s log files for multiple servers.</p>
<h2>VLF Size Matters</h2>
<p>When you&#8217;re writing to VLFs there can be two issues.  First, the VLFs are very small and SQL Server is jumping all over the place maintaining small files and figuring out where it should write the next entry &#8211; this is common and a big performance hit.  Second, the VLFs are too large and SQL Server is reserving a ton of space when only a small part of the tail end of the VLF is in use, then trying to clear it all at once &#8211; this is less common and less of an issue.</p>
<p>In addition to this, VLFs each need to be processed when restoring or recovering a database.  Recovering is part of restarting SQL services, so you should be hitting this issue at least once a month with your Windows Updates.  Every VLF adds some overhead to this process, and a huge number adds a lot of overhead to lengthen this process.</p>
<p>Details of this are on the Microsoft Customer Service and Support blog post <a href="https://blogs.msdn.microsoft.com/psssql/2009/05/21/how-a-log-file-structure-can-affect-database-recovery-time/" target="_blank">How a log file structure can affect database recovery time</a>, and it includes this eye-opening quote, &#8220;The first phase of recovering a database is called <strong>discovery</strong> where all the VLFs are scanned (in serial and single threaded fashion) before actual recovery starts.&#8221;</p>
<p>Are your log files pre-sized to take up the entire drive even though they only ever use 5% of that space?  Is that hurting you on recovery?</p>
<h2>How Big Are My VLFs?</h2>
<p>I have a script for that.  This is set to filter only logs that I want to look at, but you can comment out there WHERE clause on the final statement to see it all.  Also, it would be extremely rare for me to look at individual VLFs, so this is only looking at the sums and averages for each file.</p>
<p>I used to manually comment out a line on this depending on the version of SQL Server until I read a post on VLFs by Andy Galbraith (<a href="http://nebraskasql.blogspot.com/" target="_blank">b</a>|<a href="https://twitter.com/DBA_ANDY" target="_blank">t</a>) <a href="http://nebraskasql.blogspot.com/2016/01/counting-your-vlfs-or-temp-tables.html" target="_blank">Counting Your VLFs, or, Temp Tables Inside IF&#8230;ELSE Blocks</a>.  Thank you, Andy.  The timing of your post was perfect to help me make this easier for everyone.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">CREATE TABLE </span>#VLF_temp 
<span style="color:gray;">(
	</span>RecoveryUnitID <span style="color:blue;">int
	</span><span style="color:gray;">, </span>FileID <span style="color:blue;">varchar</span><span style="color:gray;">(</span>3<span style="color:gray;">) 
	, </span>FileSize <span style="color:blue;">numeric</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>0<span style="color:gray;">)
	, </span>StartOffset <span style="color:blue;">bigint 
	</span><span style="color:gray;">, </span>FSeqNo <span style="color:blue;">bigint 
	</span><span style="color:gray;">, </span><span style="color:blue;">Status char</span><span style="color:gray;">(</span>1<span style="color:gray;">)
	, </span>Parity <span style="color:blue;">varchar</span><span style="color:gray;">(</span>4<span style="color:gray;">) 
	, </span>CreateLSN <span style="color:blue;">numeric</span><span style="color:gray;">(</span>25<span style="color:gray;">,</span>0<span style="color:gray;">)
)

</span><span style="color:blue;">CREATE TABLE </span>#VLF_db_total_temp
<span style="color:gray;">(
	</span>DatabaseName <span style="color:blue;">sysname 
	</span><span style="color:gray;">, </span>LogiFilename <span style="color:blue;">sysname
	</span><span style="color:gray;">, </span>PhysFileName <span style="color:blue;">sysname
	</span><span style="color:gray;">, </span>AVG_VLF_Size_MB <span style="color:blue;">DECIMAL</span><span style="color:gray;">(</span>12<span style="color:gray;">,</span>2<span style="color:gray;">)
	, </span>vlf_count <span style="color:blue;">int
	</span><span style="color:gray;">, </span>log_size_mb <span style="color:blue;">FLOAT
	</span><span style="color:gray;">, </span>log_growth_mb <span style="color:blue;">FLOAT
</span><span style="color:gray;">)

</span><span style="color:blue;">IF </span><span style="color:gray;">(</span><span style="color:blue;">SELECT </span><span style="color:gray;">LEFT(</span><span style="color:magenta;">cast</span><span style="color:gray;">(</span><span style="color:magenta;">serverproperty</span><span style="color:gray;">(</span><span style="color:red;">'ProductVersion'</span><span style="color:gray;">) </span><span style="color:blue;">as varchar</span><span style="color:gray;">),</span>2<span style="color:gray;">)) in (</span><span style="color:red;">'8.'</span><span style="color:gray;">,</span><span style="color:red;">'9.'</span><span style="color:gray;">,</span><span style="color:red;">'10'</span><span style="color:gray;">) </span><span style="color:blue;">BEGIN
	ALTER TABLE </span>#VLF_temp <span style="color:blue;">DROP COLUMN </span>RecoveryUnitID
<span style="color:blue;">END

DECLARE </span>db_cursor <span style="color:blue;">CURSOR READ_ONLY FOR 
SELECT </span>name <span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">databases
</span><span style="color:blue;">WHERE State </span><span style="color:gray;">= </span>0

<span style="color:blue;">DECLARE </span>@name <span style="color:blue;">sysname</span><span style="color:gray;">, </span>@stmt <span style="color:blue;">varchar</span><span style="color:gray;">(</span>40<span style="color:gray;">)

</span><span style="color:blue;">OPEN </span>db_cursor

<span style="color:blue;">FETCH NEXT FROM </span>db_cursor <span style="color:blue;">INTO </span>@name

<span style="color:blue;">WHILE </span><span style="color:gray;">(</span><span style="color:magenta;">@@fetch_status </span><span style="color:gray;">&lt;&gt; -</span>1<span style="color:gray;">) </span><span style="color:blue;">BEGIN
	IF </span><span style="color:gray;">(</span><span style="color:magenta;">@@fetch_status </span><span style="color:gray;">&lt;&gt; -</span>2<span style="color:gray;">) </span><span style="color:blue;">BEGIN
		INSERT INTO </span>#VLF_temp
		<span style="color:blue;">EXEC </span><span style="color:gray;">(</span><span style="color:red;">'DBCC LOGINFO ([' </span><span style="color:gray;">+ </span>@name <span style="color:gray;">+ </span><span style="color:red;">']) WITH NO_INFOMSGS'</span><span style="color:gray;">)

		</span><span style="color:blue;">INSERT INTO </span>#VLF_db_total_temp <span style="color:gray;">(</span>DatabaseName<span style="color:gray;">, </span>LogiFilename<span style="color:gray;">, </span>PhysFileName<span style="color:gray;">, </span>vlf_count<span style="color:gray;">, </span>AVG_VLF_Size_MB<span style="color:gray;">, </span>log_size_mb<span style="color:gray;">, </span>log_growth_mb<span style="color:gray;">)
		</span><span style="color:blue;">SELECT </span>DatabaseName <span style="color:gray;">= </span>@name
			<span style="color:gray;">, </span>LogiFilename <span style="color:gray;">= </span>mf<span style="color:gray;">.</span>name
			<span style="color:gray;">, </span>PhysFileName <span style="color:gray;">= </span>mf<span style="color:gray;">.</span>physical_name
			<span style="color:gray;">, </span>vlf_count <span style="color:gray;">= </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(*)
			, </span>AVG_VLF_Size_MB <span style="color:gray;">= (</span><span style="color:magenta;">AVG</span><span style="color:gray;">(</span>FileSize<span style="color:gray;">)/</span>1024<span style="color:gray;">)/</span>1024
			<span style="color:gray;">, </span>log_size_mb <span style="color:gray;">= (</span>mf<span style="color:gray;">.</span>size <span style="color:gray;">* </span>8<span style="color:gray;">)/</span>1024 
			<span style="color:gray;">, </span>log_growth_mb <span style="color:gray;">= </span><span style="color:blue;">CASE </span>mf<span style="color:gray;">.</span>is_percent_growth
									<span style="color:blue;">WHEN </span>1 <span style="color:blue;">THEN </span><span style="color:gray;">(</span>mf<span style="color:gray;">.</span>size <span style="color:gray;">* </span>8<span style="color:gray;">)/</span>1024 <span style="color:gray;">* </span>mf<span style="color:gray;">.</span>growth<span style="color:gray;">/</span>100
									<span style="color:blue;">WHEN </span>0 <span style="color:blue;">THEN  </span><span style="color:gray;">(</span>mf<span style="color:gray;">.</span>growth <span style="color:gray;">* </span>8<span style="color:gray;">)/</span>1024 
									<span style="color:blue;">END
		FROM </span>#VLF_temp vt
			<span style="color:gray;">INNER JOIN </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">master_files </span>mf <span style="color:blue;">ON </span>mf<span style="color:gray;">.</span>database_id <span style="color:gray;">= </span><span style="color:magenta;">db_id</span><span style="color:gray;">(</span>@name<span style="color:gray;">) AND </span>mf<span style="color:gray;">.</span><span style="color:magenta;">file_id </span><span style="color:gray;">= </span>vt<span style="color:gray;">.</span>fileid
		<span style="color:blue;">GROUP BY </span>mf<span style="color:gray;">.</span>name<span style="color:gray;">, </span>mf<span style="color:gray;">.</span>physical_name<span style="color:gray;">,</span>mf<span style="color:gray;">.</span>size<span style="color:gray;">,</span>mf<span style="color:gray;">.</span>growth<span style="color:gray;">,</span>mf<span style="color:gray;">.</span>is_percent_growth<span style="color:gray;">;           

		</span><span style="color:blue;">TRUNCATE TABLE </span>#VLF_temp           
	<span style="color:blue;">END

	FETCH NEXT FROM </span>db_cursor <span style="color:blue;">INTO </span>@name
<span style="color:blue;">END

CLOSE </span>db_cursor
<span style="color:blue;">DEALLOCATE </span>db_cursor

<span style="color:blue;">SELECT </span><span style="color:gray;">* 
	, </span>USEDB <span style="color:gray;">= </span><span style="color:red;">'USE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">']' 
	</span><span style="color:gray;">, </span>CP <span style="color:gray;">= </span><span style="color:red;">'CHECKPOINT'
	</span><span style="color:gray;">, </span>ShrinkZero <span style="color:gray;">= </span><span style="color:red;">'DBCC SHRINKFILE (N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''' , 0)'
	</span><span style="color:gray;">, </span>GrowBack <span style="color:gray;">= </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''', SIZE = ' </span><span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>log_size_mb <span style="color:blue;">AS VARCHAR</span><span style="color:gray;">(</span>20<span style="color:gray;">)) + </span><span style="color:red;">'MB ) --I split this up when over 10,000 MB'
	</span><span style="color:gray;">, </span>ChangeGrowth <span style="color:gray;">= </span><span style="color:blue;">CASE </span><span style="color:green;">--Note, never gets up to the 8000 MB I recommend for manual growths, but still 250 MB VLFs
						</span><span style="color:blue;">WHEN </span>DatabaseName <span style="color:gray;">= </span><span style="color:red;">'tempdb' </span><span style="color:blue;">THEN </span><span style="color:red;">''
						</span><span style="color:blue;">WHEN </span>log_size_mb <span style="color:gray;">&lt;= </span>128 <span style="color:blue;">THEN </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''', FILEGROWTH = 32MB )'
						</span><span style="color:blue;">WHEN </span>log_size_mb <span style="color:gray;">&lt;= </span>512 <span style="color:blue;">THEN </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''', FILEGROWTH = 128MB )'
						</span><span style="color:blue;">WHEN </span>log_size_mb <span style="color:gray;">&lt;= </span>4000 <span style="color:blue;">THEN </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''', FILEGROWTH = 1000MB )'
						</span><span style="color:blue;">WHEN </span>log_size_mb <span style="color:gray;">&lt;= </span>16000 <span style="color:blue;">THEN </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''', FILEGROWTH = 1000MB )'
						</span><span style="color:blue;">ELSE </span><span style="color:red;">'ALTER DATABASE [' </span><span style="color:gray;">+ </span>DatabaseName <span style="color:gray;">+ </span><span style="color:red;">'] MODIFY FILE ( NAME = N''' </span><span style="color:gray;">+ </span>LogiFileName <span style="color:gray;">+ </span><span style="color:red;">''', FILEGROWTH = 1000MB )'
						</span><span style="color:blue;">END
FROM </span>#VLF_db_total_temp
<span style="color:blue;">WHERE </span><span style="color:gray;">(</span>vlf_count <span style="color:gray;">&gt; </span>50 
		<span style="color:gray;">AND </span>Avg_VLF_Size_MB <span style="color:gray;">&lt; </span>vlf_count <span style="color:gray;">/ </span>3<span style="color:gray;">) 
	OR (</span>vlf_count <span style="color:gray;">&gt; </span>250
		<span style="color:gray;">AND </span>AVG_VLF_Size_MB <span style="color:gray;">&lt; </span>vlf_count / 2<span style="color:gray;">)
</span><span style="color:blue;">ORDER BY </span>vlf_count <span style="color:blue;">DESC 

DROP TABLE </span>#VLF_temp 
<span style="color:blue;">DROP TABLE </span>#VLF_db_total_temp
</pre>
<h2>What&#8217;s the Right Size?</h2>
<p>The most common thing you&#8217;ll see on the internet is people talking about having too many with the limit being 1,000 VLFs and ideally at 50 VLFs.  I&#8217;m a little different and focus more on size than number.  The concept I&#8217;m going for is that excessive context switching is what causes me grief, and a reasonable size VLF will keep that under control.</p>
<p>Now there&#8217;s no magic number that will work noticeably better in all cases, so I won&#8217;t give you static numbers and say they&#8217;re magic.  What I can say is that too small is basically fragmentation with the overhead of context switching, too many increases recovery time, too big is huge chucks, and too few could cause extra growths as unused space is still being reserved.</p>
<p>It&#8217;s easy to visualize how smaller and excessive numbers of VLFs can cause issues, but that doesn&#8217;t mean huge VLFs are the way to go.  Tammy Richter Jones (<a href="http://blogs.msdn.com/b/sqladventurer/" target="_blank">b</a>) gets into details with her post <a href="http://blogs.msdn.com/b/sqladventurer/archive/2012/03/16/if-gt-50-vlfs-are-bad-then-16-is-good-right.aspx" target="_blank">If &gt; 50 VLFs are bad, then 16 is good, right?</a>.  I&#8217;d love to see more on the affects of huge VLFs to have a more complete understanding if a 1 TB log file is better off with 50 20GB VLFs or 2,000 500MB VLFs.  I&#8217;m not convinced that &gt;50 is bad.</p>
<p>Personally, I like formulas a lot more than static numbers, especially since they allow me to avoid extremes without stressing out.  The formulas I used broke it down into three categories:</p>
<ol>
<li>Below 50 VLFs, this is a reasonable number, they get a free ride no matter how big they are.</li>
<li>Between 50 and 250 VLFs, the average size in MB has to be at least 1/3 of the count of VLFs.
<ul>
<li>For a file with 100 VLFs, the average VLF has to be over 33 MB.</li>
</ul>
</li>
<li>Over 250 VLFs, the average size in MB has to be at least 1/2 of the count of VLFs.
<ul>
<li>For a file with 500 VLFs, the average VLF has to be over 250 MB.</li>
</ul>
</li>
</ol>
<p>This is good enough for almost any database.  I don&#8217;t care about there being a couple small VLFs mixed in there because I&#8217;ll spend most of my time in the large ones.  I don&#8217;t care about the numbers getting larger because the average VLF is large enough to avoid too much context switching.  You&#8217;re not out to micromanage, so take a relaxed approach to this unless you have a reason to not be relaxed.</p>
<h2>What&#8217;s Default?</h2>
<p>Remember how I said there were no magic numbers?  Well, that doesn&#8217;t mean there aren&#8217;t wrong numbers.</p>
<p>By default, every database is based off of model, which has a 1 MB log files growing in 10% increments.  So 1/10th of a MB for the first growth, which is just a tiny fragment.  If you leave it this way on large databases you could end up with a 1 TB log trying to grow 100 GB at a time, which your users <strong>will</strong> notice.</p>
<p>Be default, you get the worst of both worlds!  The goal here is to avoid extremes, so you&#8217;ll want to change the growth rates away from the default.</p>
<h2>How Do I Change the Size?</h2>
<p>There&#8217;s only one way to change the size of your VLFs.  Delete them and recreate them.</p>
<p>For every database EXCEPT TempDB, this means shrinking the logs (it deletes VLFs to get to the size you want), then growing the logs again.  Both manual and automatic growths will split the new physical space into VLFs, but that depends on your version of SQL Server.</p>
<p>Here are the growth rates I pulled from Paul Randal&#8217;s post <a href="http://www.sqlskills.com/blogs/paul/important-change-vlf-creation-algorithm-sql-server-2014/">Important change to VLF creation algorithm in SQL Server 2014</a>.</p>
<p>SQL 2012 and earlier uses this formula:</p>
<blockquote><p>&lt;= 64 MB growth is 4 VLFs</p>
<p>64 MB and &lt;= 1 GB = 8 VLFs</p>
<p>1 GB = 16 VLFs</p></blockquote>
<p>SQL 2014+ uses this formula:</p>
<blockquote><p>Is the growth size less than 1/8 the size of the current log size?</p>
<p>Yes: create 1 new VLF equal to the growth size</p>
<p>No: use the formula above</p></blockquote>
<p>So if you have too many VLFs you can shrink your log file.  It will delete VLFs that are not in use to make it smaller.  Then grow the file in increments that give you VLFs sized how you want them.  If you&#8217;re going for 500 MB VLFs then you grow your file 8,000 MB at a time.</p>
<p>The VLFs that weren&#8217;t deleted in this process, because they were in use or you didn&#8217;t shrink the file as far as you could have, will not be affected.  This means you&#8217;ll have different sized VLFs throughout your file, but that doesn&#8217;t really matter.  What does matter is that you don&#8217;t have any ridiculously large VLFs and you&#8217;re spending most of your time in properly sized VLFs.</p>
<p>Here&#8217;s how I do it.  Shrink it as much as possible.  Shrinking is never as easy as it should be, so verify it shrunk it a significant amount.  If it didn&#8217;t, take log backups and try again, repeating this process a handful of times if needed.  Then, if I want the log to be 32,000 MB, I just grow it by 8,000 MB 4 times.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:green;">--Run a log backup before this if database is not in simple and log backups run less frequently than every 5 minutes.
</span><span style="color:blue;">USE </span>[DatabaseName]
<span style="color:blue;">GO
CHECKPOINT
GO
DBCC </span>SHRINKFILE <span style="color:gray;">(</span><span style="color:red;">N'LogicalFileName_Log' </span><span style="color:gray;">, </span>0<span style="color:gray;">)
</span><span style="color:blue;">GO
USE </span>[master]
<span style="color:blue;">GO
ALTER DATABASE </span>[DatabaseName] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'LogicalFileName_Log'</span><span style="color:gray;">, </span>SIZE <span style="color:gray;">= </span>8000MB <span style="color:gray;">)
</span><span style="color:blue;">ALTER DATABASE </span>[DatabaseName] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'LogicalFileName_Log'</span><span style="color:gray;">, </span>SIZE <span style="color:gray;">= </span>16000MB <span style="color:gray;">)
</span><span style="color:blue;">ALTER DATABASE </span>[DatabaseName] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'LogicalFileName_Log'</span><span style="color:gray;">, </span>SIZE <span style="color:gray;">= </span>24000MB <span style="color:gray;">)
</span><span style="color:blue;">ALTER DATABASE </span>[DatabaseName] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'LogicalFileName_Log'</span><span style="color:gray;">, </span>SIZE <span style="color:gray;">= </span>32000MB <span style="color:gray;">)
</span><span style="color:blue;">GO
</span></pre>
<p>Assuming you were able to shrink this down to 10 VLFs remaining and you want it to be 32,000 MB, this would give you 74 VLFs with all new ones being 500 MB.  It goes over the 50 VLF limit I&#8217;ve seen elsewhere, but I&#8217;m very happy with these sizes and numbers.</p>
<p>With 8,000 MB growths the new 2014 calculation won&#8217;t kick in until the log was already 64,000 MB, and at that point an 8,000 MB VLF probably isn&#8217;t a bad thing.</p>
<h2>Autogrowth Rates</h2>
<p>I recommend manually growing larger log files between 8,000 and 16,000 MB at a time, but not autogrowths.  The process that causes a file to grow, and any other process that needs that space, will sit around and wait for an autogrowth to complete.  That could take a minute on descent disks, which is longer than many timeouts I&#8217;ve worked with.</p>
<p>Manually growing files is best, but almost every database will rely on autogrowth.  Know your timeouts, know how long it takes to grow a file, and size it accordingly.  I like to do 1,000 MB here, but will do as less if I have to.  If you can&#8217;t make a descent sized growth based on this, be more paranoid about manually growing files while leaving autogrowth set to smaller sizes.</p>
<p>The reason for 1,000 MB is that this is the largest size that will still give you 8 VLFs instead of being split into 16 VLFs, with the actual limit being at 1,024 MB.  If you did 2,000 MB, you&#8217;d still end up with 125 MB VLFs.  You can get that up to 250 MB each at 4,000 MB, but you&#8217;re telling your users to wait while 4,000 MB is zeroed out before they can move on.  Although the calculation changes for larger logs on SQL 2014, I&#8217;d still stick with 1,000 MB for a good autogrowth size.</p>
<p>Note, there is a resolved bug that would cause issues when log files grew in 4 GB increments, so you&#8217;ll see a lot of people using 8,000 MB instead of 8,192 MB.  We aren&#8217;t using an effected patch level anymore, but DBAs are made to be paranoid.  We&#8217;re not just lazy at math, although this doesn&#8217;t rule out that possibility.</p>
<h2>TempDB</h2>
<p>TempDB is different.  The database is recreated every time the SQL service restarts, so the log is recreated as well, following the formulas above.  If the log is over 1 GB, it starts out with 16 VLFs.  Jonathan Kehayias got into the details in his post <a href="https://www.sqlskills.com/blogs/jonathan/tsql-tuesday-11-misconceptions-the-tempdb-log-file-and-vlf-counts/" target="_blank">TSQL Tuesday #11 &#8211; Misconceptions &#8211; The TempDB Log File and VLF Counts</a>.</p>
<p>So TempDB never has too many on startup, but is this too few?  What if TempDB&#8217;s log is 40 GB, do you want a 2.5 GB VLF?  Is setting up TempDB to have 5 log files that are 8,000 MB each so it starts up with 500 MB VLFs a better idea?</p>
<p>Seriously, someone answer this one, I&#8217;m curious myself.</p>
<h2>Changes Have Risk</h2>
<p>The script above creates scripts to make changes.  It&#8217;s not perfectly safe to make those changes.  Test it, understand it, and be careful running it.</p>
<p>One of the most important pieces is that the scripts it generates will try to shrink the log as much as possible before growing it again.  In terms of cleaning up VLFs, this is perfect.  In terms of trying to run this during the day, it risks transactions failing because they&#8217;re trying to grab the next VLF as you&#8217;re running a shrink job to delete all unused VLFs.</p>
<p>You might be able to get away with this during the day most of the time, but it&#8217;s best to wait for a maintenance window to be safe.</p>
<h2>What Others Did</h2>
<p>Linchi Shea showed performance degradation from having too many small VLFs on <a href="http://sqlblog.com/blogs/linchi_shea/archive/2009/02/09/performance-impact-a-large-number-of-virtual-log-files-part-i.aspx" target="_blank">Performance impact: a large number of virtual log files – Part I</a>.  Updates can take almost 10x as long, and I would assume its due to the size and not number of VLFs.</p>
<p>SQLskills.com has blog posts that read like a series on this.  Kimberly Tripp wrote <a href="http://sqlskills.com/blogs/kimberly/post/8-Steps-to-better-Transaction-Log-throughput.aspx" target="_blank">8 Steps to better Transaction Log throughput</a>, then followed that up with <a href="http://www.sqlskills.com/blogs/kimberly/transaction-log-vlfs-too-many-or-too-few/" target="_blank">Transaction Log VLFs too many or too few?</a>, which was updated to link to Paul Randal&#8217;s post <a href="http://www.sqlskills.com/blogs/paul/important-change-vlf-creation-algorithm-sql-server-2014/" target="_blank">Important change to VLF creation algorithm in SQL Server 2014</a>.  All are must-reads if you want to really understand this topic.</p>
<p>Tony Rogerson (<a href="https://sqlserverfaq.com/tonyrogerson/" target="_blank">b</a>|<a href="https://twitter.com/tonyrogerson" target="_blank">t</a>) at the time of this writing has only put one post on his new blog, <a href="https://sqlserverfaq.com/tonyrogerson/2016/01/19/transaction-log-concepts-acid-wal-vlfs-checkpoint-process/" target="_blank">Transaction Log Concepts: Part 1</a>.  If this is his typical quality of work, we have a lot to look forward to.  I&#8217;m personally looking forward to the other 3 parts of this 4 part series.</p>
<p>Grahm Kent (<a href="http://globalgoat.com" target="_blank">b</a>|<a href="http://www.twitter.com/globalgoat" target="_blank">t</a>) ran tests on the performance differences between two log files in his post <a href="http://blogs.msdn.com/b/grahamk/archive/2008/05/16/slow-recovery-times-and-slow-performance-due-to-large-numbers-of-virtual-log-files.aspx" target="_blank">Slow recovery times and slow performance due to large numbers of Virtual Log Files</a>.  Both were 5 GB, but one was 16 VLFs (320 MB each) while the other was 20,480 VLFs (0.25 MB each).  It was good to see transaction performance as well as recovery performance covered.</p>
<h2>Test It</h2>
<p>If your database is absolutely critical to the point that squeezing an extra couple milliseconds of performance throughout the day would be noticed or if a server coming back online 15 seconds faster would save thousands of dollars, test it.  See how it performs in load tests and service restarts using your test servers and your database.</p>
<p>If you have a 1 TB log file and verified it needs to be about that big, what happens when you size it so you have 2,000 VLFs that are 500 MB each?  What happens when you have 100 VLFs that are 10 GB each?  Did it change recovery, any replication you&#8217;re doing, backup times, etc.?</p>
<p>Know that this can make a difference and that this is just another configuration change you can test out.  Crazy numbers on either side can be bad, and perfect numbers in the middle aren&#8217;t going to come from some stranger on the internet that never saw your server.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/02/08/database-log-vlfs/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40924</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>TempDB Excessive Memory Usage Example</title>
		<link>https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/</link>
					<comments>https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 01 Feb 2016 12:00:54 +0000</pubDate>
				<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[Tuning]]></category>
		<category><![CDATA[TempDB]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=41222</guid>

					<description><![CDATA[I see TempDB using more memory than I feel it should and found a way to resolve it.  Previously I dove into technical details and questioning what I saw in my post TempDB Memory Leak? which is great for proving and debugging an issue.  Now I want to step back and talk about it from a [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I see TempDB using more memory than I feel it should and found a way to resolve it.  Previously I dove into technical details and questioning what I saw in my post <a href="https://simplesqlserver.com/2016/01/05/tempdb-memory-leak" target="_blank"><span style="color:#0066cc;">TempDB Memory Leak?</span></a> which is great for proving and debugging an issue.  Now I want to step back and talk about it from a practical perspective.</p>
<p>To keep it practical, I&#8217;m going to walk through a single server, step-by-step.  What I saw that caught my eye and how I applied my workaround.  This is a real, production server with screen shots from Idera Diagnostic Manager for both before and after comparisons.</p>
<h2>What I&#8217;m Seeing</h2>
<p>A large portion of my buffer pool is being utilized by TempDB pages which are unallocated on disk.  The easiest way to see this is this quick query:</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span>Unallocated_MB <span style="color:gray;">= </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">)/</span>128
<span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_os_buffer_descriptors </span>bd
	<span style="color:gray;">LEFT JOIN </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">allocation_units </span>au <span style="color:blue;">ON </span>bd<span style="color:gray;">.</span>allocation_unit_id <span style="color:gray;">= </span>au<span style="color:gray;">.</span>allocation_unit_id
<span style="color:blue;">WHERE </span>bd<span style="color:gray;">.</span>database_id <span style="color:gray;">= </span>2
	<span style="color:gray;">AND </span>au<span style="color:gray;">.</span>allocation_unit_id <span style="color:gray;">IS NULL
</span></pre>
<p>The details of everything in memory can be seen on my post <a href="https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/" target="_blank">Query the Buffer Pool</a>.</p>
<p>Since these are all pointing to real, but unallocated TempDB pages on disk, I was able to develop a workaround of simply having fewer TempDB pages on disk.  AKA, I made TempDB data files smaller.</p>
<h2>Sizing TempDB</h2>
<p>Here we&#8217;re talking about being hurt by TempDB being too large, but we can&#8217;t lose sight of TempDB being too small hurting you as well.  Specifically, when your server is the busiest with a small TempDB it will have to stop and grow the files, then deal with any fragmentation that process caused.  So, while I&#8217;m not going to recommend filling your dedicated drive, I&#8217;m not going to recommend making TempDB tiny, either.</p>
<p>My recommendation is to monitor your server, find out how much space TempDB uses, and make your TempDB <strong>significantly larger than that</strong>.  The longer you have watched your server, the better.  The goal is still to avoid almost any growth possible, but I&#8217;m adding in a &#8220;don&#8217;t go crazy&#8221; clause.</p>
<p>You can do this yourself as I talked about in <a href="https://simplesqlserver.com/2013/09/10/monitoring-database-and-table-sizes/" target="_blank">Monitoring Database and Table Sizes</a> using the function call FileProperty(&lt;filename&gt;, &#8216;space used&#8217;).  If you do it this way, make sure you have this running for a minimum of several weeks at least once an hour, day and night.  It&#8217;s possible, not difficult to do, and not recommended for servers that have a reasonable monitoring system already in place.</p>
<p>If you&#8217;re doing this to your production servers you should already have monitoring software that&#8217;s watching file sizes for you, even if it&#8217;s only tracking this in the background with no obvious way to find the information in the GUI.  This is the situation I find myself in using Idera Diagnostic Manager.  The data&#8217;s there, but the GUI is set up for more immediate concerns.  I need this query to get what I want out of it.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT TOP </span>100 S<span style="color:gray;">.</span>InstanceName
	<span style="color:gray;">, </span>DS<span style="color:gray;">.</span>UTCCollectionDateTime
	<span style="color:gray;">, </span>D<span style="color:gray;">.</span>DatabaseName 
	<span style="color:gray;">, </span>DataUsed_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">((</span>DataSizeInKilobytes <span style="color:gray;">+ </span>IndexSizeInKilobytes<span style="color:gray;">) / </span>1024 <span style="color:gray;">/ </span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">)) 
	, </span>LogUsed_GB <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>LogSizeInKilobytes <span style="color:gray;">/ </span>1024 <span style="color:gray;">/ </span>1024.0 <span style="color:blue;">AS DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>1<span style="color:gray;">))
</span><span style="color:blue;">FROM </span>SQLdmRepository<span style="color:gray;">.</span>dbo<span style="color:gray;">.</span>DatabaseSize DS
	<span style="color:gray;">INNER JOIN </span>SQLdmRepository<span style="color:gray;">.</span>dbo<span style="color:gray;">.</span>SQLServerDatabaseNames D <span style="color:blue;">ON </span>DS<span style="color:gray;">.</span>DatabaseID <span style="color:gray;">= </span>D<span style="color:gray;">.</span>DatabaseID
	<span style="color:gray;">INNER JOIN </span>SQLdmRepository<span style="color:gray;">.</span>dbo<span style="color:gray;">.</span>MonitoredSQLServers S <span style="color:blue;">ON </span>S<span style="color:gray;">.</span>SQLServerID <span style="color:gray;">= </span>D<span style="color:gray;">.</span>SQLServerID 
<span style="color:blue;">WHERE </span><span style="color:magenta;">UPPER</span><span style="color:gray;">(</span>S<span style="color:gray;">.</span>InstanceName<span style="color:gray;">) = </span><span style="color:red;">'SERVER\INSTANCE'  
	</span><span style="color:gray;">AND </span>D<span style="color:gray;">.</span>DatabaseName <span style="color:gray;">= </span><span style="color:red;">'tempdb'
	</span><span style="color:gray;">AND </span>UTCCollectionDateTime <span style="color:gray;">&gt; </span><span style="color:magenta;">GetUTCDate</span><span style="color:gray;">()-</span>180
<span style="color:blue;">ORDER BY </span>4 <span style="color:blue;">DESC
</span></pre>
<p>My recommendation is to take the peak size outside of a one-time process and make the total TempDB data files at least 150% of that size.  If you have 4 TempDB data files and the peak size used is 4 GB, 150% of that is 6 GB, so make each file at least 1.5 GB.  Then set autogrowth to something reasonable because one-time processes will happen.</p>
<h2>My Before Observations</h2>
<p>All of the screen shots and trending information are from Idera Diagnostic Manger, but you should be able to get the same basic information out of any monitoring software.</p>
<p>I had a server with rather low PLE and a lot of physical I/O.</p>
<p><img loading="lazy" data-attachment-id="41270" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/server_oversized_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg" data-orig-size="1035,729" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Server_Oversized_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg?w=652" class="alignnone size-full wp-image-41270" src="https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg" alt="Server_Oversized_TempDB" width="1035" height="729" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg 1035w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg?w=150&amp;h=106 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg?w=300&amp;h=211 300w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg?w=768&amp;h=541 768w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg?w=1024&amp;h=721 1024w" sizes="(max-width: 1035px) 100vw, 1035px" /></p>
<p>The physical I/O is difficult to see here because this server has a lot of writes.  When you look closer you can see that it rather regularly went to 400 page reads per second.</p>
<p><img loading="lazy" data-attachment-id="41595" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/physicalio_oversize_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg" data-orig-size="514,347" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="PhysicalIO_Oversize_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg?w=514" class="alignnone  wp-image-41595" src="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg" alt="PhysicalIO_Oversize_TempDB" width="518" height="350" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg 514w, https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg?w=150&amp;h=101 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg?w=300&amp;h=203 300w" sizes="(max-width: 518px) 100vw, 518px" /></p>
<p>All of those reads caused lots of waits on the server.  In my 12-hour sample period I saw a total of 34,000.  The units shown for this field are fine for comparison reasons, but they appear to be a total of the ms/s on the chart for values collected every 6 minutes, not the total waits.  You have to query the tables behind Idera Diagnostic Manager to get the real values.  In this case, the total waits were 138 minutes for shared and 49 for exclusive latches, for a total of 187 minutes of waiting for the 12 hours.</p>
<p><img loading="lazy" data-attachment-id="41626" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/waits_oversize_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg" data-orig-size="791,498" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Waits_Oversize_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg?w=652" class="alignnone size-full wp-image-41626" src="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg" alt="Waits_Oversize_TempDB" width="791" height="498" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg 791w, https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg?w=150&amp;h=94 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg?w=300&amp;h=189 300w, https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg?w=768&amp;h=484 768w" sizes="(max-width: 791px) 100vw, 791px" /></p>
<p>Just seeing this I wanted to request more memory on the VM, after all it only had 16 GB.  However, I made it a rule to never add memory without justifying what&#8217;s in memory first.  To do that, I used my query on <a href="https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/" target="_blank">Query the Buffer Pool</a>, and this was the first line in the results:</p>
<p><img loading="lazy" data-attachment-id="41281" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/cache_oversized_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg" data-orig-size="600,38" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Cache_Oversized_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg?w=600" class="alignnone size-full wp-image-41281" src="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg" alt="Cache_Oversized_TempDB" width="600" height="38" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg 600w, https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg?w=150&amp;h=10 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg?w=300&amp;h=19 300w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p>Of the 8.5 GB of memory used by the buffer pool, about 6 GB of that was used by unallocated space in TempDB.  I can accept some because of how TempDB caches temp tables and other stuff, but to have 70% of the buffer pool used up while I&#8217;m seeing low PLE and high I/O is not acceptable.</p>
<h2>My Fix</h2>
<p>I had the files set to practically fill the dedicated drive according to best practices, so each of the 4 files was 7,000 MB adding up to almost 28 GB on a 30 GB drive.</p>
<p>Using the query in the Sizing TempDB section above, this server has never used more than 1 GB of data in TempDB at one time.  While that makes for some interesting questions on how it&#8217;s using 6 GB of cache, it does give me a lot of room to reduce the size.</p>
<p>There were no complaints about performance due to what this server is being used for, so I didn&#8217;t try to shrink TempDB.  Instead I took the patient approach an resized TempDB, which took effect the next time SQL Services restarted.  Then I waited for our scheduled server reboots for Windows Updates.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">ALTER DATABASE </span>[tempdb] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'tempdev'</span><span style="color:gray;">,</span>SIZE <span style="color:gray;">= </span>512MB <span style="color:gray;">, </span>FILEGROWTH <span style="color:gray;">= </span>128MB <span style="color:gray;">)
</span><span style="color:blue;">ALTER DATABASE </span>[tempdb] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'tempdev2'</span><span style="color:gray;">,</span>SIZE <span style="color:gray;">= </span>512MB <span style="color:gray;">, </span>FILEGROWTH <span style="color:gray;">= </span>128MB <span style="color:gray;">)
</span><span style="color:blue;">ALTER DATABASE </span>[tempdb] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'tempdev3'</span><span style="color:gray;">,</span>SIZE <span style="color:gray;">= </span>512MB <span style="color:gray;">, </span>FILEGROWTH <span style="color:gray;">= </span>128MB <span style="color:gray;">)
</span><span style="color:blue;">ALTER DATABASE </span>[tempdb] <span style="color:blue;">MODIFY FILE </span><span style="color:gray;">( </span>NAME <span style="color:gray;">= </span><span style="color:red;">N'tempdev4'</span><span style="color:gray;">,</span>SIZE <span style="color:gray;">= </span>512MB <span style="color:gray;">, </span>FILEGROWTH <span style="color:gray;">= </span>128MB <span style="color:gray;">)
</span></pre>
<p>Note that my recommendation above would have been to make the files at least 1.5 GB and I made them 2 GB here.  I&#8217;m not worried about exact numbers, I&#8217;m worried about being reasonable sizes that also avoid growths.  With peak observed usage at 1 GB and TempDB sized at 2 GB, any growths should be very rare.</p>
<p>Overall, the data files will grow at 512 MB at a time, which, when it happens, is large enough to avoid extreme fragmentation yet small enough to avoid extreme waits.  This should only happen during &#8220;I didn&#8217;t mean to do that&#8221; or &#8220;how&#8217;d it get that execution plan&#8221; moments, but it will happen.</p>
<h2>My After Observations</h2>
<p>After the change I waited a couple days to let the cache stabilize after a reboot.  We rebooted on Saturday, and all of these numbers are from the Tuesday after.</p>
<p>When I looked again, TempDB was using 1/10 of the space it was before for unallocated pages in memory.  It&#8217;s not perfect, but it&#8217;s much better.</p>
<p><img loading="lazy" data-attachment-id="41609" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/cache_rightsize_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg" data-orig-size="609,40" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Cache_RightSize_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg?w=609" class="alignnone size-full wp-image-41609" src="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg" alt="Cache_RightSize_TempDB" width="609" height="40" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg 609w, https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg?w=150&amp;h=10 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg?w=300&amp;h=20 300w" sizes="(max-width: 609px) 100vw, 609px" /></p>
<p>TempDB using less cache allowed PLE be a lot more stable, regularly staying well above 2,000.  Using <a href="http://www.sqlskills.com/blogs/jonathan/finding-what-queries-in-the-plan-cache-use-a-specific-index/" target="_blank">Jonathan Kehayias&#8217;s formula</a>, PLE should be above 1,050 on a server with 14 GB of cache, so this is looking good.</p>
<p><img loading="lazy" data-attachment-id="41594" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/server_rightsize_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg" data-orig-size="1042,705" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Server_RightSize_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg?w=652" class="alignnone size-full wp-image-41594" src="https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg" alt="Server_RightSize_TempDB" width="1042" height="705" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg 1042w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg?w=150&amp;h=101 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg?w=300&amp;h=203 300w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg?w=768&amp;h=520 768w, https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg?w=1024&amp;h=693 1024w" sizes="(max-width: 1042px) 100vw, 1042px" /></p>
<p>With data staying in cache more, physical I/O also dropped.  Before it was normal to see spikes to 400 page reads per second, now you&#8217;re seeing about half of that.</p>
<p><img loading="lazy" data-attachment-id="41596" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/physicalio_rightsize_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg" data-orig-size="514,346" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="PhysicalIO_RightSize_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg?w=514" class="alignnone size-full wp-image-41596" src="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg" alt="PhysicalIO_RightSize_TempDB" width="514" height="346" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg 514w, https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg?w=150&amp;h=101 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg?w=300&amp;h=202 300w" sizes="(max-width: 514px) 100vw, 514px" /></p>
<p>Less activity also meant less waits.  The couple spikes still went almost as high, but those are things you&#8217;ll fix more through tuning than memory management.  The totals at the bottom are what I&#8217;m focused on right now, the PageIOLatch_xx waits combined added up to about 11,000 for the day, so about 1/3 of what it was before.</p>
<p>Again, I don&#8217;t like how Idera Diagnostic Manager calculates the totals.  When I queried the tables I saw that there was 60 minutes of waits on shared latches and 13 minutes on exclusive latches, for a total of 73 minutes.  This is in comparison to 138 + 49 = 187 minutes before.</p>
<p><img loading="lazy" data-attachment-id="41630" data-permalink="https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/waits_rightsize_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg" data-orig-size="791,498" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Waits_Rightsize_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg?w=652" class="alignnone size-full wp-image-41630" src="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg" alt="Waits_Rightsize_TempDB" width="791" height="498" srcset="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg 791w, https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg?w=150&amp;h=94 150w, https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg?w=300&amp;h=189 300w, https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg?w=768&amp;h=484 768w" sizes="(max-width: 791px) 100vw, 791px" /></p>
<h2>The Downside</h2>
<p>Even with the new, smaller size, TempDB is a lot larger than it needs to be so it would be very rare to see any growths in the near future.  However, I have to keep in mind that the load on my servers will always be increasing, so I may end up hitting autogrowth down the road.  Then I&#8217;ll reboot the servers each month with Windows Updates, it&#8217;ll shrink TempDB back down for me, and it will have to grow again the next month.</p>
<p>Because of this, I&#8217;ll need to visit every one of my servers from time to time (semi-annually?) to see if this is occurring and increase the size of TempDB if it is.  I&#8217;ll use the same script for determining the peak sizes because I have software that&#8217;s capturing that already, but I could find other methods to get that information if I needed to.</p>
<p>The only other downside I see is with servers that hit a high peak TempDB usage overnight then never come close to it again during the day.  This workaround won&#8217;t do much for those servers.  You need that space overnight, so trying to make the size smaller than what it needs at 2 AM will just make everything worse.  However, knowing about this will drive you nuts enough to look into the overnight processes to see if  you can lower peak usage.  Perhaps you can spread out some concurrent tasks or tune some bad queries that never mattered due to their timing.  It&#8217;s not a bad thing to do this work, it&#8217;s just that this probably wasn&#8217;t a good time for something to climb up your priority list.</p>
<h2>Take a Look</h2>
<p>Take a look at your servers.  How much cache is TempDB using?  Does TempDB ever even use half of the space allocated to it?  Is it causing issues on your servers?</p>
<p>Never make a change just because you saw someone say it&#8217;s a good idea, especially if they&#8217;ve never seen your servers before.  Look at the numbers on your servers for yourself, ask questions to understand it, and make the best decision for those specific servers.</p>
<p>If you do make this change, check TempDB before your next reboot.  Did it have to grow?  If it did, you need to change the size manually so it doesn&#8217;t have to hit an autogrowth every month.  If you shrunk it down to 2 GB total then it grew to 2.5 GB, consider making it 3.5 GB to avoid future growths while keeping the files reasonably sized.</p>
<p>If this was an issue on your servers and you feel SQL Server shouldn&#8217;t have functioned this way, please visit the <a href="https://connect.microsoft.com/SQLServer/feedback/details/2215297" target="_blank">Connect Item</a> on this.  An up-vote is great, a comment or anything else is even better.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/02/01/tempdb-excessive-memory-usage-example/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">41222</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/server_oversized_tempdb.jpg">
			<media:title type="html">Server_Oversized_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_oversize_tempdb.jpg">
			<media:title type="html">PhysicalIO_Oversize_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_oversize_tempdb.jpg">
			<media:title type="html">Waits_Oversize_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_oversized_tempdb1.jpg">
			<media:title type="html">Cache_Oversized_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/cache_rightsize_tempdb.jpg">
			<media:title type="html">Cache_RightSize_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/server_rightsize_tempdb.jpg">
			<media:title type="html">Server_RightSize_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/physicalio_rightsize_tempdb.jpg">
			<media:title type="html">PhysicalIO_RightSize_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2016/01/waits_rightsize_tempdb.jpg">
			<media:title type="html">Waits_Rightsize_TempDB</media:title>
		</media:content>
	</item>
		<item>
		<title>Tim Ford’s Entry-Level Content Challenge</title>
		<link>https://simplesqlserver.com/2016/01/26/tim-fords-entry-level-content-challenge/</link>
					<comments>https://simplesqlserver.com/2016/01/26/tim-fords-entry-level-content-challenge/#respond</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Tue, 26 Jan 2016 12:00:10 +0000</pubDate>
				<category><![CDATA[Entry Level]]></category>
		<category><![CDATA[General Chat]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[entrylevel]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=41338</guid>

					<description><![CDATA[All bloggers started as beginners.  We picked things up, learned them, and got to the point where we wanted to start sharing our knowledge.  The problem is that most of us moved on to talking about advanced topics and left people to fend for themselves on the difficult entry-level learning curve.  My blog, Simple SQL Server, [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>All bloggers started as beginners.  We picked things up, learned them, and got to the point where we wanted to start sharing our knowledge.  The problem is that most of us moved on to talking about advanced topics and left people to fend for themselves on the difficult entry-level learning curve.  My blog, Simple SQL Server, was actually created with me saying I was going to fill that gap, right up until the time I figured out the best way to learn advanced topics was to write things down as I learned.</p>
<h2>Entry-Level Content Challenge</h2>
<p>Tim Ford (<a href="http://thesqlagentman.com/" target="_blank"><span style="color:#0066cc;">b</span></a>|<a href="https://twitter.com/sqlagentman" target="_blank"><span style="color:#0066cc;">t</span></a>) noticed how we moved to advanced topics and asked us to remember where we came from in his <a href="http://thesqlagentman.com/2016/01/entry-level-content/" target="_blank"><span style="color:#0066cc;">Entry-Level Content Challenge</span></a>.</p>
<p>It&#8217;s very simple.  Create one blog post a month that someone just learning your job could understand and benefit from, use the #EntryLevel tag on your blog and notification on twitter, and link to his original post.</p>
<p>This is for everyone, I don&#8217;t care what job you have.  I&#8217;m a SQL Server DBA, Tim&#8217;s a SQL Server DBA, but every profession has beginners that could use some help.</p>
<h2>We&#8217;re Outnumbered</h2>
<p>One of the things, if not THE biggest thing, you hope to do with a blog is to make a difference.  Then you talk about advanced topics to help other people near your level keep moving forward.  You also realize that teaching the details on these topics accelerates your learning and confidence faster than you thought possible.  It&#8217;s great; I recommend everyone does it.</p>
<p>However, there are a couple problems with this logic.  By the time people feel confident enough to blog, they aren&#8217;t talking about the fundamentals anymore.  Also, for every advanced person in any field, there are many, many more entry-level people who could benefit from learning the fundamentals properly from the start.</p>
<p>Bloggers aren&#8217;t talking to the largest portion of their potential audience, who just happen to be going through the most difficult part of the learning curve.</p>
<h2>It&#8217;s the Right Thing To Do</h2>
<p>In no specific order, here&#8217;s why it&#8217;s the right thing to do.  This list contains selfish reasons, moral reasons, and everything in-between.</p>
<ul>
<li>You want to reach a larger audience, and there&#8217;s more people who need this content.</li>
<li>You were a beginner once and learned off of others.</li>
<li>You wish you learned the fundamentals right from the start, but you didn&#8217;t always.</li>
<li>You learn everything you teach better, even if you consider it simple.</li>
<li>Tim challenged you, and it&#8217;s hard to say no to a challenge.</li>
<li>You always wanted to make a difference.</li>
</ul>
<h2>New Bloggers</h2>
<p>This is also a great opportunity for new bloggers.  Talk about subjects you know well, even if you&#8217;re not at the level you feel comfortable teaching advanced topics.</p>
<p>The content is the only difficult part of blogging.  Setting up a blog and creating a post is practically identical to setting up a new email account and sending an email.  If you want to get into blogging and need help, let me know.</p>
<h2>If Einstein Can Do It&#8230;</h2>
<p>Einstein said &#8220;<strong>If you can&#8217;t explain it simply, you don&#8217;t understand it well enough.&#8221;</strong>  I took this quote to heart enough to name my blog after it.  Try explaining the fundamentals simply, you&#8217;ll see how much your understanding of the topic increases to accomplish that feat.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/01/26/tim-fords-entry-level-content-challenge/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">41338</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Querying Deadlocks From System_Health XEvent</title>
		<link>https://simplesqlserver.com/2016/01/25/querying-deadlocks-from-system_health-xevent/</link>
					<comments>https://simplesqlserver.com/2016/01/25/querying-deadlocks-from-system_health-xevent/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 25 Jan 2016 12:00:05 +0000</pubDate>
				<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40822</guid>

					<description><![CDATA[It&#8217;s easy to query XEvents to see some of the basic info in deadlocks, including the system_health session which is already capturing this information by default. This script will, by default, read from system_health.  However, change the name of one parameter at the start to have it read from any active session.  I pull the [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s easy to query XEvents to see some of the basic info in deadlocks, including the system_health session which is already capturing this information by default.</p>
<p>This script will, by default, read from system_health.  However, change the name of one parameter at the start to have it read from any active session.  I pull the file name automatically, so the session has to be active to avoid many changes to the script.</p>
<h2>The Script</h2>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DECLARE </span>@SessionName <span style="color:blue;">SysName 

SELECT </span>@SessionName <span style="color:gray;">= </span><span style="color:red;">'system_health'

</span><span style="color:green;">/* 
SELECT  Session_Name = s.name, s.blocked_event_fire_time, s.dropped_buffer_count, s.dropped_event_count, s.pending_buffers
FROM sys.dm_xe_session_targets t
	INNER JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address
WHERE target_name = 'event_file'
--*/

</span><span style="color:blue;">IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'tempdb..#Events'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#Events
<span style="color:blue;">END

DECLARE </span>@Target_File <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">)
	, </span>@Target_Dir <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">)
	, </span>@Target_File_WildCard <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">)

</span><span style="color:blue;">SELECT </span>@Target_File <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>t<span style="color:gray;">.</span>target_data <span style="color:blue;">as XML</span><span style="color:gray;">).</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'EventFileTarget[1]/File[1]/@name'</span><span style="color:gray;">, </span><span style="color:red;">'NVARCHAR(256)'</span><span style="color:gray;">)
</span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_xe_session_targets </span>t
	<span style="color:gray;">INNER JOIN </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_xe_sessions </span>s <span style="color:blue;">ON </span>s<span style="color:gray;">.</span><span style="color:blue;">address </span><span style="color:gray;">= </span>t<span style="color:gray;">.</span>event_session_address
<span style="color:blue;">WHERE </span>s<span style="color:gray;">.</span>name <span style="color:gray;">= </span>@SessionName
	<span style="color:gray;">AND </span>t<span style="color:gray;">.</span>target_name <span style="color:gray;">= </span><span style="color:red;">'event_file'

</span><span style="color:blue;">SELECT </span>@Target_Dir <span style="color:gray;">= LEFT(</span>@Target_File<span style="color:gray;">, </span><span style="color:magenta;">Len</span><span style="color:gray;">(</span>@Target_File<span style="color:gray;">) - </span><span style="color:magenta;">CHARINDEX</span><span style="color:gray;">(</span><span style="color:red;">'\'</span><span style="color:gray;">, </span><span style="color:magenta;">REVERSE</span><span style="color:gray;">(</span>@Target_File<span style="color:gray;">))) 

</span><span style="color:blue;">SELECT </span>@Target_File_WildCard <span style="color:gray;">= </span>@Target_Dir <span style="color:gray;">+ </span><span style="color:red;">'\'  </span><span style="color:gray;">+ </span>@SessionName <span style="color:gray;">+ </span><span style="color:red;">'_*.xel'

</span><span style="color:green;">--Keep this as a separate table because it's called twice in the next query.  You don't want this running twice.
</span><span style="color:blue;">SELECT </span>DeadlockGraph <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>event_data <span style="color:blue;">AS XML</span><span style="color:gray;">)
	, </span>DeadlockID <span style="color:gray;">= </span><span style="color:magenta;">Row_Number</span><span style="color:gray;">() </span><span style="color:blue;">OVER</span><span style="color:gray;">(</span><span style="color:blue;">ORDER BY </span><span style="color:magenta;">file_name</span><span style="color:gray;">, </span>file_offset<span style="color:gray;">)
</span><span style="color:blue;">INTO </span>#Events
<span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span>fn_xe_file_target_read_file<span style="color:gray;">(</span>@Target_File_WildCard<span style="color:gray;">, null, null, null) </span><span style="color:blue;">AS </span>F
<span style="color:blue;">WHERE </span>event_data <span style="color:gray;">like </span><span style="color:red;">'&lt;event name="xml_deadlock_report%'

</span><span style="color:gray;">;</span><span style="color:blue;">WITH </span>Victims <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>VictimID <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Victims<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@id'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(50)'</span><span style="color:gray;">)
		, </span>e<span style="color:gray;">.</span>DeadlockID 
	<span style="color:blue;">FROM </span>#Events e
		<span style="color:gray;">CROSS APPLY </span>e<span style="color:gray;">.</span>DeadlockGraph<span style="color:gray;">.</span>nodes<span style="color:gray;">(</span><span style="color:red;">'/event/data/value/deadlock/victim-list/victimProcess'</span><span style="color:gray;">) </span><span style="color:blue;">as </span>Deadlock<span style="color:gray;">(</span>Victims<span style="color:gray;">)
)
, </span>DeadlockObjects <span style="color:blue;">AS
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT DISTINCT </span>e<span style="color:gray;">.</span>DeadlockID
		<span style="color:gray;">, </span>ObjectName <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Resources<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@objectname'</span><span style="color:gray;">, </span><span style="color:red;">'nvarchar(256)'</span><span style="color:gray;">)
	</span><span style="color:blue;">FROM </span>#Events e
		<span style="color:gray;">CROSS APPLY </span>e<span style="color:gray;">.</span>DeadlockGraph<span style="color:gray;">.</span>nodes<span style="color:gray;">(</span><span style="color:red;">'/event/data/value/deadlock/resource-list/*'</span><span style="color:gray;">) </span><span style="color:blue;">as </span>Deadlock<span style="color:gray;">(</span>Resources<span style="color:gray;">)
)
</span><span style="color:blue;">SELECT </span><span style="color:gray;">*
</span><span style="color:blue;">FROM
</span><span style="color:gray;">(
	</span><span style="color:blue;">SELECT </span>e<span style="color:gray;">.</span>DeadlockID
		<span style="color:gray;">, </span>TransactionTime <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@lasttranstarted'</span><span style="color:gray;">, </span><span style="color:red;">'datetime'</span><span style="color:gray;">)
		, </span>DeadlockGraph
		<span style="color:gray;">, </span>DeadlockObjects <span style="color:gray;">= </span><span style="color:magenta;">substring</span><span style="color:gray;">((</span><span style="color:blue;">SELECT </span><span style="color:gray;">(</span><span style="color:red;">', ' </span><span style="color:gray;">+ </span>o<span style="color:gray;">.</span>ObjectName<span style="color:gray;">)
							</span><span style="color:blue;">FROM </span>DeadlockObjects o
							<span style="color:blue;">WHERE </span>o<span style="color:gray;">.</span>DeadlockID <span style="color:gray;">= </span>e<span style="color:gray;">.</span>DeadlockID
							<span style="color:blue;">ORDER BY </span>o<span style="color:gray;">.</span>ObjectName
							<span style="color:blue;">FOR XML PATH </span><span style="color:gray;">(</span><span style="color:red;">''</span><span style="color:gray;">)
							), </span>3<span style="color:gray;">, </span>4000<span style="color:gray;">)
		, </span>Victim <span style="color:gray;">= </span><span style="color:blue;">CASE WHEN </span>v<span style="color:gray;">.</span>VictimID <span style="color:gray;">IS NOT NULL 
							</span><span style="color:blue;">THEN </span>1 
						<span style="color:blue;">ELSE </span>0 
						<span style="color:blue;">END
		</span><span style="color:gray;">, </span>SPID <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@spid'</span><span style="color:gray;">, </span><span style="color:red;">'int'</span><span style="color:gray;">)
		, </span>ProcedureName <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'executionStack[1]/frame[1]/@procname[1]'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(200)'</span><span style="color:gray;">)
		, </span>LockMode <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@lockMode'</span><span style="color:gray;">, </span><span style="color:red;">'char(1)'</span><span style="color:gray;">)
		, </span>Code <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'executionStack[1]/frame[1]'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(1000)'</span><span style="color:gray;">)
		, </span>ClientApp <span style="color:gray;">= </span><span style="color:blue;">CASE </span><span style="color:gray;">LEFT(</span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@clientapp'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(100)'</span><span style="color:gray;">), </span>29<span style="color:gray;">)
						</span><span style="color:blue;">WHEN </span><span style="color:red;">'SQLAgent - TSQL JobStep (Job '
							</span><span style="color:blue;">THEN </span><span style="color:red;">'SQLAgent Job: ' </span><span style="color:gray;">+ (</span><span style="color:blue;">SELECT </span>name <span style="color:blue;">FROM </span>msdb<span style="color:gray;">..</span>sysjobs sj <span style="color:blue;">WHERE </span><span style="color:magenta;">substring</span><span style="color:gray;">(</span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@clientapp'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(100)'</span><span style="color:gray;">),</span>32<span style="color:gray;">,</span>32<span style="color:gray;">)=(</span><span style="color:magenta;">substring</span><span style="color:gray;">(</span><span style="color:green;">sys</span><span style="color:gray;">.</span>fn_varbintohexstr<span style="color:gray;">(</span>sj<span style="color:gray;">.</span>job_id<span style="color:gray;">),</span>3<span style="color:gray;">,</span>100<span style="color:gray;">))) + </span><span style="color:red;">' - ' </span><span style="color:gray;">+ </span><span style="color:magenta;">SUBSTRING</span><span style="color:gray;">(</span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@clientapp'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(100)'</span><span style="color:gray;">), </span>67<span style="color:gray;">, </span><span style="color:magenta;">len</span><span style="color:gray;">(</span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@clientapp'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(100)'</span><span style="color:gray;">))-</span>67<span style="color:gray;">)
						</span><span style="color:blue;">ELSE </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@clientapp'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(100)'</span><span style="color:gray;">)
						</span><span style="color:blue;">END 
		</span><span style="color:gray;">, </span>HostName <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@hostname'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(20)'</span><span style="color:gray;">)
		, </span>LoginName <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@loginname'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(20)'</span><span style="color:gray;">)
		, </span>InputBuffer <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'inputbuf[1]'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(1000)'</span><span style="color:gray;">)
	</span><span style="color:blue;">FROM </span>#Events e
		<span style="color:gray;">CROSS APPLY </span>e<span style="color:gray;">.</span>DeadlockGraph<span style="color:gray;">.</span>nodes<span style="color:gray;">(</span><span style="color:red;">'/event/data/value/deadlock/process-list/process'</span><span style="color:gray;">) </span><span style="color:blue;">as </span>Deadlock<span style="color:gray;">(</span>Process<span style="color:gray;">)
		LEFT JOIN </span>Victims v <span style="color:blue;">ON </span>v<span style="color:gray;">.</span>DeadlockID <span style="color:gray;">= </span>e<span style="color:gray;">.</span>DeadlockID <span style="color:gray;">AND </span>v<span style="color:gray;">.</span>VictimID <span style="color:gray;">= </span>Deadlock<span style="color:gray;">.</span>Process<span style="color:gray;">.</span><span style="color:blue;">value</span><span style="color:gray;">(</span><span style="color:red;">'@id'</span><span style="color:gray;">, </span><span style="color:red;">'varchar(50)'</span><span style="color:gray;">)
) </span>X <span style="color:green;">--In a subquery to make filtering easier (use column names, not XML parsing), no other reason
</span><span style="color:blue;">ORDER BY </span>DeadlockID <span style="color:blue;">DESC
</span></pre>
<h2>System_health session</h2>
<p>The system_health XEvent by default is turned on, captures deadlocks, and is limited to four 5MB files.  You can change all three of those, but it&#8217;s not common for people to do that.</p>
<p>Yes, you&#8217;re limited to more recent events, but the information is there.  If you find the limitations are too much, you have a couple options that should be considered in this order.</p>
<ol>
<li>Is anything else capturing deadlocks?  For me, I have Idera Diagnostic Manager capturing deadlocks as well for my production servers.  This is my main source of looking at deadlocks, so I actually rarely use system_health for this purpose in production.  The first goal is to avoid doing anything additional.</li>
<li>Consider setting system_health to use bigger files (not too big) or have more rollover files. The more you do the more resources it will take to read it all at once, so don&#8217;t go crazy.  However, if you have 5 days of data when you wish you had 7, it&#8217;s not horrible to double the size of data you retain.  The second goal is to increase disk usage before you add more overhead.</li>
<li>Consider a separate XEvent session.  Ugh&#8230;I wish I didn&#8217;t have to throw this out there as an option.  Love your servers, don&#8217;t add more overhead if you don&#8217;t have to.  The last resort is to not end up here at your last resort.</li>
</ol>
<h2>Idera Diagnostic Manager</h2>
<p>At the time of me writing this, I have a script to look at all of the deadlocks in Idera DM that I use quite regularly for my production servers.  However, they just came out with a new major version and I want to make sure my query runs there before posting my script publically.  If you don&#8217;t care and just want the script, email me.</p>
<h2>Viewing the Deadlock</h2>
<p>The details of this script give you a great overview of the deadlock.  You know the code involved on each side, the tables and applications involved, etc.   It&#8217;s enough to look for trends, know if a deadlock was one you expected, and other basics.</p>
<p>However, this is just a summary list of your recent deadlocks, it&#8217;s not the details.  For me, I&#8217;m very happy just looking at the XML output to see the details.  This is how I look at it from XEvents and my monitoring software.  That makes me odd, and I&#8217;m ok with that.</p>
<p>Jonathan Kehayias (<a href="https://www.sqlskills.com/blogs/jonathan/" target="_blank">b</a>|<a href="http://twitter.com/SQLPoolBoy" target="_blank">t</a>) talks about viewing them in SQL Sentry Plan Explorer Pro in his post <a href="https://www.sqlskills.com/blogs/jonathan/graphically-viewing-extended-events-deadlock-graphs/" target="_blank">Graphically Viewing Extended Events Deadlock Graphs</a>.  There&#8217;s no single right answer on how to view deadlocks, so do what works best for you.</p>
<h2>Troubleshooting Deadlocks</h2>
<p>This topic goes well beyond what I&#8217;m going for in this post.  Jonathan Kehayias gets into it some in his post <a href="https://www.sqlskills.com/blogs/jonathan/the-accidental-dba-day-29-of-30-troubleshooting-deadlocks/" target="_blank">The Accidental DBA (Day 29 of 30): Troubleshooting Deadlocks</a>, which is just a start.  The book he co-authored with Ted Krueger (<a href="http://blogs.lessthandot.com/index.php/author/onpnt/" target="_blank">b</a>|<a href="http://twitter.com/onpnt" target="_blank">t</a>), <a href="http://www.sqlservercentral.com/articles/books/76296/" target="_blank">Troubleshooting SQL Server &#8211; A Guide for the Accidental DBA</a>, is available as a free PDF and has an entire chapter on this subject.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/01/25/querying-deadlocks-from-system_health-xevent/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40822</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Shrinking Database Data Files</title>
		<link>https://simplesqlserver.com/2016/01/19/shrinking-database-data-files/</link>
					<comments>https://simplesqlserver.com/2016/01/19/shrinking-database-data-files/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Tue, 19 Jan 2016 12:00:53 +0000</pubDate>
				<category><![CDATA[Entry Level]]></category>
		<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[General Chat]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[entrylevel]]></category>
		<category><![CDATA[Shrink Database]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40747</guid>

					<description><![CDATA[Normal DBAs will sum up shrinking a database data file in one word: NO! I&#8217;ll talk to you about this subject because I&#8217;m not normal.  Still, my goal is that you&#8217;ll go from &#8220;why can&#8217;t I shrink my files&#8221; to &#8220;I don&#8217;t want to shrink my files.&#8221; Truth be told, even when I HAVE to shrink [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Normal DBAs will sum up shrinking a database data file in one word: NO!</p>
<p>I&#8217;ll talk to you about this subject because I&#8217;m not normal.  Still, my goal is that you&#8217;ll go from &#8220;why can&#8217;t I shrink my files&#8221; to &#8220;I don&#8217;t want to shrink my files.&#8221;</p>
<p><strong>Truth be told, even when I HAVE to shrink files, I don&#8217;t want to.</strong></p>
<p>Basically, you needed the space at one point in time, so you&#8217;ll probably need it again.  Unless you changed a process or something extremely rare happened, you&#8217;ll use the space again and there&#8217;s no reason to take on the issues involved with shrinking just to have a file grow again.  While this advice is mostly the same for log files, it&#8217;s different enough where I&#8217;m making a separate post for shrinking database log files</p>
<h2>When should you shrink data files</h2>
<p>First, a process changed where you&#8217;ll never need that amount of space again, such as the database used to keep 10 years of history but now an automated process keeps it pruned to 6 months.  This change also left you with a very significant amount of free space that you need for another purpose.</p>
<p>Really, that&#8217;s about it.</p>
<h2>What happens when you shrink data files</h2>
<p>The most common way to shrink a file is to have it reorganize pages before releasing free space, so I&#8217;ll cover that.  It basically has two steps:</p>
<ol>
<li>Fragment the file as much as possible (killing performance)</li>
<li>Truncate the end of the file</li>
</ol>
<p>By &#8220;reorganize&#8221; it means take the tail end of the file after the size you said you&#8217;d like the file to be shrunk to, find every populated page of data after that point, then move those pages to the first available spot you find in the file.  When the end of the file is empty, truncate it.</p>
<p>It&#8217;s an expensive process that leaves a big mess behind, but there are times you&#8217;ll need to go through with it (kicking and screaming).  If you do, schedule an index maintenance job immediately after this to undo most of the damage you just did and make sure the server still loves you.</p>
<p>Think about this, you&#8217;re considering kicking off a process where you have to tell the server you&#8217;re sorry afterwards.  You better have a good reason for this.  A 100 GB database with only 2 GB used <strong><span style="text-decoration:underline;">may be</span> </strong>a good reason to shrink.  A 100 GB database with 75 GB used is normal and healthy.</p>
<p>If you are going to shrink, make sure the database won&#8217;t be using that space again.  If you have a 100 GB database with only 2 GB used, does it populate, process, and purge 90 GB in an overnight process?  Find out before you shrink.</p>
<h2>Manual Shrinking</h2>
<p>If I haven&#8217;t scared you off yet (and I hope I did) then here&#8217;s how you shrink.</p>
<p>Here&#8217;s the link how to shrink a file using T-SQL: <a href="https://msdn.microsoft.com/en-us/library/ms189493.aspx" target="_blank">DBCC ShrinkFile</a></p>
<p>Here&#8217;s the link how to shrink a file using the GUI: <a href="https://msdn.microsoft.com/en-us/library/ms190757.aspx" target="_blank">SSMS Shrink File</a></p>
<p>There are Shrink Database options as well.  I don&#8217;t use it and don&#8217;t recommend it.  You know what files you want to shrink, don&#8217;t shrink them all.  When this is used it&#8217;s typically because people want to shrink their data files, but they mess up their log files while their at it.  Be nice to yourself, don&#8217;t use shrink database.</p>
<h2>What about the Auto-Shrink option</h2>
<p>No.</p>
<p>Just no.</p>
<p>It&#8217;s not a feature, it&#8217;s a threat.</p>
<p>If you have a certain amount of free space it will automatically shrink your database causing all the problems I just discussed, but it will probably kick in during your busiest time of day.  Then it won&#8217;t follow that up with index maintenance, so it just left you hopping all over your disk to find data.  The best part is that it didn&#8217;t check to see if you would need that space again, so it&#8217;ll probably grow tomorrow to get that space back.</p>
<p>In the example above of a 100 GB database that only uses 2 GB during the day but populates 90 GB for overnight processing, those 2 GB will be horribly fragmented, you&#8217;ll spend the resources shrinking, then you&#8217;ll take the performance hit growing the database every night as it puts the growth anywhere it can which typically means physical file fragmentation as well.  In the mean time your monitoring software will show that you have tons of free space, right up until you get the disk full error.  You can auto-shrink that database and get all those issues, or you can just let it do its job as you do yours without a maintenance nightmare.</p>
<p>The <a href="https://connect.microsoft.com/SQLServer/feedback/details/427685/please-remove-auto-shrink-from-maintenance-plans" target="_blank">Connect Item</a> on this is marked as fixed, stating that they&#8217;ll consider removing it in future versions.  This wasn&#8217;t a joke opened by some wannabe DBA.  This was serious and opened by someone who went on to become the <a href="http://www.sqlpass.org/AboutPASS/ThomasLaRock.aspx" target="_blank">president of the Professional Association for SQL Server</a>.</p>
<p>To be fair, we all word our feelings on this differently.  I say no, Tom LaRock says <a href="http://thomaslarock.com/2009/03/when-to-use-auto-shrink/" target="_blank">never</a>, Paul Randal says <a href="http://www.sqlskills.com/blogs/paul/auto-shrink-turn-it-off/" target="_blank">Turn It Off!</a>, and Brent Ozar is colorful enough to come up with the term <a href="http://www.brentozar.com/blitz/auto-shrink-enabled/" target="_blank">Hamster Wheel of Death</a>.</p>
<p>As of the time of this writing, no one has used the term &#8220;Magical Unicorn&#8221; to describe this feature.</p>
<h2>Sum it up</h2>
<ol>
<li>Don&#8217;t shrink data files.</li>
<li>If you didn&#8217;t listen to #1, why?</li>
<li>Follow it up with index maintenance.</li>
<li>Every time you try to automate this process a unicorn dies.</li>
</ol>
<h2>Entry-Level Content Challenge</h2>
<p>I&#8217;m taking Tim Ford&#8217;s (<a href="http://thesqlagentman.com/" target="_blank"><span style="color:#0066cc;">b</span></a>|<a href="https://twitter.com/sqlagentman" target="_blank"><span style="color:#0066cc;">t</span></a>) <a href="http://thesqlagentman.com/2016/01/entry-level-content/" target="_blank"><span style="color:#0066cc;">Entry-Level Content Challenge</span></a>, and this is my first post with the #EntryLevel tag.  The challenge is to have one post that earns that tag each month, but I hope to exceed that.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/01/19/shrinking-database-data-files/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40747</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>SQL Saturday Cleveland #SQLSat473</title>
		<link>https://simplesqlserver.com/2016/01/11/sql-saturday-cleveland-sqlsat473/</link>
					<comments>https://simplesqlserver.com/2016/01/11/sql-saturday-cleveland-sqlsat473/#respond</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 11 Jan 2016 13:00:55 +0000</pubDate>
				<category><![CDATA[General Chat]]></category>
		<category><![CDATA[Presentation]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=39809</guid>

					<description><![CDATA[On February 6, 2016, Cleveland is hosting a free training event for SQL Server.  This has a lot of the great stuff from the big, paid events, and skips some of the negatives. There&#8217;s a great team from the North Ohio SQL Server Users Group that took on a very difficult task to put all [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>On February 6, 2016, Cleveland is hosting a <a href="http://www.sqlsaturday.com/473/EventHome.aspx" target="_blank">free training event for SQL Server</a>.  This has a lot of the great stuff from the big, paid events, and skips some of the negatives.</p>
<p>There&#8217;s a great team from the <a href="http://ohionorth.sqlpass.org/" target="_blank"><span style="color:#0066cc;">North Ohio SQL Server Users Group</span></a> that took on a very difficult task to put all of this together.  They hand selected every presentation, being forced to turn away over half of the abstracts and many speakers.  What they came up with was a variety of topics that span the breadth of SQL Server and the experience levels of the professionals working with databases.</p>
<h2>What is <span style="text-decoration:underline;">this</span> SQL Saturday?</h2>
<p>For those of you who have never been to a SQL Saturday, here&#8217;s how this one works.  Other SQL Saturdays will vary from this slightly, but not too much.</p>
<p>There are 6 training rooms with hour-long presentations running simultaneously in each.  You move from room to room between each session choosing the ones that apply to you the most.  The <a href="http://www.sqlsaturday.com/473/Sessions/Schedule.aspx" target="_blank">schedule for the sessions</a> is posted in advance, and it&#8217;s impressive.</p>
<p>An optional $10 lunch, which is typically very obvious that no one is making a profit off of, is the closest thing the event has to an entrance fee. The stuff you&#8217;d expect to cost you is provided by volunteers and sponsors who pull together the venue, presenters, and community for you.</p>
<p>There&#8217;s time to network with vendors, attendees, and speakers before the event, during breaks and lunch, and often end up in conversations that last throughout the next session.  This differs from the big events in that it&#8217;s much smaller and personable with most attendees being relatively local.  The people you meet at a regional event like this are going to be more relevant to your life because they&#8217;re part of your local community.</p>
<p>The event at <a href="https://www.onbase.com/" target="_blank">Hyland Software</a> concludes with prizes ranging from $100 gift cards to new laptops being handed out to random attendees.  Yes, you can basically get paid to go to free training, which I still don&#8217;t quite understand.</p>
<p>Then there&#8217;s the after-party, also provided by the SQL Saturday volunteers and sponsors.  There&#8217;s food, fun, and more time to get to know some great people.</p>
<p>The venue can&#8217;t hold everyone who should come, and often can&#8217;t hold everyone who wants to come.  <a href="http://www.sqlsaturday.com/473/EventHome.aspx" target="_blank">Register now</a> to make sure there&#8217;s room for you, and make sure to cancel if your plans fall through.</p>
<h2>What is SQL Saturday?</h2>
<p>Not just this event, what is <a href="http://www.sqlsaturday.com/About.aspx" target="_blank">SQL Saturday as a whole</a>?  It&#8217;s a program specifically set up to help everyone in the community to develop.</p>
<p>The obvious people benefiting are the attendees who get a chance to see all the sessions and meet the vendors.  You can see how they would benefit from here, but not quite how much until you&#8217;re at the event.</p>
<p>The less obvious are the speakers, some speaking publically for the first time.  As one of the speakers, I can personally say that I&#8217;ve grown more as a person and as a professional than I thought possible.  It&#8217;s a step I highly recommend, and one I&#8217;m very grateful to have with SQL Saturday.</p>
<p>The even less obvious are the vendors.  They&#8217;re speaking to the public, not just their established customers, getting candid feedback on what works, what doesn&#8217;t work, and how they can make their offerings better.  Even if they don&#8217;t make a single sale from attending the event, they can always come out ahead.</p>
<p>SQL Saturday didn&#8217;t just pop out of nowhere, and <a href="https://ozar.me/2015/11/sql-server-dbas-today-lets-thank-steve-jones/" target="_blank">someone told me we should give thanks</a> to the people who started it all.</p>
<h2 style="text-align:center;"><strong>Thank you:</strong></h2>
<h2 style="text-align:center;"><strong>Steve Jones (<a href="http://www.sqlservercentral.com/blogs/steve_jones/default.aspx" target="_blank">b</a>|<a href="https://twitter.com/way0utwest" target="_blank">t</a>)</strong></h2>
<h2 style="text-align:center;"><strong>Andy Warren (<a href="http://www.sqlandy.com/" target="_blank">b</a>|<a href="https://twitter.com/sqlandy" target="_blank">t</a>)</strong></h2>
<h2 style="text-align:center;"><strong>Brian Knight (<a href="http://www.bidn.com/blogs/BrianKnight" target="_blank">b</a>|<a href="https://twitter.com/brianknight" target="_blank">t</a>)</strong></h2>
<p>These event are around the world with one or more occurring almost every weekend of the year as an annual event in most major cities.  See <a href="http://www.sqlsaturday.com/" target="_blank">SQL Saturday&#8217;s website</a> for a full list of what&#8217;s coming up.  If your city isn&#8217;t coming up soon, look in past events because most cities hold it about the same time every year.</p>
<h2>Who&#8217;s speaking?</h2>
<p>Where do I start?  Well, by listing EVERYONE on the <a href="http://www.sqlsaturday.com/473/Sessions/Schedule.aspx" target="_blank">current schedule</a>, along with the blogs and twitter handles I&#8217;m aware of.</p>
<ul>
<li>Deji Akomolafe (<a href="http://blogs.vmware.com/apps" target="_blank">b</a>|<a href="https://twitter.com/dejify" target="_blank">t</a>)</li>
<li>Jim Arko (<a href="http://www.jimarko.com/" target="_blank">b</a>|<a href="https://twitter.com/jimarko" target="_blank">t</a>)</li>
<li>Adam Belebczuk (<a href="http://www.sqldiablo.com/" target="_blank">b</a>|<a href="https://twitter.com/SQLDiablo" target="_blank">t</a>) &lt;&#8211; Organizer</li>
<li>Chris Bell (<a href="http://www.wateroxconsulting.com/blog" target="_blank">b</a>|<a href="https://twitter.com/cbelldba" target="_blank">t</a>)</li>
<li>Delora Bradish (<a href="http://www.delorabradish.com/" target="_blank">b</a>|<a href="https://twitter.com/sqlsme" target="_blank">t</a>)</li>
<li>Mindy Curnutt (<a href="http://www.mindycurnutt.com/" target="_blank">b</a>|<a href="https://twitter.com/sqlgirl" target="_blank">t</a>)</li>
<li>Aaron Cutshall (<a href="https://twitter.com/ancutshall" target="_blank">t</a>)</li>
<li>Joey D&#8217;Antoni (<a href="http://joeydantoni.com/" target="_blank">b</a>|<a href="https://twitter.com/jdanton" target="_blank">t</a>)</li>
<li>David Eldersveld (<a href="http://www.blue-granite.com/blog/author/david-eldersveld" target="_blank">b</a>|<a href="https://twitter.com/dr_eldersveld" target="_blank">t</a>)</li>
<li>Cassandra Faris (<a href="https://twitter.com/cassandrafaris" target="_blank">t</a>)</li>
<li>Kevin Feasel (<a href="http://www.catallaxyservices.com/" target="_blank">b</a>|<a href="https://twitter.com/feaselkl" target="_blank">t</a>)</li>
<li>Frank Gill (<a href="http://http//skreebydba.com/" target="_blank">b</a>|<a href="https://twitter.com/skreebydba" target="_blank">t</a>)</li>
<li>Amy Herold (<a href="http://www.sqlkitten.com/" target="_blank">b</a>|<a href="https://twitter.com/texasamy" target="_blank">t</a>)</li>
<li>Paul Hiles (<a href="https://twitter.com/PaulGHiles" target="_blank">t</a>) &lt;&#8211; Organizer</li>
<li>Steve Hood (<a href="https://simplesqlserver.com/" target="_blank">b</a>|<a href="https://twitter.com/SteveHoodSQL" target="_blank">t</a>) &lt;&#8211; What am I doing on a list this awesome?</li>
<li>Michael John</li>
<li>Jonathan Kehayias (<a href="https://www.sqlskills.com/blogs/jonathan/" target="_blank">b</a>|<a href="http://twitter.com/SQLPoolBoy" target="_blank">t</a>)</li>
<li>Dave Mattingly (<a href="https://twitter.com/blackwyrm" target="_blank">t</a>)</li>
<li>David Maxwell (<a href="https://dmmaxwell.wordpress.com/" target="_blank">b</a>|<a href="https://twitter.com/dmmaxwell" target="_blank">t</a>)</li>
<li>Evelyn Maxwell &lt;&#8211; bright future ahead for her</li>
<li>Eugene Meidinger (<a href="http://www.sqlgene.com/" target="_blank">b</a>|<a href="https://twitter.com/sqlgene" target="_blank">t</a>)</li>
<li>Kon Melamud (<a href="https://twitter.com/kmelamud" target="_blank">t</a>)</li>
<li>Ben Miller (<a href="http://dbaduck.com/" target="_blank">b</a>|<a href="https://twitter.com/DBAduck" target="_blank">t</a>)</li>
<li>Jeff Moden (<a href="http://www.sqlservercentral.com/Authors/Articles/Jeff_Moden/80567/" target="_blank">b</a>)</li>
<li>Colleen Morrow (<a href="http://colleenmorrow.com/" target="_blank">b</a>|<a href="https://twitter.com/ClevelandDBA" target="_blank">t</a>) &lt;&#8211; Organizer</li>
<li>Matt Nelson (<a href="http://wwww.nelsonsweb.net/" target="_blank">b</a>|<a href="https://twitter.com/http://twitter.com/mattpgh" target="_blank">t</a>)</li>
<li>Wendy Pastrick (<a href="http://wendyverse.blogspot.com/" target="_blank">b</a>|<a href="https://twitter.com/wendy_dance" target="_blank">t</a>)</li>
<li>Pat Phelan (<a href="https://twitter.com/YetAnotherSQL" target="_blank">t</a>)</li>
<li>Justin Randall (<a href="http://blogs.sqlsentry.com/justinrandall" target="_blank">b</a>|<a href="https://twitter.com/jh_randall" target="_blank">t</a>)</li>
<li>Wayne Sheffield (<a href="http://blog.waynesheffield.com/wayne" target="_blank">b</a>|<a href="https://twitter.com/DBAWayne" target="_blank">t</a>)</li>
<li>Peter Shore (<a href="https://shoresql.wordpress.com/" target="_blank">b</a>|<a href="https://twitter.com/pshore73" target="_blank">t</a>)</li>
<li>Warren Sifre (<a href="http://www.broadstrokeconsulting.com/blog/" target="_blank">b</a>|<a href="https://twitter.com/WAS_SQL" target="_blank">t</a>)</li>
<li>Julie Smith (<a href="http://datachix.com/" target="_blank">b</a>|<a href="https://twitter.com/juliechix" target="_blank">t</a>)</li>
<li>Erin Stellato (<a href="http://www.sqlskills.com/blogs/erin" target="_blank">b</a>|<a href="https://twitter.com/erinstellato" target="_blank">t</a>) &lt;&#8211; Organizer</li>
<li>Michael J. Swart (<a href="http://michaeljswart.com/" target="_blank">b</a>|<a href="https://twitter.com/MJSwart" target="_blank">t</a>)</li>
<li>Allen White (<a href="http://sqlblog.com/blogs/allen_white/default.aspx" target="_blank">b</a>|<a href="https://twitter.com/SQLRunr" target="_blank">t</a>) &lt;&#8211; Organizer</li>
<li>Andy Yun (<a href="http://sqlbek.wordpress.com/" target="_blank">b</a>|<a href="https://twitter.com/SQLBek" target="_blank">t</a>)</li>
</ul>
<p>Note that the schedule may change some before the event, but it never changes too much.</p>
<h2>What do we ask of you?</h2>
<p>Show up.</p>
<p>Despite the amazing list of speakers, we&#8217;re all volunteers.  That means we do it because we want to do it, and truly enjoy it.  If you want to help us enjoy it even more, be involved.  Talk to us and the other attendees, ask questions, and <a href="https://twitter.com/BrentO/status/675313862195957760" target="_blank">give feedback</a>.</p>
<p>If you want to absolutely blow us away, get in touch afterwards and let us know the difference it made for you.  Yes, we want this, it will absolutely make our day!  Let the organizers know what you think of the event.  Tell the presenters how you were able to use their advice at work, and even ask questions down the road.  Get in touch with the vendors to discuss their products, even if you aren&#8217;t planning a purchase, to give them real feedback and see what&#8217;s out there.  I used to think this was imposing, but now I realize this is the one of the most awesome compliments you can get.</p>
<h2>The most important part&#8230;.</h2>
<p>Show up!</p>
<p>Hope to see you there!</p>
<h2>UPDATE &#8211; 2016-01-13 &#8211; On the waiting list</h2>
<p>If you registered before today, you&#8217;re in.  If you&#8217;re registering now, you&#8217;re on the waiting list.</p>
<p>If you can&#8217;t make it, please cancel your registration so someone on the waiting list can get in.  We&#8217;ll miss you, but at least there will be someone else we won&#8217;t be missing.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/01/11/sql-saturday-cleveland-sqlsat473/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">39809</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>TempDB memory leak?</title>
		<link>https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/</link>
					<comments>https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Tue, 05 Jan 2016 15:00:32 +0000</pubDate>
				<category><![CDATA[Buffer Pool]]></category>
		<category><![CDATA[File Sizes]]></category>
		<category><![CDATA[General Chat]]></category>
		<category><![CDATA[Indexes]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[File Size]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[TempDB]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=39079</guid>

					<description><![CDATA[I found a bug where I&#8217;m seeing TempDB use more memory than it should on multiple versions of SQL Server, especially on servers set up with common best practices. There&#8217;s a workaround that has a profound affect on server performance, adding to my belief that this is a legitimate bug and leading me to open a Connect [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I found a bug where I&#8217;m seeing TempDB use more memory than it should on multiple versions of SQL Server, especially on servers set up with common best practices. There&#8217;s a workaround that has a profound affect on server performance, adding to my belief that this is a legitimate bug and leading me to open a <a href="https://connect.microsoft.com/SQLServer/feedback/details/2215297" target="_blank">Connect Item</a> on the issue.</p>
<h2>Querying the Buffer Pool</h2>
<p>I have a query to show me what&#8217;s in the buffer pool for the entire server, showing the expected results as well as excessive space being used by TempDB.  The newest version showing these details is on my post <a href="https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/" target="_blank">Querying the Buffer Pool</a>.</p>
<p>It shows number of pages in the buffer pool grouped by the database, table, and index.  The query makes use of left joins so it can see space in memory that&#8217;s not currently allocated to a specific object.</p>
<p>The results are surprising in many ways.</p>
<p>The good surprises are seeing what indexes are hogging up your buffer pool so you have an idea of where to start tuning.  I&#8217;m a huge fan of this and have blogged about it in <a href="https://simplesqlserver.com/2014/01/08/cleaning-up-the-buffer-pool/" target="_blank">Cleaning Up the Buffer Pool to Increase PLE</a>, although the name of my older post is misleading because it does more than just help memory management in SQL Server.</p>
<h2>The Bug</h2>
<p>The bad surprise was a bug which has been harassing me for quite some time now.  As I mentioned, the query will return all the space in the buffer pool, specifically the contents of <a href="https://msdn.microsoft.com/en-us/library/ms173442.aspx" target="_blank"><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_os_buffer_descriptors</span></a>, and does a left join to the tables leading up to and including sys.indexes so space not currently allocated to a table will show up.  The problem is that the space that shows up as unallocated for TempDB is much larger than expected, in this case taking up 1/3 of my buffer pool.</p>
<p><img loading="lazy" data-attachment-id="39013" data-permalink="https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/querybufferpool_tempdb/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg" data-orig-size="1142,35" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="QueryBufferPool_TempDB" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg?w=652" class="alignnone wp-image-39013" src="https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg" alt="QueryBufferPool_TempDB" width="1085" height="33" srcset="https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg?w=1085&amp;h=33 1085w, https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg?w=150&amp;h=5 150w, https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg?w=300&amp;h=9 300w, https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg?w=768&amp;h=24 768w, https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg?w=1024&amp;h=31 1024w, https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg 1142w" sizes="(max-width: 1085px) 100vw, 1085px" /></p>
<p>On this post I&#8217;m talking about a single server, but the problem wasn&#8217;t limited to a single server.  It showed up at the same time, caused by the same change (implementing a common best practice), partially resolved by the same partial rollback (undoing the best practice) on SQL 2008 R2, SQL 2012, and SQL 2014.</p>
<h2>Details About the Bug</h2>
<p>So the query I have on yesterday&#8217;s post, <a href="https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/" target="_blank">Querying the Buffer Pool</a>, showed I had unallocated space in TempDB in memory, and a lot of it.  However, it doesn&#8217;t show details.</p>
<p>To start looking at the details, what kind of pages are these that exist in sys.dm_os_buffer_descriptors, but not in sys.allocation_units?</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT </span>bd<span style="color:gray;">.</span>page_type
	<span style="color:gray;">, </span>MB <span style="color:gray;">= </span><span style="color:magenta;">count</span><span style="color:gray;">(</span>1<span style="color:gray;">) / </span>128
<span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_os_buffer_descriptors </span>bd
	<span style="color:gray;">LEFT JOIN </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">allocation_units </span>au <span style="color:blue;">ON </span>bd<span style="color:gray;">.</span>allocation_unit_id <span style="color:gray;">= </span>au<span style="color:gray;">.</span>allocation_unit_id
<span style="color:blue;">WHERE </span>bd<span style="color:gray;">.</span>database_id <span style="color:gray;">= </span>2 <span style="color:green;">--TempDB
	</span><span style="color:gray;">AND </span>bd<span style="color:gray;">.</span>is_modified <span style="color:gray;">= </span>0 <span style="color:green;">--Let's not play dirty, only clean pages
	</span><span style="color:gray;">AND </span>au<span style="color:gray;">.</span>allocation_unit_id <span style="color:gray;">IS NULL </span><span style="color:green;">--It's not even allocated
</span><span style="color:blue;">GROUP BY </span>bd<span style="color:gray;">.</span>page_type 
<span style="color:blue;">ORDER BY </span>2 <span style="color:blue;">DESC
</span></pre>
<p><img loading="lazy" data-attachment-id="39152" data-permalink="https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/tempdb_bufferpool_unallocated/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_bufferpool_unallocated.jpg" data-orig-size="188,75" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="TempDB_BufferPool_Unallocated" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_bufferpool_unallocated.jpg?w=188" class="alignnone size-full wp-image-39152" src="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_bufferpool_unallocated.jpg" alt="TempDB_BufferPool_Unallocated" width="188" height="75" srcset="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_bufferpool_unallocated.jpg 188w, https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_bufferpool_unallocated.jpg?w=150&amp;h=60 150w" sizes="(max-width: 188px) 100vw, 188px" /></p>
<p>Ok, so we&#8217;re dealing with typical data in TempDB.  Well, other than it not being allocated, of course.</p>
<p>So I run another query to get more details.  This time I want to look inside the pages to see if they tell a different story.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SELECT TOP </span>100 bd<span style="color:gray;">.*
</span><span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_os_buffer_descriptors </span>bd
	<span style="color:gray;">LEFT JOIN </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">allocation_units </span>au <span style="color:blue;">ON </span>bd<span style="color:gray;">.</span>allocation_unit_id <span style="color:gray;">= </span>au<span style="color:gray;">.</span>allocation_unit_id
<span style="color:blue;">WHERE </span>bd<span style="color:gray;">.</span>database_id <span style="color:gray;">= </span>2 <span style="color:green;">--TempDB
	</span><span style="color:gray;">AND </span>bd<span style="color:gray;">.</span>is_modified <span style="color:gray;">= </span>0 <span style="color:green;">--Let's not play dirty, only clean pages
	</span><span style="color:gray;">AND </span>au<span style="color:gray;">.</span>allocation_unit_id <span style="color:gray;">IS NULL </span><span style="color:green;">--It's not even allocated</span>
</pre>
<p><img loading="lazy" data-attachment-id="39166" data-permalink="https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/tempdb_pagelevel_unallocated/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg" data-orig-size="901,207" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="TempDB_PageLevel_Unallocated" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg?w=652" class="alignnone size-full wp-image-39166" src="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg" alt="TempDB_PageLevel_Unallocated" width="901" height="207" srcset="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg 901w, https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg?w=150&amp;h=34 150w, https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg?w=300&amp;h=69 300w, https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg?w=768&amp;h=176 768w" sizes="(max-width: 901px) 100vw, 901px" /></p>
<p>Then I follow that up with Paul Randal&#8217;s <a href="http://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/06/10/625659.aspx" target="_blank">How to use DBCC PAGE</a>, which comes with all the disclaimers about using an undocumented and unsupported trace flag and command.  This one isn&#8217;t horrible in my mind or Paul&#8217;s comments, but remember the undocumented and unsupported parts.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DBCC </span>TRACEON <span style="color:gray;">(</span>3604<span style="color:gray;">);

</span><span style="color:blue;">DBCC PAGE </span><span style="color:gray;">(</span>2<span style="color:gray;">, </span>5<span style="color:gray;">, </span>502219	<span style="color:gray;">, </span>0<span style="color:gray;">)
</span><span style="color:blue;">DBCC PAGE </span><span style="color:gray;">(</span>2<span style="color:gray;">, </span>5<span style="color:gray;">, </span>374929	<span style="color:gray;">, </span>0<span style="color:gray;">)
</span><span style="color:blue;">DBCC PAGE </span><span style="color:gray;">(</span>2<span style="color:gray;">, </span>5<span style="color:gray;">, </span>69868	<span style="color:gray;">, </span>0<span style="color:gray;">)
</span><span style="color:blue;">DBCC PAGE </span><span style="color:gray;">(</span>2<span style="color:gray;">, </span>5<span style="color:gray;">, </span>453687	<span style="color:gray;">, </span>0<span style="color:gray;">)
</span><span style="color:blue;">DBCC PAGE </span><span style="color:gray;">(</span>2<span style="color:gray;">, </span>5<span style="color:gray;">, </span>214988	<span style="color:gray;">, </span>0<span style="color:gray;">)
</span><span style="color:blue;">DBCC PAGE </span><span style="color:gray;">(</span>2<span style="color:gray;">, </span>5<span style="color:gray;">, </span>440966	<span style="color:gray;">, </span>0<span style="color:gray;">)

</span><span style="color:blue;">DBCC </span>TRACEOFF <span style="color:gray;">(</span>3604<span style="color:gray;">);
</span></pre>
<p>The results all looked about the same to me.</p>
<p><img loading="lazy" data-attachment-id="39178" data-permalink="https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/dbcc_page_results/#main" data-orig-file="https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg" data-orig-size="755,488" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="DBCC_Page_Results" data-image-description="" data-image-caption="" data-large-file="https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg?w=652" class="alignnone size-full wp-image-39178" src="https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg" alt="DBCC_Page_Results" width="755" height="488" srcset="https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg 755w, https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg?w=150&amp;h=97 150w, https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg?w=300&amp;h=194 300w" sizes="(max-width: 755px) 100vw, 755px" /></p>
<p>There are several important parts to me.  The m_objId is a negative value I can&#8217;t find in TempDB.sys.objects, so it WAS a temporary object that no longer exists.  Across the board, these are &#8220;NOT ALLOCATED&#8221;, &#8220;NOT CHANGED&#8221;, &#8220;NOT MIN_LOGGED&#8221;, &#8220;0_PCT_FULL&#8221;, so there&#8217;s nothing there.</p>
<p>To me it looks like temp objects made it into memory and remained in memory after the temporary objects were dropped.  I have no idea what objects these were or how they were dropped, but I&#8217;m imagining these were temp tables automatically dropped when the session was either closed or reset.</p>
<h2>A Recent Change (A CLUE)</h2>
<p>I found this by noticing that PLE for several servers was lower now than it has been in the past, so I was peeking in the buffer pool to see who was playing nice.  Going off of &#8220;when did PLE start to be lower&#8221; I noticed that I implemented a change around that time to use a common best practice.</p>
<p>That change was presizing TempDB data files to take up a vast majority of the dedicated LUN instead of letting them grow as needed.  It avoids waiting for file growth, especially if you&#8217;re using <a href="http://www.brentozar.com/archive/2014/08/sql-server-transparent-data-encryption-tde-common-questions/" target="_blank">TDE</a> (I&#8217;m not) and can&#8217;t use <a href="http://blogs.msdn.com/b/sql_pfe_blog/archive/2009/12/23/how-and-why-to-enable-instant-file-initialization.aspx" target="_blank">IFI</a> (I can), but for several other reasons as well, including file fragmentation and the slight pause even IFI causes.  So at the start of all these festivities, I took the 4 TempDB data files from 100 MB each to 12 GB each, using up 48 GB of the 50 GB available.</p>
<h2>A Workaround</h2>
<p>Seeing this, I wanted to partially roll back the change the next opportunity I had.  100 MB was too small and I was aware that it invoked file growths every month (we reboot monthly for OS updates).  48 GB wasn&#8217;t right though, we just have that much space on the drive due to server build standards and paranoia (I&#8217;m a DBA).  So I went through our Idera Diagnostic Manager monitoring software and found the most space TempDB used, which is captured once an hour.  I found that 4.8 GB was the peak usage with several incidents of usage going over 4.5 GB.</p>
<p>With that information available and still not wanting an autogrowth for all the reasons listed above, I decided that all 4 files should be 1.5 GB, so 6 GB total.  That means peak usage was about 75% full, leaving plenty of room for error, especially with my baseline only being captured once an hour.  Autogrowth is set to 256 MB, so it&#8217;d add 1 GB total each growth.  I can live with that.</p>
<p>I can&#8217;t say it eliminated the issue because I still have 2 GB of unallocated TempDB space in cache, but it&#8217;s better than 8 GB.  It can be considered more acceptable than other issues I need to tackle right now, but it still bugs me.</p>
<h2>What&#8217;s the Best Practice?</h2>
<p>It&#8217;s a best practice to have TempDB data files on their own LUN, drive, array, however you want to word it.  Then it just make sense to have the total size of your data files add up to 90% or more of the drive size.  I see this advice everywhere, with these two standing out:</p>
<ul>
<li>Solar Winds &#8211; <a href="http://logicalread.solarwinds.com/sql-server-tempdb-best-practices-initial-sizing-w01/#.VmnlHE3rvcs" target="_blank"><span style="color:#0066cc;">Configuration Best Practices for SQL Server Tempdb–Initial Sizing</span></a>
<ul>
<li>&#8220;Next, if you can give tempdb its own disk, then configure it to almost fill the drive. If nothing else will ever be on the drive, then you’re better off setting it to be larger than you’ll ever need. There’s no performance penalty, and you’ll never have to worry about autogrow again.&#8221;</li>
</ul>
</li>
<li>Brent Ozar &#8211; <a href="http://www.brentozar.com/archive/2008/03/sql-server-2005-setup-checklist-part-1-before-the-install/" target="_blank"><span style="color:#0066cc;">SQL Server 2005/2008/2012/2014 Setup Checklist</span></a>
<ul>
<li>&#8220;Notice that I don’t have filegrowth enabled.  You want to proactively create the TempDB files at their full sizes to avoid drive fragmentation.&#8221;</li>
</ul>
</li>
</ul>
<p>Jonathan Kehayias does it a little bit differently in his post <a href="https://www.sqlskills.com/blogs/jonathan/sql-server-installation-checklist/"><span style="color:#0066cc;">SQL Server Installation Checklist</span></a> saying to add space to TempDB files in 4 GB increments.  Although he doesn&#8217;t fill the drive by default, this isn&#8217;t mentioned by him, either.</p>
<p>Now I need to be perfectly clear on this, <strong>I trust these three sources.</strong>  I trust Jonathan and Brent more than I trust myself with setting up SQL Server.  I also feel the same about the authors I know on the Solar Winds post.  <strong>This does not change that.</strong></p>
<p>Sizing TempDB like that often means it&#8217;s much larger than you need.  The workaround I&#8217;m using is to right-size these files instead.  For me, for now, I&#8217;m going to stick with seeing how large TempDB gets and make it slightly larger than that until I have a solid answer to my problem.</p>
<h2>What Was It?</h2>
<p>I still don&#8217;t know.  The workaround managed to knock it off of my priority list enough where I&#8217;m not actively working on it.  However, my drive to understand SQL Server better won&#8217;t leave me alone.</p>
<p>This post is my solution.  I have some very intelligent people reading this who I hope will at least lead me further down the rabbit hole, even if they don&#8217;t have a conclusive answer.  There&#8217;s a good chance I&#8217;ll be asking for help on Twitter with #sqlhelp or opening a connect item on this, for which I have a very well documented description of the issue that I can link to.</p>
<h2>Updates:</h2>
<h4>2016-01-06 &#8211; Caching of Temporary Objects</h4>
<p>Due to a comment, I started looking into the caching of temporary objects to see if this was the root cause.  The comment specifically mentioned Paul White&#8217;s (<a href="http://sqlblog.com/blogs/paul_white/" target="_blank">b</a>|<a href="https://twitter.com/SQL_Kiwi" target="_blank">t</a>) post <a href="http://sqlblog.com/blogs/paul_white/archive/2012/08/17/temporary-object-caching-explained.aspx" target="_blank">Temporary Object Caching Explained</a>, and I also read over Itzik Ben-Gan&#8217;s (<a href="http://blogs.solidq.com/en/author/ibengan/" target="_blank">b</a>|<a href="https://twitter.com/ItzikBenGan" target="_blank">t</a>) post <a href="http://sqlmag.com/sql-server/caching-temporary-objects" target="_blank"><span style="color:#0066cc;">Caching Temporary Objects</span></a>.</p>
<p>Both of these left me with the impression that smaller amounts of data would be left in the cache linked to temporary objects linked to the proc cache.  What I&#8217;m seeing is large amounts of data in the buffer pool that did not drop when I ran DBCC FREEPROCCACHE (on a test server that wasn&#8217;t in active use) as I expected if this was the full explanation.</p>
<p>While it&#8217;s very likely this is related to the issue on hand, I&#8217;m not ready to accept it as a full explanation.  If the memory associated with TempDB dropped when clearing the proc cache (on a test server) then it would have been a great explanation with a poor side effect of going too far with the memory being used.</p>
<h4>2016-01-07 &#8211; Opened a Connect Item</h4>
<p>I mentioned this issue on the comments of Paul White&#8217;s blog post mentioned in the last update and comments below on this post.  His response concluded with this:</p>
<blockquote><p>So, when memory pressure is detected, I would expect memory use like this to be freed up for reuse in pretty short order, by design. If it is not, and bad things happen because memory for unallocated tempdb is not released/reused, that would be a bug.</p></blockquote>
<p>While I was already leaning that way, it pushed me over the edge to decided it was time to open up a connect item on this issue.  I feel it&#8217;s well worth the read going to Paul&#8217;s post and the connect item.  Also, if you&#8217;re seeing this as well, an upvote on connect is very appreciated.</p>
<p><a href="https://connect.microsoft.com/SQLServer/feedback/details/2215297">https://connect.microsoft.com/SQLServer/feedback/details/2215297</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/feed/</wfw:commentRss>
			<slash:comments>22</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">39079</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2015/12/querybufferpool_tempdb.jpg">
			<media:title type="html">QueryBufferPool_TempDB</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_bufferpool_unallocated.jpg">
			<media:title type="html">TempDB_BufferPool_Unallocated</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2015/12/tempdb_pagelevel_unallocated.jpg">
			<media:title type="html">TempDB_PageLevel_Unallocated</media:title>
		</media:content>

		<media:content medium="image" url="https://simplesqlserver.com/wp-content/uploads/2015/12/dbcc_page_results.jpg">
			<media:title type="html">DBCC_Page_Results</media:title>
		</media:content>
	</item>
		<item>
		<title>Query the Buffer Pool</title>
		<link>https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/</link>
					<comments>https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 04 Jan 2016 16:00:08 +0000</pubDate>
				<category><![CDATA[General Chat]]></category>
		<category><![CDATA[Indexes]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[System State]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[Tuning]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=39005</guid>

					<description><![CDATA[SQL Server's buffer pool is a majority of your memory, but most DBAs can't tell you what's in it.]]></description>
										<content:encoded><![CDATA[<p>DBAs are known for asking for more memory, but often can&#8217;t say what&#8217;s in memory.  While I agree that many database servers can use more memory, I&#8217;m a firm believer in knowing how you&#8217;re using your resources before asking for more.  The script below allows me to do just that.</p>
<h2>What It Returns</h2>
<p>This will return every index that is using at least 1 MB of memory for every database on your server.  It also returns space in memory that is associated with unallocated space in the tables which shows up as NULL for everything except the size of the space and the table name.</p>
<p>I&#8217;ll warn you now that the unallocated space can be surprisingly high for TempDB, and I talk about that in <a href="https://simplesqlserver.com/2016/01/05/tempdb-memory-leak/" target="_blank">TempDB Memory Leak?</a>.  Hopefully we can get a good comment thread going on that post to talk through what we&#8217;re seeing and how common the issue really is.</p>
<h2>The Script</h2>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'TempDB..#BufferSummary'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#BufferSummary
<span style="color:blue;">END

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'TempDB..#BufferPool'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#BufferPool
<span style="color:blue;">END

CREATE TABLE </span>#BufferPool
<span style="color:gray;">(
	</span>Cached_MB <span style="color:blue;">Int
	</span><span style="color:gray;">, </span>Database_Name <span style="color:blue;">SysName
	</span><span style="color:gray;">, </span><span style="color:magenta;">Schema_Name </span><span style="color:blue;">SysName </span><span style="color:gray;">NULL
	, </span><span style="color:magenta;">Object_Name </span><span style="color:blue;">SysName </span><span style="color:gray;">NULL
	, </span>Index_ID <span style="color:blue;">Int </span><span style="color:gray;">NULL
	, </span>Index_Name <span style="color:blue;">SysName </span><span style="color:gray;">NULL
	, </span>Used_MB <span style="color:blue;">Int </span><span style="color:gray;">NULL
	, </span>Used_InRow_MB <span style="color:blue;">Int </span><span style="color:gray;">NULL
	, R</span>ow_Count <span style="color:blue;">BigInt </span><span style="color:gray;">NULL
)

</span><span style="color:blue;">SELECT </span>Pages <span style="color:gray;">= </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">)
	, </span>allocation_unit_id
	<span style="color:gray;">, </span>database_id
<span style="color:blue;">INTO </span>#BufferSummary
<span style="color:blue;">FROM </span><span style="color:green;">sys</span><span style="color:gray;">.</span><span style="color:green;">dm_os_buffer_descriptors 
</span><span style="color:blue;">GROUP BY </span>allocation_unit_id<span style="color:gray;">, </span>database_id 
	
<span style="color:blue;">DECLARE </span>@DateAdded <span style="color:blue;">SmallDateTime  
SELECT </span>@DateAdded <span style="color:gray;">= </span><span style="color:magenta;">GETDATE</span><span style="color:gray;">()  
  
</span><span style="color:blue;">DECLARE </span>@SQL <span style="color:blue;">NVarChar</span><span style="color:gray;">(</span>4000<span style="color:gray;">)  
</span><span style="color:blue;">SELECT </span>@SQL <span style="color:gray;">= </span><span style="color:red;">' USE [?]  
INSERT INTO #BufferPool (
	Cached_MB 
	, Database_Name 
	, Schema_Name 
	, Object_Name 
	, Index_ID 
	, Index_Name 
	, Used_MB 
	, Used_InRow_MB 
	, Row_Count 
	)  
SELECT sum(bd.Pages)/128 
	, DB_Name(bd.database_id)
	, Schema_Name(o.schema_id)
	, o.name
	, p.index_id 
	, ix.Name
	, i.Used_MB
	, i.Used_InRow_MB
	, i.Row_Count     
FROM #BufferSummary AS bd 
	LEFT JOIN sys.allocation_units au ON bd.allocation_unit_id = au.allocation_unit_id
	LEFT JOIN sys.partitions p ON (au.container_id = p.hobt_id AND au.type in (1,3)) OR (au.container_id = p.partition_id and au.type = 2)
	LEFT JOIN (
		SELECT PS.object_id
			, PS.index_id 
			, Used_MB = SUM(PS.used_page_count) / 128 
			, Used_InRow_MB = SUM(PS.in_row_used_page_count) / 128
			, Used_LOB_MB = SUM(PS.lob_used_page_count) / 128
			, Reserved_MB = SUM(PS.reserved_page_count) / 128
			, Row_Count = SUM(row_count)
		FROM sys.dm_db_partition_stats PS
		GROUP BY PS.object_id
			, PS.index_id
	) i ON p.object_id = i.object_id AND p.index_id = i.index_id
	LEFT JOIN sys.indexes ix ON i.object_id = ix.object_id AND i.index_id = ix.index_id
	LEFT JOIN sys.objects o ON p.object_id = o.object_id
WHERE database_id = db_id()  
GROUP BY bd.database_id   
	, o.schema_id
	, o.name
	, p.index_id
	, ix.Name
	, i.Used_MB
	, i.Used_InRow_MB
	, i.Row_Count     
HAVING SUM(bd.pages) &gt; 128  
ORDER BY 1 DESC;'  

</span><span style="color:blue;">EXEC </span><span style="color:maroon;">sp_MSforeachdb </span>@SQL

<span style="color:blue;">SELECT </span>Cached_MB 
	<span style="color:gray;">, </span>Pct_of_Cache <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>Cached_MB <span style="color:gray;">* </span>100.0 <span style="color:gray;">/ </span><span style="color:magenta;">SUM</span><span style="color:gray;">(</span>Cached_MB<span style="color:gray;">) </span><span style="color:blue;">OVER </span><span style="color:gray;">() </span><span style="color:blue;">as Dec</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>3<span style="color:gray;">))
	, </span>Pct_Index_in_Cache <span style="color:gray;">= </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>Cached_MB <span style="color:gray;">* </span>100.0 <span style="color:gray;">/ </span><span style="color:blue;">CASE </span>Used_MB <span style="color:blue;">WHEN </span>0 <span style="color:blue;">THEN </span>0.001 <span style="color:blue;">ELSE </span>Used_MB <span style="color:blue;">END as DEC</span><span style="color:gray;">(</span>20<span style="color:gray;">,</span>3<span style="color:gray;">))
	, </span>Database_Name 
	<span style="color:gray;">, </span><span style="color:magenta;">Schema_Name 
	</span><span style="color:gray;">, </span><span style="color:magenta;">Object_Name 
	</span><span style="color:gray;">, </span>Index_ID 
	<span style="color:gray;">, </span>Index_Name 
	<span style="color:gray;">, </span>Used_MB 
	<span style="color:gray;">, </span>Used_InRow_MB 
	<span style="color:gray;">, </span>Row_Count 
<span style="color:blue;">FROM </span>#BufferPool 
<span style="color:blue;">ORDER BY </span>Cached_MB <span style="color:blue;">DESC
</span></pre>
<h2>Where&#8217;d the Script Come From</h2>
<p>I&#8217;ve had a script similar to this one around for a while.  It&#8217;s originally based off of Jonathan Kehayias&#8217;s script on his post <a href="http://www.sqlskills.com/blogs/jonathan/finding-what-queries-in-the-plan-cache-use-a-specific-index/" target="_blank">Finding What Queries in the Plan Cache Use a Specific Index</a>, and I couldn&#8217;t have done this without having his script to start with.</p>
<p>Then I originally posted a version of this script on my post <a href="https://simplesqlserver.com/2014/01/08/cleaning-up-the-buffer-pool/" target="_blank">Cleaning Up the Buffer Pool to Increase PLE</a>, which was great to see the index usage for a single database.  It runs slower than this, only returns a single database, and does not show unallocated space in memory.  Those changes warranted either an update to that post or a completely new post&#8230;I opted for the latter.</p>
<h2>What It Means</h2>
<p>Now you can see what&#8217;s in your memory. Hopefully you&#8217;ll see one or two things that stand out on here that don&#8217;t make sense; those are your easy tuning opportunities.</p>
<p>If an index is 100% in cache then you&#8217;re scanning on it, and that may be an issue.  Yes, you can find when you did scans on indexes using the scripts in my <a href="https://simplesqlserver.com/2013/05/16/indexes-unused-and-duplicates/" target="_blank">Indexes &#8211; Unused and Duplicates</a> post, but it helps to have another view of what that means in your memory.</p>
<p>One thing the index monitoring scripts in the post I just mentioned can&#8217;t do is tell you when you&#8217;re doing large seeks as opposed to small seeks.  With the typical phone book example, you could ask for all the names in the phone book where the last names begins with anything from A to Y, giving you 98% of the phone book as results.  Index usage stats will show you did a seek, which sounds efficient.  The script on this post will show that you have 98% of your index in cache immediately after running the query, and that gives you the opportunity to find the issue.</p>
<p>When you see an index that looks out of place here, dive back into the scripts on <a href="https://simplesqlserver.com/2014/01/08/cleaning-up-the-buffer-pool/" target="_blank"><span style="color:#0066cc;">Cleaning Up the Buffer Pool to Increase PLE</span></a> to see what&#8217;s in cache using that index.  If the query isn&#8217;t in cache for any reason, you may be able to look at the last time the index had a scan or seek against it in sys.dm_db_index_usage_stats and compare that to results from an <a href="https://simplesqlserver.com/2015/10/26/extended-events-intro/" target="_blank">Extended Events</a> session you had running to see what it could have been.</p>
<p>The main point is that you have something to get you started.  You have specific indexes that are in memory, and you can hunt down when and why those indexes are being used that way.  It&#8217;s not always going to be easy, but you have a start.</p>
<h2>We&#8217;re All On a Budget</h2>
<p>It&#8217;s not too uncommon for this process to end in asking for more memory, and I view memory like being on a budget.  The amount of memory you have right now is your current budget.  Asking for more memory should be viewed like asking for more money in a financial budget.  For a financial budget increase, here are the questions I&#8217;d be prepared to answer:</p>
<ol>
<li>What did you spend the money we already gave you on?</li>
<li>Did you spend that money as efficiently as possible?</li>
<li>What else do you want to spend money on?</li>
</ol>
<p>Now you can answer these questions in database form:</p>
<ol>
<li>Here&#8217;s what I have in cache at multiple times, specifically right after PLE dropped.</li>
<li>I went through the queries that pulled the data into cache and tuned what I could.</li>
<li>When I checked what&#8217;s in cache multiple times, these indexes fluctuated a lot in how much was in there.  I believe adding more memory would allow them to stay in cache instead of kicking each other out to make room.</li>
</ol>
<p>Be it Virtual or Physical environments, there&#8217;s only so much memory that can be made available to us.  We&#8217;re on budgets of how much memory the host has, how many memory slots a server has, and how large the memory chips are that those slots can handle.  Prove you&#8217;re doing it right and it&#8217;s a lot harder to say no to you.</p>
<p>I have an odd habit of getting the memory I ask for because I answer these questions up front in the initial request for memory.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2016/01/04/query-the-buffer-pool/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">39005</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Thank You for More Than You Realize</title>
		<link>https://simplesqlserver.com/2015/12/28/thank-you/</link>
					<comments>https://simplesqlserver.com/2015/12/28/thank-you/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Mon, 28 Dec 2015 13:00:33 +0000</pubDate>
				<category><![CDATA[General Chat]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40001</guid>

					<description><![CDATA[Blogging and presenting have changed me for the better, and in ways I didn&#8217;t see coming.  However, I know I wouldn&#8217;t have had them if it wasn&#8217;t for three things.  Getting it all started, having help getting established, and having an audience for my work were supported by several groups of people, with a couple people really standing [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Blogging and presenting have changed me for the better, and in ways I didn&#8217;t see coming.  However, I know I wouldn&#8217;t have had them if it wasn&#8217;t for three things.  Getting it all started, having help getting established, and having an audience for my work were supported by several groups of people, with a couple people really standing out.</p>
<h2><strong>Getting it started</strong></h2>
<p>This all started because Tom Zimmerman noticed I had an answer for everything, even if I didn&#8217;t know it right away.  I learned which bloggers and forums to trust, and would look up any issue quickly and easily.  However, he pointed out that I was a &#8220;leech&#8221;, always taking and never contributing.</p>
<p>Tom was right, so I eventually started what I thought was an altruistic path to find ways to contribute.  I settled on blogging even though I thought it would be complex to set it up and maintain.  Although it turned out it was as easy as writing an email, I wouldn&#8217;t have gotten past the hurdle of starting if it wasn&#8217;t for Tom.</p>
<p>Thank you, Tom.</p>
<h2><strong>Establishing the blog</strong></h2>
<p>So I created my blog, decided that wording things towards more junior level DBAs was the best thing for me and an area that could use more content, and was on my way.  However, I didn&#8217;t know how to promote myself, get people to read what I wrote, or really much of anything.  If only a couple people were reading it then I would have given up, quickly.</p>
<p>To get established, as I blogged in the past, I was very fortunate to come across Brent Ozar&#8217;s blogs on bogging and presenting.  To me it&#8217;s even more significant than his work blogging SQL Server.  Some of the stuff he talked about:</p>
<ul>
<li>Having your blog reach established audiences.</li>
<li>Improve search engine results with SEO.</li>
<li>Writing consistently to establish your own audience.</li>
<li>Picking an audience and talking to a person (2010 version of me).</li>
</ul>
<p>People were reading my syndicated work from the start, and some of my posts were creeping their way up in the search engines to get readers there, too.  My words were being heard, and I needed that to keep going.</p>
<p>That part worked but he was also talking about presenting.  How to do it, how to get better at it, where to do it, and how it changes you.  Well, if one part worked for me, lets try the next part.  That, too, worked out rather well.</p>
<p>Thank you, Brent.</p>
<h2>Having an audience</h2>
<p>So I mentioned syndicating my blog and doing presentations, but those both required an established audience where unknown people were accepted and encouraged to participate.  For that I found a couple places.</p>
<p>SQL Server Central and Toad World (formerly SQLServerpedia.com) were the two places to syndicate that I found very early on, and to this day the only two I use.</p>
<p>SQL Server Central is the more important of the two for me for several reasons.  I used and abused this site in the past to the point I was called a leech, so it was an honor to be there.  Then they do daily and weekly emails highlighting blogs and articles with both of them selecting a couple blog posts syndicated to SQL Server Central.  To this day it makes my day seeing my work on there.  The vote of confidence and boost in readers is a real boost to my morale and dedication to write more, better content.</p>
<p>You can&#8217;t discount Toad World which is slow to get reads, but gets many of them over time.  It&#8217;s a lot like the SEO work for search engines where you need to do good work and be patient.  I didn&#8217;t start with patience (still not doing too well there, either), but I&#8217;m very glad I have this going as well.</p>
<p>Then there were presentations, which I jumped right into doing at SQL Saturdays without ever presenting to a user group first.  I didn&#8217;t do too well the first time or two, but I&#8217;m also an introvert to the point that my oldest brother told me he would have bet money against me publically speaking.  However, the audience was there, the organizers encouraged new people to participate, and I was able to refine myself to the point of standing toe-to-toe with names I&#8217;ve known for years.  Better yet, now they&#8217;re not names to me, they&#8217;re people I&#8217;ve met and continue to know better.</p>
<p>Yeah, you know that these things started somewhere by a couple people, but I never put much thought into that.  Not until I read a post thanking Steve Jones.  SQL Server Central and SQL Saturday, the two places I thank the most for hooking me up with an audience, were both started by the same person.  Sure, he had help, such as Andy Warren and Brian Knight for SQL Saturday, but he was key in starting two organizations that are still key to me.</p>
<p>Thank you, Steve.</p>
<h2>Skipping people</h2>
<p>Oh the people I would name if I thought I could name them all.  Coworkers, mentors, SQL Saturday volunteers, bloggers, presenters, readers, attendees, and more should all be mentioned.  Even if I was overconfident enough to think I could name them all, the readers would move along like people in a theater during the credits.  To keep it short and simple&#8230;.</p>
<p>Thank you.</p>
<h2>Where am I now?</h2>
<p>I am SOOO glad that I had all of this established.  This past year has been the worst.  I wish that last sentence needed a filter or something saying it was the worst for certain areas, but it was pretty all-inclusive.  Work, home, life in general beat me down to nothing and I needed to be picked back up.  I didn&#8217;t blog for 17 months, skipped submitting to some SQL Saturdays I love, and in general completely fell apart.</p>
<p>When I started to get it back together, I had some things established that helped more professionally than I could have ever hoped for.  My blog was still pulling in views, especially with being in the top 5 search results for a common search term on Google.  I didn&#8217;t really have an established audience for my blog because I was inconsistent before I took 17 months off, but it was syndicated to an audience happy to see me return.  SQL Saturdays were coming up waiting for an abstract.</p>
<p>My confidence was just waiting for me to say that I&#8217;m back.  So, confidence, I&#8217;m back.</p>
<p>Now I&#8217;m ending the year with a blog that has 125,000 lifetime views not counting syndication that takes it closer to a total of 200,000.  I&#8217;m also pulling together a presentation for SQL Saturday Cleveland that I&#8217;ll blog about in January, and I&#8217;ll pin their speaker evaluation forms on my cube wall, too.</p>
<p>I have goals of blogging better to make a bigger difference for the readers, blogging more consistently to earn followers, and do more with presentations to reach more people with better content.  The details I have for these goals that I didn&#8217;t list here aren&#8217;t easy, but it&#8217;s not easy to describe what these accomplishments do to a person, either.</p>
<p>I understand what it really means to me, especially when I&#8217;m down.  Hopefully, I&#8217;ll never need it like that again, but I&#8217;m so glad all of this was there when I did.</p>
<p>Thank you.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2015/12/28/thank-you/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40001</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Advent of Code – Day 4</title>
		<link>https://simplesqlserver.com/2015/12/24/advent-of-code-day-4/</link>
					<comments>https://simplesqlserver.com/2015/12/24/advent-of-code-day-4/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Thu, 24 Dec 2015 16:05:02 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40265</guid>

					<description><![CDATA[Day 4 of the Advent of Code has us mining for data in MD5 hashes, which is out of the comfort zone for a SQL DBA to say the least. Some would say it&#8217;s a poor use for SQL Server, and I&#8217;d have to agree.  However, sometimes things just need to get done, so I&#8217;m [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="http://adventofcode.com/day/4" target="_blank">Day 4</a> of the <a href="http://adventofcode.com/" target="_blank">Advent of Code</a> has us mining for data in MD5 hashes, which is out of the comfort zone for a SQL DBA to say the least.</p>
<p>Some would say it&#8217;s a poor use for SQL Server, and I&#8217;d have to agree.  However, sometimes things just need to get done, so I&#8217;m playing along with a single tool approach.</p>
<p>Now SQL Server isn&#8217;t supposed to do well with row-by-row processing, right?  Andy Warren was posing the same question <a href="http://sqlandy.com/2015/12/thoughts-on-advent-of-code/" target="_blank">in his post</a> pondering possible solutions to this exact problem. Well&#8230;the problem is that row-by-row processing is typically referencing a table every row.  Earlier today I mentioned that I didn&#8217;t have a good example of set based being slower, but now I found a way.  Sure, some could say that it&#8217;s poor examples on poor ways to use SQL Server, but come on&#8230;we can all find production code that meets that criteria.</p>
<p>So, typical DBA fashion, I made it set-based evaluating values in batches of up to 50,000.  It worked.</p>
<p>Here&#8217;s my code for Part 2.  It&#8217;s so close to Part 1 that you&#8217;ll have trouble telling the difference.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'TempDB..#Numbers'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#Numbers 
<span style="color:blue;">END

CREATE TABLE </span>#Numbers
<span style="color:gray;">(
	</span>ID <span style="color:blue;">INT IDENTITY</span><span style="color:gray;">(</span>1<span style="color:gray;">,</span>1<span style="color:gray;">) </span><span style="color:blue;">PRIMARY KEY
	</span><span style="color:gray;">, </span>b <span style="color:blue;">BIT
</span><span style="color:gray;">)

</span><span style="color:blue;">DECLARE </span>@StartString <span style="color:blue;">varchar</span><span style="color:gray;">(</span>100<span style="color:gray;">) = </span><span style="color:red;">'iwrupvqb'
</span><span style="color:blue;">DECLARE </span>@RowCount <span style="color:blue;">Int 

INSERT INTO </span>#Numbers <span style="color:gray;">(</span>b<span style="color:gray;">)
</span><span style="color:blue;">SELECT </span>0

<span style="color:blue;">SELECT </span>@RowCount <span style="color:gray;">= </span><span style="color:magenta;">@@RowCount 

</span><span style="color:blue;">WHILE </span>0 <span style="color:gray;">= (</span><span style="color:blue;">SELECT </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">) 
			</span><span style="color:blue;">FROM </span><span style="color:gray;">(</span><span style="color:blue;">SELECT TOP </span><span style="color:gray;">(</span>@RowCount<span style="color:gray;">) </span>ID 
					<span style="color:blue;">FROM </span>#Numbers 
					<span style="color:blue;">ORDER BY </span>ID <span style="color:blue;">DESC
				</span><span style="color:gray;">) </span>X 
			<span style="color:blue;">WHERE </span><span style="color:magenta;">CONVERT</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>10<span style="color:gray;">), </span><span style="color:magenta;">HASHBYTES</span><span style="color:gray;">(</span><span style="color:red;">'MD5'</span><span style="color:gray;">, </span>@StartString <span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>ID <span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">))), </span>2<span style="color:gray;">) LIKE </span><span style="color:red;">'000000%'</span><span style="color:gray;">) 
	</span><span style="color:blue;">BEGIN

	INSERT INTO </span>#Numbers <span style="color:gray;">(</span>b<span style="color:gray;">)  
	</span><span style="color:blue;">SELECT TOP </span><span style="color:gray;">(</span>50000<span style="color:gray;">) </span>b
	<span style="color:blue;">FROM </span>#Numbers
	
	<span style="color:blue;">SELECT </span>@RowCount <span style="color:gray;">= </span><span style="color:magenta;">@@RowCount 
</span><span style="color:blue;">END

SELECT </span>ID <span style="color:gray;">= </span><span style="color:magenta;">MIN</span><span style="color:gray;">(</span>ID<span style="color:gray;">)
	, </span>MD5_Hash <span style="color:gray;">= </span><span style="color:magenta;">CONVERT</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">), </span><span style="color:magenta;">HASHBYTES</span><span style="color:gray;">(</span><span style="color:red;">'MD5'</span><span style="color:gray;">, </span>@StartString <span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span><span style="color:magenta;">min</span><span style="color:gray;">(</span>ID<span style="color:gray;">) </span><span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">))), </span>2<span style="color:gray;">) 
</span><span style="color:blue;">FROM </span><span style="color:gray;">(</span><span style="color:blue;">SELECT TOP </span><span style="color:gray;">(</span>@RowCount<span style="color:gray;">) </span>ID 
		<span style="color:blue;">FROM </span>#Numbers 
		<span style="color:blue;">ORDER BY </span>ID <span style="color:blue;">DESC
	</span><span style="color:gray;">) </span>X 
<span style="color:blue;">WHERE </span><span style="color:magenta;">CONVERT</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>10<span style="color:gray;">), </span><span style="color:magenta;">HASHBYTES</span><span style="color:gray;">(</span><span style="color:red;">'MD5'</span><span style="color:gray;">, </span>@StartString <span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>ID <span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">))), </span>2<span style="color:gray;">) LIKE </span><span style="color:red;">'000000%'
</span></pre>
<p>Then I realized I didn&#8217;t even need a table and just ran the code. As you can see, the code was much easier to write, and it also worked. More importantly, it worked in 36 seconds as opposed to 113 seconds in the set-based approach for part 2. Part 1, in case you&#8217;re wondering was 1 second compared to 3 seconds, so the non-set based was still about 3 times faster.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">DECLARE </span>@i <span style="color:blue;">Int </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@StartString <span style="color:blue;">varchar</span><span style="color:gray;">(</span>100<span style="color:gray;">) = </span><span style="color:red;">'iwrupvqb'

</span><span style="color:blue;">WHILE </span><span style="color:magenta;">CONVERT</span><span style="color:gray;">(</span><span style="color:blue;">VarChar</span><span style="color:gray;">(</span>10<span style="color:gray;">), </span><span style="color:magenta;">HASHBYTES</span><span style="color:gray;">(</span><span style="color:red;">'MD5'</span><span style="color:gray;">, </span>@StartString <span style="color:gray;">+ </span><span style="color:magenta;">CAST</span><span style="color:gray;">(</span>@i <span style="color:blue;">as VarChar</span><span style="color:gray;">(</span>1000<span style="color:gray;">))), </span>2<span style="color:gray;">) NOT LIKE </span><span style="color:red;">'000000%' </span><span style="color:blue;">BEGIN
	SET </span>@i <span style="color:gray;">= </span>@i <span style="color:gray;">+ </span>1
<span style="color:blue;">END

SELECT </span>@i 
</pre>
<p>I&#8217;m saving the link to my own post to give to others as an example of when set-based is worse. Hint: it&#8217;s ONLY because I wasn&#8217;t referencing a table in the loop, and I could probably still find a way to make it more efficient in a set.  It&#8217;s still true that row-by-row would be slower, I&#8217;m cheating by not using rows.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2015/12/24/advent-of-code-day-4/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40265</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
		<item>
		<title>Advent of Code – Day 3</title>
		<link>https://simplesqlserver.com/2015/12/24/advent-of-code-day-3/</link>
					<comments>https://simplesqlserver.com/2015/12/24/advent-of-code-day-3/#comments</comments>
		
		<dc:creator><![CDATA[Steve Hood]]></dc:creator>
		<pubDate>Thu, 24 Dec 2015 14:53:18 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">http://simplesqlserver.com/?p=40241</guid>

					<description><![CDATA[Continuing with the language-independent code challenge on Advent of Code &#8211; Day 3, I&#8217;m tasked with figuring out how well a drunken elf can guide Santa to houses.  Since his instructions had Santa stumbling into the same house multiple times they then added in a robot to help Santa using the same instruction set, which [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Continuing with the language-independent code challenge on <a href="http://adventofcode.com/day/3" target="_blank">Advent of Code &#8211; Day 3</a>, I&#8217;m tasked with figuring out how well a drunken elf can guide Santa to houses.  Since his instructions had Santa stumbling into the same house multiple times they then added in a robot to help Santa using the same instruction set, which went on to prove automation can be harmful if not done properly.</p>
<p>Wow, I bet you never thought you&#8217;d read that paragraph&#8230;</p>
<p>The elf gave Santa instructions on how to get around a grid of houses using ^, v, &lt;, and &gt; to point the way.  When that failed, they tried again with a robot assistant using the same instructions with Santa taking the odd numbered instructions and his automated assistant taking the even numbered instructions.</p>
<p>My code did not have to change much to partially automate Santa&#8217;s job, which is actually kinda disturbing.  However, since it didn&#8217;t work out so well I went back and added a parameter for them to easily roll back the change.  Now it&#8217;s easier to go back to happy and jolly instead of cold and&#8230;terminator.</p>
<pre style="font-family:consolas;font-size:12px;"><span style="color:blue;">SET NOCOUNT ON

IF </span><span style="color:magenta;">OBJECT_ID</span><span style="color:gray;">(</span><span style="color:red;">'TempDB..#visited'</span><span style="color:gray;">) IS NOT NULL </span><span style="color:blue;">BEGIN
	DROP TABLE </span>#visited
<span style="color:blue;">END

DECLARE </span>@input <span style="color:blue;">varchar</span><span style="color:gray;">(</span><span style="color:magenta;">max</span><span style="color:gray;">) = </span><span style="color:red;">'&gt;^^v^&lt;&gt;v&lt;&lt;&lt;v&lt;v^&gt;&gt;v^^^&lt;v&lt;&gt;^^&gt;&lt;^&lt;&lt;^vv&gt;&gt;&gt;^&lt;&lt;^&gt;&gt;&lt;vv&lt;&lt;v^&lt;^^&gt;&lt;&gt;&gt;&gt;&lt;&gt;v&lt;&gt;&lt;&gt;^^&lt;^^^&lt;&gt;&lt;&gt;&gt;vv&gt;vv&gt;v&lt;&lt;^&gt;v&lt;&gt;^&gt;v&lt;v^&lt;&gt;v&gt;&gt;&lt;&gt;^v&lt;&lt;&lt;&lt;v^vv^&gt;&lt;v&gt;v^&gt;&gt;&gt;vv&gt;v^^^&lt;^^&lt;&gt;&gt;v&lt;^^v&lt;&gt;^&lt;vv^^&lt;^&gt;&lt;&lt;&gt;^&gt;&gt;&lt;^&lt;&gt;&gt;&gt;&lt;&gt;&lt;vv&gt;&lt;&gt;v&lt;&lt;&lt;&gt;&lt;&gt;&lt;&gt;v&gt;&lt;&lt;&gt;^^^^v&gt;&gt;^&gt;^&lt;v&lt;&lt;vv^^&lt;v&lt;^&lt;^&gt;^^v^^^^^v&lt;&gt;&lt;^v&gt;&lt;&lt;&gt;&lt;^v^&gt;v&lt;&lt;&gt;^&lt;&gt;^^v^&lt;&gt;v&lt;v^&gt;v&gt;^^&lt;vv^v&gt;&lt;^&lt;&gt;^v&lt;&gt;&lt;^&gt;&lt;v^&gt;&lt;&gt;&lt;&gt;&lt;&lt;&lt;&lt;&gt;^vv^&gt;^vvvvv&gt;&lt;&gt;&lt;^&lt;vv^v^v&gt;v&lt;&lt;^&lt;^^v^&lt;&gt;^&lt;vv&gt;&lt;v&lt;v^v&lt;&lt;v&lt;&lt;^^&gt;&gt;^^^v^&gt;v&lt;&gt;&lt;^vv&lt;&lt;^&lt;&gt;v&lt;v&gt;&lt;&gt;&lt;v^^&gt;&lt;v&lt;&gt;^^&gt;^^&gt;v^&gt;^&lt;&lt;&lt;&lt;v&gt;&lt;v&lt;&lt;&gt;v&gt;&lt;^v&gt;^&gt;&gt;&lt;v^^&lt;^&gt;v&lt;vvvv&lt;&gt;&gt;&gt;&gt;&gt;^v^^&gt;v&lt;v&lt;^&lt;vv&gt;^&gt;^vv^&gt;vv^^v&lt;&lt;^&lt;^^&lt;&gt;v&gt;vv^v&gt;&gt;&lt;&gt;&gt;&gt;v^&gt;^&gt;^^v&lt;&gt;^&lt;v&lt;&lt;&gt;^vv&gt;v^&lt;&lt;v&gt;v&lt;&lt;&gt;&lt;v&gt;^vvv&lt;v&lt;vvv^v&lt;vv&lt;v^^^&gt;v&gt;&lt;&lt;^&lt;&gt;&gt;&lt;v^^&gt;^v^&gt;&gt;^v&lt;^&lt;&gt;&lt;v&lt;&gt;&gt;v^&lt;&gt;&gt;v&lt;&gt;&gt;v^^^&gt;&lt;^&gt;&gt;vvvv&gt;^v&lt;^&gt;&lt;&lt;&gt;&gt;^&lt;&gt;^&gt;vv&gt;&lt;v&lt;&lt;&gt;&gt;^^&gt;v^^^&gt;&lt;^&lt;&lt;^^v&gt;v&lt;^&lt;&lt;&gt;v&gt;^^vvv^v^&gt;v^&lt;&gt;^^&lt;&gt;v^v&gt;v&gt;v&lt;v^&gt;vv&gt;^^v&lt;&gt;v&gt;&gt;^&lt;&gt;&gt;&lt;&gt;v&gt;v^&lt;&lt;vvvv&lt;vvv&gt;&lt;v^&lt;^&gt;^v&lt;&gt;&gt;^&gt;&lt;v&gt;&gt;&lt;&gt;^&lt;v&gt;v&lt;v&gt;vv^&gt;&gt;vvv&lt;&gt;v&gt;v&lt;v^&gt;&gt;^&gt;&gt;v&lt;&lt;&gt;^&lt;&gt;^&lt;&gt;&gt;&gt;^v&lt;&lt;&lt;^&lt;^v&gt;vv^&gt;&gt;&lt;&lt;&gt;&lt;v^&gt;^v^^^v&lt;&gt;^^vv&gt;&lt;&gt;&gt;&lt;&gt;&gt;^&gt;v^&lt;v&lt;&gt;v&lt;&gt;&gt;^&lt;&lt;^v&gt;^^^&lt;&gt;^v^&gt;&lt;&gt;v&lt;&lt;v&gt;vv^&gt;vv&lt;&lt;&gt;&gt;&gt;&lt;&lt;^v^&lt;&gt;v&lt;vv&gt;&gt;&gt;^^&lt;&gt;^&gt;&lt;&lt;^&gt;vv&gt;&gt;^&lt;&lt;v^^vv&lt;&gt;&gt;&gt;&lt;v&gt;v&gt;&lt;^&lt;v&lt;&lt;&gt;&gt;&gt;^^&lt;&gt;&gt;^&lt;^v&gt;&lt;&gt;vv^^^v&gt;vvv&gt;^&gt;&lt;&lt;&gt;^^&gt;^&lt;&lt;v^&lt;v&lt;^v&lt;&lt;&gt;vvv&lt;^&lt;&lt;&gt;^&gt;^v&lt;vv&lt;^&gt;vvv&gt;v&gt;vv^&lt;v^&gt;&lt;&gt;&gt;^vv&lt;^^^vv&gt;&lt;^vv&lt;v^&lt;&gt;&lt;v^vvv&gt;&lt;&lt;^&gt;^^&gt;&lt;v&lt;&lt;vv^&gt;v&lt;vv&lt;v&gt;^&lt;&gt;^v&lt;&lt;&gt;v&lt;v^v^&gt;^&gt;^&gt;v&lt;&lt;^vvv&lt;&lt;&lt;v&gt;^^&gt;^&lt;&lt;&lt;&lt;&gt;vv&gt;&gt;^&lt;&gt;^&gt;&gt;&gt;v&lt;v&gt;^^&lt;v^&lt;v&lt;&gt;&gt;&gt;vv&gt;^^v&lt;&lt;&gt;&gt;&gt;^^v&gt;&lt;&lt;&lt;v&lt;v&lt;^v&lt;&gt;^^&gt;&lt;v&lt;^v&lt;&lt;v^&gt;&lt;&gt;&lt;^&lt;&gt;&lt;v&lt;^^v&gt;&gt;&gt;&lt;v^^v&lt;&lt;v^&gt;&lt;^&lt;&gt;&lt;&lt;v^&gt;&gt;&lt;^&lt;&gt;v&gt;v^&lt;&gt;&lt;^&lt;v&gt;^v^&gt;^&gt;^vv^&gt;^^&lt;&lt;vv^&gt;vv&lt;^vvv&lt;&gt;&gt;^^&lt;^&gt;v^&gt;^&gt;^&lt;v^&gt;&lt;v&lt;v&gt;&gt;&gt;v&lt;&lt;&lt;&gt;&lt;^v&lt;&lt;&gt;&lt;^&lt;vv^v^^^&gt;v&lt;^^&lt;v^vvv&lt;v&lt;&gt;&lt;v&lt;vv&lt;^vv&lt;&gt;vv&lt;v^&lt;&gt;&gt;vvvvv&lt;&lt;&gt;^v^v&gt;vv&gt;&gt;&gt;vvv^^&lt;^&lt;^&lt;&gt;&lt;&gt;v&lt;v&gt;&gt;&lt;^v&gt;&lt;^&lt;&lt;&lt;&gt;&gt;&lt;&lt;&lt;v&gt;^&gt;v&lt;&gt;^&gt;^v&gt;&gt;^&lt;&gt;v^&lt;^&gt;&gt;&lt;&lt;&gt;^&lt;v&gt;^&gt;^^^&gt;^^&lt;v&gt;&gt;&gt;&gt;&lt;&gt;^v^v&gt;&lt;&lt;&lt;&lt;vv^&lt;vv&lt;&gt;vv&gt;v&lt;&gt;v^&lt;v^&gt;v&gt;&lt;&gt;&gt;&gt;v^&lt;&gt;&lt;^vvv&gt;vv^&lt;^&lt;&lt;^&lt;^^v&gt;^&gt;&gt;&gt;v&lt;^&lt;^v^^&lt;^&lt;^&gt;&gt;&gt;&lt;v&gt;vv&gt;^&lt;&lt;&gt;&lt;&gt;^&gt;&gt;v&gt;^&lt;&lt;&gt;&gt;&lt;^&lt;&gt;v&lt;&gt;vv^^&gt;^&gt;vvv^v&lt;&lt;^&lt;^^&lt;vv&lt;&gt;^vvv&lt;^^v^vv^&gt;&gt;v&lt;^&gt;^^&lt;v^&lt;&gt;v&lt;^&lt;^vv&gt;v&lt;&lt;vv&gt;vv&gt;^&gt;vvv&gt;&gt;&gt;^^&gt;v&lt;&gt;^v&gt;v^&lt;^&gt;&gt;v&gt;^^v&gt;&gt;&gt;&gt;v^&lt;v&gt;v&lt;^&gt;v&lt;v&lt;&lt;&gt;&gt;^v&lt;^^&lt;v&gt;&lt;^&lt;&gt;&gt;^&lt;&lt;vv^&gt;&gt;v&lt;&lt;v&gt;^v&lt;&gt;&gt;&lt;^&gt;vv&lt;v&lt;^&gt;&gt;^^&lt;vvvvvvvvv&gt;&gt;&gt;v&lt;v&lt;&gt;v^&lt;&gt;&gt;^vv&lt;v^^v&lt;&lt;^vvv^&lt;&lt;^&gt;&lt;&gt;vv&lt;&gt;&lt;&lt;&gt;&gt;v&gt;vv^&gt;&lt;&gt;&gt;^^v^&gt;&gt;v^v^&gt;&lt;&lt;&lt;&gt;&gt;^^&lt;^v&lt;&lt;^&lt;&gt;&gt;&gt;&gt;^&lt;^&gt;v^&gt;&lt;&lt;^&gt;v&lt;^v&lt;^&gt;&gt;^^&lt;&lt;&lt;&lt;&gt;&lt;^&lt;^v^v&lt;&gt;&gt;^v&lt;^&lt;&lt;vv^&lt;&gt;&lt;^^vv&gt;&lt;v^v^v&gt;^&gt;&gt;^&gt;^vv^&gt;^v&lt;v^v&lt;&lt;vvv^&gt;&lt;&gt;&gt;^v^^&gt;&lt;&gt;v&gt;vv&gt;&lt;^&gt;&gt;vv&lt;vvv&lt;&lt;&lt;&lt;^&lt;&gt;vvv^v&lt;v&gt;^&lt;v&lt;^&gt;^&lt;^&lt;v&lt;&gt;&lt;&gt;v^^^^&lt;&lt;vv&lt;^^vv&lt;v&gt;&gt;&lt;&lt;v^&gt;&lt;&gt;&gt;&gt;&lt;v^&gt;^v&gt;&lt;^&gt;^&gt;&lt;vv^&lt;&gt;&lt;^&lt;v&gt;&gt;&lt;&lt;^vv&lt;&gt;&gt;v^&lt;&lt;v&lt;&gt;v&gt;&lt;v&lt;&gt;&lt;&gt;&lt;vv&gt;^&gt;&gt;v^&lt;^&lt;v&gt;^&gt;&lt;&gt;&gt;&gt;&lt;^&gt;&lt;v^v&lt;&gt;&gt;&gt;^^&lt;^&gt;v&lt;v&gt;vvv&lt;&gt;^&lt;&lt;&gt;&lt;v^^&gt;^&gt;&gt;v&lt;^v&gt;^&gt;v&gt;&gt;&gt;vv&gt;v&gt;&gt;v^^^&lt;^&lt;vvv^&lt;&gt;^&gt;^&lt;v^&lt;v^v&gt;v&gt;^&gt;vv&gt;vvv&lt;&gt;v&lt;^&gt;v&gt;^^&gt;&gt;^&lt;vv^^v&gt;v^^^^^v^vv&gt;&lt;^&lt;&gt;&lt;&gt;^&gt;vv&lt;^&gt;&gt;^vvvv^^^&gt;^&lt;vv&gt;^v&lt;&lt;^&gt;&lt;^^&gt;^&lt;&gt;^^&gt;^&lt;&lt;v&lt;^&gt;&gt;&gt;^&gt;&lt;&lt;^^&gt;v^v&gt;&gt;^&gt;vvvv&gt;^^v&gt;&lt;v&gt;&gt;vv&gt;&lt;&lt;&lt;vv&lt;^&gt;v&gt;^^^&lt;v&gt;v^vvv&lt;^&gt;&lt;&lt;^&gt;^&lt;&gt;^&gt;&lt;&lt;&lt;&lt;&lt;v^&lt;&lt;vv^v&gt;^&lt;&gt;v&lt;v&gt;^&gt;^&gt;&gt;&lt;&gt;v^v&lt;^vv^^&gt;vv&lt;&lt;v^v&gt;vv^vvv&lt;&lt;&lt;&lt;&gt;^v&lt;v^^v^v&gt;v&lt;&lt;v&gt;^^&lt;&gt;^vv^^&gt;^&gt;^v^vv^&gt;&gt;v^vv^^&lt;vv&gt;&lt;&lt;v^v^^v&gt;&lt;vv&lt;^vvv&lt;vv^^&lt;&lt;v&gt;v^v^^^^v&lt;^&lt;^&gt;v&gt;^&gt;v&gt;^vv^v^^&lt;v&lt;^vvvv&lt;&lt;&lt;&gt;^&lt;^^^&lt;^^&lt;&gt;^&lt;&gt;&lt;vv&lt;^^&lt;&lt;^&gt;&gt;&gt;&lt;v^vvvv&gt;^&lt;&gt;&gt;^^&gt;v^^v^&lt;&lt;v^^^&lt;&lt;&lt;&gt;&lt;^&lt;v^v^^v&lt;v^&lt;&gt;v&gt;&lt;&lt;v&lt;&gt;^v&gt;v&lt;^&gt;&lt;^&gt;vv^^&lt;vvv&lt;^v&gt;&gt;v&gt;^&lt;&gt;&lt;v^&gt;&lt;^^^&lt;v^&gt;&gt;vv&lt;&lt;&lt;&lt;&lt;^&lt;&gt;^v^v&gt;^vv^&lt;&gt;v&gt;v&lt;^&gt;vv&lt;&lt;^vv&gt;vv&lt;v&lt;&gt;&lt;&gt;&gt;v&gt;&gt;&lt;v&lt;^&lt;^^&gt;&gt;&lt;&lt;v^v&lt;&lt;^&gt;&lt;v&lt;^&lt;vv&lt;v&lt;&lt;vv^&gt;^&lt;&lt;&gt;&lt;^^&gt;^&lt;^&gt;&gt;^&lt;vv&gt;&gt;&lt;v&lt;&lt;vvv&lt;^^v^&gt;^^&lt;^v&gt;^v&lt;v&lt;&gt;v&gt;&lt;v^v^&lt;&lt;^&lt;&gt;&lt;&lt;v&lt;&lt;^v&gt;v&lt;&lt;&gt;&gt;^&gt;v&gt;&gt;v&gt;&gt;v&lt;^&lt;&lt;^&lt;^&gt;&gt;&gt;v&gt;^^^v&gt;&lt;^&gt;^^&gt;&gt;v&lt;&lt;&gt;^v&gt;&lt;v&gt;vvv^vv&lt;&lt;&lt;&gt;vvv&lt;&lt;&gt;^&gt;&gt;&gt;v&lt;v&lt;v^&lt;^&lt;^&gt;^&lt;^&gt;v^^v&lt;^^&lt;v&lt;&gt;v&lt;&gt;&gt;^^&gt;^v^&gt;v&lt;&lt;&lt;&lt;^&lt;&gt;v^&gt;&lt;&lt;&lt;v&gt;&gt;&gt;&gt;&lt;&lt;v^&lt;^vv&gt;v&gt;&gt;&lt;&gt;&gt;^&lt;&lt;&lt;^&lt;^^&gt;v&lt;&gt;&gt;v&lt;&gt;vv&lt;&lt;^&lt;&lt;&gt;&lt;&lt;^&gt;v^^^vv^&gt;vvvv&gt;&gt;v&gt;v^&gt;&lt;&lt;v&lt;&gt;vv^&lt;&lt;&gt;&lt;&lt;vvv&gt;^&gt;&gt;&gt;^&lt;&lt;&lt;^&lt;^&lt;&lt;v&gt;^&gt;v&lt;&gt;&gt;v&gt;&gt;vv^^&gt;&lt;&lt;&lt;&lt;^^^v&gt;&gt;&lt;&lt;^&gt;&lt;v&gt;&lt;v^^&gt;&lt;v&lt;&lt;v^^v^^v&gt;&gt;v&lt;&gt;&lt;&gt;&lt;&lt;&gt;^&gt;&lt;v&gt;&lt;^&lt;vv&gt;&gt;&lt;^v&lt;&gt;v&lt;vvv&lt;&gt;^&gt;&gt;&lt;v&gt;&gt;v&lt;^&gt;&lt;&lt;v&gt;^&lt;&gt;^v&gt;&lt;^&gt;&lt;^^&lt;v&gt;^&gt;&lt;^^v^&lt;&lt;&gt;&lt;&gt;&gt;^&gt;v^&lt;^v^vv&lt;&gt;&lt;^&gt;vv^&gt;v^vvv^&lt;&gt;&gt;^&gt;&lt;^&lt;^&lt;&gt;^&lt;&lt;v^v&lt;^v&gt;&lt;&gt;^v&lt;v&gt;&gt;^&gt;&gt;^v^vv&gt;&gt;&lt;vv&gt;&lt;v^^&lt;&lt;^v^&lt;&gt;^v&lt;&lt;&gt;^&gt;&lt;^&gt;&gt;&lt;v&gt;&gt;v&lt;&lt;&lt;v^^vv&lt;&gt;^^v&gt;&gt;&gt;&lt;&gt;&lt;&gt;&lt;&lt;v^&lt;&lt;&lt;v^&lt;^^&gt;&lt;v^&gt;v^^vv&lt;v^&lt;&gt;&gt;vv^&lt;^v&lt;&gt;^v&gt;&gt;v^v&gt;v&lt;^^vv&gt;&lt;&gt;^v&lt;&lt;&gt;v^&lt;&gt;v^&gt;&gt;v&gt;vvv&lt;^&gt;&lt;&gt;&lt;^^&gt;^vv^&gt;&gt;v^&gt;^&lt;^^&lt;&gt;&lt;&gt;&gt;&lt;&lt;&gt;^^^&gt;&lt;^v^v&gt;&lt;&lt;&lt;&gt;&lt;&lt;^v^vv&gt;v&gt;&gt;&lt;^&gt;&gt;&gt;&lt;v^&gt;v&lt;v&gt;&lt;&gt;&lt;v^v&lt;&gt;v^^&gt;&gt;v&lt;&lt;&gt;v&gt;v&lt;v&lt;v&lt;^^&lt;&gt;&lt;&gt;v^^&lt;&gt;&gt;v&lt;^v&lt;v&gt;v&lt;&gt;&lt;v&lt;v&gt;^&lt;&lt;&gt;v&gt;vv^^&lt;&gt;&gt;^^^&lt;&gt;^^&gt;^v&gt;v&gt;&gt;&gt;^v^v&gt;&lt;v^^&lt;&gt;&lt;v&gt;^^v^v&lt;^&lt;^^&gt;&lt;&lt;v&lt;^&lt;^&lt;&gt;^&lt;&gt;&gt;&lt;&lt;&gt;^&gt;&gt;^&gt;^^&gt;&lt;v&gt;&lt;&gt;v&lt;&gt;&lt;&gt;&gt;&lt;&lt;&lt;&gt;&gt;&gt;&gt;vv&gt;&gt;&gt;^&gt;&gt;^v&lt;^&gt;v^^^v&lt;&lt;vv&gt;&gt;&lt;&lt;&lt;^&lt;&lt;&lt;&gt;&gt;&gt;&gt;&gt;^&gt;vv&lt;^v^&lt;&gt;^&lt;v^&gt;^v&gt;&lt;v&gt;vvv&lt;&gt;&gt;&gt;^v^^^v&lt;&lt;&lt;&lt;&gt;&gt;^^&lt;vv&lt;^&lt;^^&gt;^&lt;&gt;v&lt;^&lt;&lt;&lt;&gt;&gt;&lt;&gt;&gt;v&lt;^&lt;&gt;^&lt;vvv&lt;^&lt;&gt;&gt;&lt;&gt;&lt;&lt;v&gt;^^^&gt;^^&lt;&lt;v&lt;v^&gt;^^v^&gt;&gt;&lt;&lt;^vv&gt;&lt;v&gt;^v&gt;&gt;^&lt;v&gt;v&gt;^^&gt;^v&gt;^vvv&lt;&gt;v^v^^&lt;&gt;&lt;vv&gt;vv^&gt;&gt;&gt;&lt;&gt;v&lt;^&gt;&lt;v&lt;v^&lt;&gt;&lt;&lt;&lt;&gt;^v&gt;^v&lt;&lt;&lt;^&gt;^&gt;^&gt;v^v&lt;&lt;&gt;&lt;vvv&lt;&lt;v^^&lt;&gt;&lt;v&gt;^&gt;&gt;&gt;&lt;vv&gt;&gt;&lt;v&gt;&gt;v^&lt;vv&gt;&gt;vv&lt;&lt;^v^v&lt;&lt;&gt;&lt;^v&lt;vv&gt;&gt;&gt;vv&lt;&gt;&gt;&gt;&gt;^vv&gt;v^&lt;&gt;vv&gt;v^v&lt;v^&gt;&lt;v&lt;^^^^^&gt;vv&lt;&gt;&lt;&lt;vvv^&lt;v&gt;&lt;^&lt;vv&gt;&lt;^^^vv^&lt;&gt;^^^^&lt;^&gt;&lt;^&lt;&gt;v^&lt;v^v&lt;&lt;^v&lt;&lt;^^&lt;&gt;&gt;^&lt;v^^&gt;&gt;&gt;vv&lt;vvv&lt;&gt;v&lt;&lt;&gt;&gt;&lt;^vvv^&lt;&lt;^^&lt;&lt;&gt;&gt;&gt;^&lt;&gt;&gt;&gt;v^^&gt;&lt;&gt;&gt;&lt;&lt;&gt;&gt;&lt;v^v&gt;&gt;&gt;&gt;&gt;&gt;&lt;&gt;&gt;&gt;&lt;v^&lt;&lt;vvv^&gt;v&lt;&gt;&gt;v^&lt;&gt;vv&lt;&gt;&lt;^^^^v^&lt;&lt;^&lt;v^vv&gt;&lt;&lt;^^&gt;v&lt;^vvv^v&gt;&gt;v&gt;^&gt;&gt;v&gt;^^&gt;&lt;&lt;v^&lt;&gt;v&lt;&gt;vv&lt;^v^vv&gt;&lt;v&gt;&lt;&lt;vv^v&gt;&gt;v^&gt;&gt;v&lt;^^^&gt;^&gt;&lt;&lt;v&lt;&gt;^&gt;&lt;&gt;v&gt;&gt;&gt;vvv&lt;v&lt;vv&lt;^&gt;&gt;^v&lt;v&gt;^&lt;^^^^^v&gt;&lt;&gt;v&gt;&lt;&gt;v^v^v&lt;v^vv^v&gt;vvvv&lt;&gt;vv&lt;&lt;&lt;vv&lt;v&lt;&lt;&gt;^&lt;^&gt;^^v^&lt;&lt;&gt;^&lt;v&gt;&lt;^&gt;&lt;v&lt;v&lt;&gt;&lt;&lt;&gt;v^&lt;&lt;^&lt;&gt;&lt;vv&gt;v&lt;&lt;^v&gt;&gt;^v&lt;&gt;&lt;v&gt;^&gt;&gt;^^&gt;&lt;&gt;v^&lt;^&lt;vvv^&gt;^&gt;^&lt;&lt;&lt;&lt;&gt;vv&gt;^v^v&lt;^^^&lt;vv&gt;&gt;&lt;&gt;^^&lt;&lt;v&lt;^&lt;^^&gt;&gt;&gt;v^v&lt;&lt;^^^&lt;v&lt;v&lt;^&lt;&gt;^v&lt;v&gt;&lt;v^vv^^v^^v^^&lt;vv&lt;&gt;^&lt;&gt;&lt;vv^&lt;^v^&lt;&lt;^&gt;&lt;&lt;vvv&gt;^^&lt;^^^&lt;^v&gt;^&gt;^vv&gt;&lt;&lt;&lt;^v&lt;v&gt;vv&gt;v&lt;&gt;v^v&lt;v^&gt;v^&gt;&gt;&gt;v^v&lt;&gt;^v&lt;&lt;&gt;^vv&gt;v&gt;v&gt;v^&lt;^&gt;v^^&lt;^&gt;^^^^vv&gt;^^&gt;&lt;^&gt;vv^&gt;&gt;^^v&gt;&gt;&lt;&lt;&lt;&lt;^&gt;&lt;&gt;v&lt;&gt;^&lt;v&lt;vv^&gt;^^&gt;&lt;&lt;^&gt;&lt;v&gt;v^&gt;^^&lt;^&gt;&gt;&gt;&lt;&gt;&gt;&lt;v^v&lt;v^&lt;v&lt;vv^v^&lt;&lt;^&lt;vvv&gt;&gt;&gt;&lt;vv&lt;^^&gt;&gt;^&gt;^&gt;&lt;&lt;v^&lt;&gt;&gt;v&gt;v^v^^&gt;&lt;&lt;&gt;vv^v&gt;v^&lt;v&gt;&lt;^&lt;&gt;^^&lt;^&gt;v&gt;^&lt;&gt;&lt;&lt;&lt;v&gt;^&lt;^&lt;^&gt;^&gt;^&gt;^^v^&lt;&lt;^^v^^&lt;^&lt;&gt;&gt;&lt;^&gt;v&gt;&gt;^^&lt;&gt;^^^&lt;&lt;&lt;&lt;v^&gt;^v&lt;^vv&gt;^&lt;&lt;&lt;v&lt;&gt;&lt;&lt;v&lt;&gt;vv&gt;&gt;&gt;v&gt;&lt;&gt;&gt;&gt;&lt;&gt;&gt;v&lt;&lt;&lt;vv&gt;&lt;&gt;^v&gt;v&lt;^&gt;&gt;&lt;^&gt;&lt;&gt;&lt;v&lt;&gt;^v^&gt;^v&gt;^v&lt;&lt;&gt;&lt;&lt;^&lt;&gt;&gt;v&gt;^&gt;&lt;&gt;^&gt;&gt;&lt;&gt;&gt;&lt;^&lt;v^&gt;&lt;v^^&lt;&gt;&lt;v&gt;&lt;^^&gt;^v^^&lt;&gt;v^&lt;v^&lt;^v&lt;v^^^^^v^&lt;&lt;^&gt;^^^&lt;^v&gt;&lt;&gt;^^&lt;&lt;&lt;&gt;&lt;&lt;&lt;&lt;&lt;^^&gt;v^vvvv&gt;v&lt;&gt;&gt;vv&lt;^&gt;^v^&gt;v&lt;^vv^v&lt;&lt;&gt;&lt;&lt;v&gt;v^v&gt;^^&gt;&lt;&gt;&lt;^&lt;v^&gt;v&gt;&lt;vv&gt;&lt;&gt;&gt;&gt;&lt;&lt;&gt;^vv&lt;&gt;v&gt;&gt;v&lt;^v&gt;&gt;&gt;v&lt;v&gt;v&gt;v&gt;^vv&lt;&lt;&gt;^^vv&lt;v&lt;^v^&lt;v&lt;v&gt;vv&lt;&gt;^&lt;^&lt;vv&lt;v^&lt;^v^^&gt;&lt;&lt;&gt;^&gt;&gt;&lt;^v&gt;vv^^v&lt;&lt;^^&gt;&lt;&lt;&gt;v^^&lt;&gt;&lt;&gt;&lt;v^^&lt;v^v&gt;^&gt;^&gt;^&gt;v&lt;^&lt;v&gt;^v^^&gt;v&lt;&gt;vvv&lt;^v&lt;v^v&gt;&lt;&lt;v^&gt;&lt;&lt;^^&gt;&lt;^&lt;&lt;v^v^&gt;v&lt;&gt;^&gt;v&gt;&lt;&gt;&lt;v&gt;^&lt;v&lt;v&gt;^&lt;^^^&gt;^v&lt;&lt;&gt;&lt;&lt;&gt;&lt;&gt;vv&gt;v^&lt;&gt;v^&gt;&lt;v^v&lt;v&gt;&lt;&gt;&lt;&lt;v&gt;v&lt;vv&gt;&lt;&lt;v&gt;&gt;v&gt;^&lt;&lt;&lt;&gt;vv&gt;&gt;vvv&gt;^^vv^v^^&lt;^^&lt;&gt;v^^&lt;&gt;v&gt;&gt;^^&gt;^&gt;^&gt;v&gt;&gt;&lt;^&gt;&gt;&lt;&gt;&gt;^&lt;&lt;&gt;&gt;&lt;^&gt;v&lt;&lt;&lt;&lt;&lt;&lt;&lt;^v^v&lt;v^&lt;v^^&gt;^&lt;&gt;&lt;&lt;v&lt;^&gt;v^&gt;v^vv&lt;&lt;^^vv^&gt;&gt;&gt;&gt;^&lt;&gt;v&lt;^v&lt;&gt;v&lt;vv&lt;^&gt;&gt;v^vv&gt;vv&gt;&lt;vv&lt;&lt;^&gt;v&gt;&gt;&lt;vv&gt;&gt;&gt;vv^&lt;&lt;&lt;&lt;vv^&gt;v&lt;&lt;&lt;&lt;^^&gt;^^v^&gt;&lt;&lt;^&lt;v^&gt;v^&gt;^^&lt;v&lt;&gt;vvv^&gt;^&lt;&gt;vvv&lt;v&lt;^^&gt;v^&lt;&lt;v&gt;&gt;&lt;&gt;v&lt;v&lt;&gt;^^&lt;vvv&gt;^&gt;vv&gt;&lt;&gt;&lt;&lt;&lt;^^vv&lt;v^&lt;v&lt;&gt;v&lt;&gt;&gt;&lt;&lt;v&gt;&lt;^vv^&gt;^&lt;^&gt;^^^&lt;&lt;&lt;v&gt;vv^&lt;^&lt;&lt;&gt;^&gt;^&lt;vv&gt;v&gt;&lt;v&lt;&lt;^&gt;&lt;^&gt;^^&lt;vv^v^^&gt;&gt;&gt;&gt;vv^&gt;&lt;^^vv&gt;&lt;&gt;^&lt;v^v&gt;v&lt;vv&gt;v&gt;&lt;&lt;&lt;v&gt;v&lt;v&gt;^&gt;&lt;v^^&gt;&lt;v&gt;v&lt;^v^&gt;&gt;^^&lt;v^&gt;^^&gt;vv&gt;&gt;vv^&gt;&lt;^vv^vv&lt;&lt;^&gt;vv&gt;^v&lt;v&gt;&lt;vv&gt;&lt;v&lt;vvvvv&gt;^^v^v&gt;&lt;v&gt;&gt;&gt;^vv&lt;&gt;v&gt;^^^^&lt;^&gt;&gt;&lt;&gt;^v^^^&gt;v&lt;^^&lt;&lt;^^v&lt;vv&lt;&gt;vvv&lt;^&gt;&gt;&lt;&gt;&lt;^&gt;&gt;^&gt;&lt;^&lt;&gt;v&lt;v&lt;&lt;&gt;&lt;&lt;v&gt;&lt;v^v&lt;&gt;&gt;&lt;^&gt;v&gt;&lt;&lt;v^&lt;v&gt;v&lt;^&lt;vv^v^v^&gt;vvv^^&gt;v&gt;^&lt;vv^&gt;v^v^&lt;&gt;v&gt;^&gt;&gt;vv&gt;&gt;&lt;^^&lt;v&lt;&lt;&gt;^vv&lt;&gt;&lt;&gt;&lt;&lt;^v&lt;v&gt;v&lt;&lt;vv&gt;&lt;&gt;&gt;&lt;^v&lt;v&gt;&gt;v^&gt;vvv^v^&lt;&lt;^&gt;&lt;v&lt;&gt;^vv^&gt;v^&lt;v&lt;^&gt;&gt;v&lt;v&gt;&lt;v&gt;&lt;v&gt;&gt;^&lt;&lt;&lt;v^&lt;&gt;&lt;&lt;&gt;v&gt;^&gt;^^&lt;v&lt;&gt;&gt;^&lt;&gt;^&gt;^&gt;&lt;&lt;&lt;^&lt;&lt;^&lt;&lt;^&gt;^v&gt;&gt;&gt;&lt;vvv&gt;&gt;&lt;&lt;&lt;&lt;v&gt;&gt;&gt;&gt;&gt;&gt;&gt;^&lt;^v&lt;^&gt;v&lt;&gt;vv&lt;&gt;&lt;&gt;v&gt;&gt;^&gt;&gt;^&gt;vv^^&gt;&lt;&lt;^&lt;v&lt;v&gt;&gt;^^&lt;&lt;^&gt;v&lt;^&gt;&gt;vv&gt;^&lt;&gt;v&gt;&lt;^&gt;v&lt;vv&gt;&gt;&gt;&gt;&gt;&gt;^v&lt;^&lt;&lt;&lt;v^&gt;&lt;vv&lt;&lt;&gt;&gt;vv&lt;&lt;&gt;&lt;v&lt;&gt;&lt;&lt;&lt;v&lt;^&lt;v&lt;&gt;&gt;v&lt;^^^^v^^&lt;^^^&lt;^&lt;vv&gt;&lt;&lt;^&gt;&gt;&lt;&gt;v&lt;&lt;&gt;v&lt;v&lt;&gt;&gt;&gt;&gt;&lt;&gt;v^vv&gt;^&gt;^&gt;&gt;vv^v&lt;v&lt;&lt;&gt;&lt;^v&gt;vv^&gt;&lt;v&lt;&lt;&gt;v^v&lt;^&gt;vv&lt;&lt;^^v&gt;&lt;^&gt;&gt;^^vv&lt;^&lt;&gt;&gt;v^^&gt;&gt;&lt;v&gt;^v&gt;&gt;&gt;^&gt;&gt;v&gt;v&lt;&gt;v&lt;^vv&gt;&lt;&gt;^&lt;&lt;^&gt;vv&gt;&gt;&gt;&lt;&gt;&lt;&gt;v^&gt;&lt;&gt;v^&gt;v&gt;v&gt;&lt;^v&lt;&gt;&lt;v&lt;v&gt;^v&lt;&lt;^vv^&gt;&lt;^^&gt;&gt;&lt;^^^&lt;&lt;&lt;^&gt;v&gt;^v&gt;&gt;&gt;&lt;^&gt;&gt;&lt;^&gt;&gt;&gt;^^^&lt;^&gt;vv&lt;&gt;&lt;&lt;&lt;v^&gt;^&lt;^^&gt;&gt;^^^v^v^v&gt;v&lt;v&gt;&gt;&gt;&gt;&lt;^&gt;&gt;&gt;v&gt;^vv&lt;&lt;^^^&lt;^^vv&gt;v&lt;&lt;&gt;&lt;v&lt;&lt;^^&gt;v&gt;&gt;&lt;&lt;v^^&gt;&lt;^&gt;^&lt;^&gt;^v^&gt;v&gt;&lt;^&lt;^vv&gt;v&gt;&gt;&lt;&gt;^&lt;&lt;vv&lt;&lt;v&gt;v&lt;vv&lt;v&gt;^&gt;^&gt;&gt;&lt;^^&lt;v&gt;^v^v&lt;&gt;&lt;&lt;&gt;vvv&lt;^&lt;v&gt;^&gt;&lt;&gt;^&gt;vvv&gt;&gt;&gt;^&gt;&lt;&lt;&gt;&gt;&lt;v^^&lt;^&lt;&lt;^v&gt;&gt;^v&lt;v&lt;vv&gt;vv^v^&gt;v&lt;&lt;vvv&lt;^^v^v&gt;^&lt;^&gt;&gt;^&gt;v&lt;^&gt;^v&lt;&lt;&gt;&lt;&lt;&lt;^&gt;^&lt;^^^&gt;vv&lt;^^^^vv&lt;v&lt;^^v&lt;&lt;&lt;&lt;v&lt;^v^&lt;&gt;&lt;v&lt;&lt;^&gt;&lt;&lt;&gt;vv&gt;&gt;&gt;&lt;^&lt;^&lt;&gt;&gt;&gt;^&gt;^&gt;&gt;^&lt;&lt;&lt;&lt;&lt;^^v&gt;^&gt;^&lt;&gt;vvv^^&lt;^&gt;&lt;^&gt;^^v&gt;^vv^&gt;&lt;v^&lt;^&lt;&lt;v^&lt;vvv&lt;&lt;^v&lt;&gt;&lt;^&gt;&lt;^&gt;&gt;&gt;v&gt;^v&gt;^&gt;^v&lt;vv^v&gt;&gt;&lt;v&gt;&lt;^&gt;&lt;v^^&gt;v^&gt;^&lt;&gt;&lt;&lt;&gt;&lt;&gt;v&lt;v^&gt;vvv^&gt;^&gt;&gt;v&lt;&gt;^&gt;&lt;^&gt;^&gt;&lt;vvv&gt;^^v^v&gt;v&lt;&gt;^v^&gt;&lt;^&gt;&gt;v&gt;v^&gt;&lt;&lt;&lt;^&gt;&gt;^&lt;&gt;^&lt;&gt;&gt;v&gt;&lt;&gt;&gt;v^&gt;^&gt;^^&lt;&gt;&gt;v^&gt;^&lt;vvvv&lt;^vvvv^&gt;&gt;vv^&lt;v^v&gt;^vv&lt;&gt;v&lt;&gt;^&lt;v&lt;v&gt;v&gt;^^&gt;&lt;^&gt;vv^&lt;^v^&lt;&lt;^&lt;^&lt;&gt;&lt;vv&lt;^v&lt;^v&gt;&lt;&gt;&gt;&gt;^v^&lt;&lt;^&gt;&lt;^&gt;vv&lt;v&gt;v&lt;^&gt;vv^&gt;v&lt;&lt;&lt;&gt;^&lt;&gt;&lt;v&lt;^^^&gt;v&gt;&lt;^^&lt;&gt;^&lt;^&lt;v^vv^&lt;&lt;^&gt;&gt;&lt;&lt;v^v&lt;^vvv&lt;&lt;&lt;&gt;&gt;vvvv^v^^^&gt;v&lt;&gt;&gt;&gt;&lt;&lt;&gt;vvv&lt;&lt;^^^&gt;v&gt;v&gt;&gt;v&lt;&lt;v&lt;v^v^&gt;^^v&gt;^&gt;&lt;^&lt;&gt;&lt;&lt;v^&lt;v&lt;v^^^&gt;&lt;&gt;v^^^&lt;v&gt;vv&lt;&gt;^&gt;^^vv&gt;^&lt;&lt;^v&lt;^v&gt;&lt;v&gt;&gt;&gt;^&gt;&gt;&gt;&lt;^&lt;&lt;&gt;^v&gt;&gt;^&gt;vv&lt;&lt;&lt;v&lt;&gt;^&lt;v&gt;&lt;v^&lt;^&lt;&gt;v&gt;v^^v^&gt;&gt;&lt;&lt;^v&lt;&lt;&lt;&lt;&gt;v&gt;v&gt;v^^&lt;^&gt;&lt;&gt;^^&lt;&lt;&lt;v&gt;vv&lt;&gt;&gt;&gt;^&gt;&gt;v&lt;&gt;&lt;v^&gt;^&lt;&gt;&lt;vv&gt;v&gt;v^v&lt;v^&lt;^&gt;&gt;^&gt;&gt;&lt;&lt;^^&lt;^^v&lt;vv&lt;&gt;&gt;&lt;&lt;&lt;v&lt;^&lt;&lt;^^^&gt;vvv^&lt;vvv&lt;^&gt;vv&gt;&lt;&gt;&gt;&lt;&lt;&lt;^&lt;v^v^^&lt;&lt;^vvv^^&lt;^&lt;&gt;&lt;&lt;&gt;^&lt;^&lt;&gt;&gt;vvv&lt;&gt;^&lt;&gt;v^v&lt;&gt;&lt;&gt;&gt;v^v&gt;&lt;&lt;&gt;&gt;&gt;vvv&gt;v&lt;&gt;^&gt;&gt;^&gt;&lt;^&gt;vv&lt;&lt;&gt;&gt;v&lt;&lt;^&gt;&lt;&gt;v&gt;&gt;^^&lt;v&gt;^&gt;^&lt;&lt;&gt;&gt;&lt;^&lt;&lt;vv&lt;^&lt;vv^vv&gt;&lt;&gt;&gt;&gt;&gt;&lt;^&lt;v&gt;^&gt;vv&lt;v&gt;&lt;&gt;^&lt;&gt;vvvvv^vv&lt;&lt;v&lt;&gt;&gt;&gt;^&lt;&lt;&gt;&lt;&gt;^^vvv&gt;&gt;&gt;vv&lt;&lt;^^&gt;&lt;^v^^v&lt;&gt;^^&gt;^&gt;&lt;^&gt;v^^^^v&lt;^&lt;&lt;vv&lt;vv&lt;&gt;vv^^&gt;v^vv&gt;v&gt;&lt;&gt;&gt;vv&gt;^&lt;^&lt;v^v^&gt;&gt;v^v^^v&gt;^&gt;vv^&gt;v&lt;vvvv&lt;^v&lt;^v&gt;^v&gt;^^v&lt;&lt;^&gt;^^&lt;&lt;&gt;^&gt;&lt;^v&gt;&gt;&gt;vv^&gt;^^&gt;vvvv&gt;&gt;v&lt;^&lt;v&gt;^&gt;&gt;&gt;v^&lt;&gt;&lt;^&lt;^^&lt;v&gt;vv^^&gt;&lt;v&gt;v^&lt;&gt;^^^&gt;&gt;&gt;&lt;^^v&gt;v&gt;^&lt;&lt;&gt;^&lt;v^&gt;vvv^&gt;^^^&gt;&lt;v&lt;^&gt;&gt;v&lt;v&gt;&gt;^v&gt;&lt;&lt;&gt;&lt;&lt;&gt;v&lt;^&lt;&lt;&gt;^&gt;&lt;&gt;^&gt;vv&gt;^&lt;v&gt;^^v&lt;&lt;^v^vvv^^&gt;^vv^&lt;^&gt;^&gt;^^v&gt;v^&gt;^&lt;&lt;&gt;&lt;&lt;^&gt;v&gt;&gt;vv^vv&gt;&lt;v&gt;&gt;^&lt;&lt;^&lt;v^^&lt;^&lt;v^^vv^&gt;&lt;^^&lt;^^&gt;&lt;v^^&gt;v^^^&lt;^&lt;&gt;^&lt;&gt;&gt;^v&lt;^vvv^^v^&lt;&gt;&lt;^&gt;&gt;&gt;&gt;&gt;v&gt;&lt;&gt;&lt;&lt;&lt;&gt;vv&lt;^v&gt;&gt;&lt;&lt;&gt;vvv&lt;&gt;&lt;&lt;vv&lt;&lt;&lt;^&gt;v^^&gt;&gt;^&gt;^v&gt;&gt;&lt;&gt;&lt;^^v&lt;&gt;&gt;&lt;&gt;&gt;v^&gt;^&lt;vv&gt;&lt;&lt;&lt;&gt;&gt;&lt;&gt;&lt;&lt;v&gt;^^&lt;&gt;&gt;v&lt;&gt;&lt;^&lt;vv&gt;vv&lt;^v&gt;^&lt;&lt;&lt;&lt;v&lt;^&lt;&lt;^^&gt;&gt;^&lt;&gt;&lt;^&gt;&gt;&lt;&lt;&gt;^&gt;v&gt;^^^v&gt;&gt;^&lt;^^v&gt;&lt;v^v&gt;^&gt;&lt;&lt;&gt;&lt;&gt;&gt;^&gt;&gt;^&lt;&lt;v&lt;&gt;^v&lt;&gt;^&gt;^&lt;v&gt;&gt;vv&gt;^vvv&lt;&lt;v&lt;&lt;^&gt;^&gt;^&lt;&lt;^^&lt;&gt;^^^^vvv&lt;&gt;^vv&lt;vvvvv^^&gt;^^&lt;^&gt;&gt;&gt;&lt;&gt;v^&lt;&gt;&lt;^&lt;&lt;^&gt;v^^v&lt;&gt;&gt;^vv&lt;&gt;v^^&lt;&gt;&gt;v^vvvvv&lt;&lt;v^&lt;v^^&gt;&gt;&gt;&lt;vvvvv&gt;&gt;&lt;^&gt;vv&gt;v^v^&lt;v&lt;^&gt;^^&gt;&lt;^&gt;^^^^v&lt;&gt;&lt;^v&lt;&lt;&gt;v^&gt;v&gt;&gt;vv&lt;&lt;&gt;^&lt;v^^&gt;vvv&gt;^^&lt;v^&lt;&gt;vv^&gt;&lt;&gt;&gt;&lt;v^^v&lt;&gt;^&gt;&gt;^&gt;v&gt;&lt;&gt;&gt;^^v&gt;^&gt;^&gt;&gt;&gt;^&gt;v&lt;^v&gt;v&gt;^&lt;^^^^^&gt;&gt;v&lt;v&lt;&gt;&gt;v&lt;&lt;^&gt;^&lt;v&lt;&lt;&gt;^^&gt;&gt;&lt;&lt;^&gt;&lt;&gt;v&lt;&gt;^^^vv&lt;&gt;^^&gt;&gt;&lt;&lt;^^&gt;v&gt;vv&gt;vv&gt;v^&gt;^v&gt;v^^&lt;&gt;&gt;&gt;&lt;&lt;v&gt;&lt;v&lt;&lt;&gt;&gt;v&gt;&lt;&gt;vvv^^v&gt;^^&gt;^vvvv^&gt;^&lt;&gt;^vvvv&gt;&lt;v&gt;&lt;v&lt;&gt;&gt;&gt;&lt;&gt;^&lt;^vv&lt;&gt;^v&lt;^v&lt;&gt;^vvv&lt;&lt;&gt;&gt;&lt;vvv^&gt;&gt;^&gt;&lt;&lt;vv^&lt;v^&gt;^&lt;v&lt;&lt;^^&gt;^^&lt;^^v^&gt;v&lt;&gt;v^v&gt;&lt;&gt;&gt;&lt;v^^&gt;&gt;^vvv&gt;&lt;^vv&gt;v^&lt;^&lt;^v&gt;&gt;v^^&gt;^vvv^&lt;v^^v^^&gt;v&lt;^&lt;&gt;&gt;^&lt;&gt;&gt;&gt;^^&lt;&gt;&lt;^^vv&lt;&gt;^vv^&lt;&gt;&gt;&gt;&gt;^^&lt;&lt;^^&lt;&gt;vv^^&gt;&lt;&gt;^^&lt;v&lt;&lt;v&gt;^&lt;v^^&gt;^v&lt;&gt;&lt;&gt;&lt;&gt;vvv&gt;^v^&gt;&gt;vv&lt;&lt;^v&lt;&lt;&gt;&gt;&lt;v&gt;^&gt;&lt;^&gt;&gt;&gt;^&lt;^&lt;^^&gt;vv^&lt;&lt;^&lt;&gt;&gt;^^&gt;&lt;&gt;&lt;&lt;v&gt;^^&lt;v&gt;&gt;v&lt;&lt;vvvv&gt;^v^vv&gt;&gt;&lt;^^&lt;&lt;^&gt;&gt;v&gt;v&lt;^^^&lt;^&gt;&lt;^^vv&gt;^vv&lt;^&lt;vv&lt;&gt;v&gt;&lt;^&lt;&gt;&lt;v&gt;&lt;^^^&gt;&gt;^&lt;&gt;&lt;^&lt;v&gt;&gt;&gt;&gt;v^&lt;v&gt;&gt;&gt;&gt;&gt;v&lt;&gt;&lt;^^&gt;v&lt;^&lt;^&gt;&gt;&lt;v&lt;&gt;^&gt;vv&gt;^^v^v^&lt;&lt;v&lt;&gt;&lt;&lt;&lt;^v^&gt;&lt;&lt;^&lt;&gt;&lt;&lt;&lt;&lt;v&lt;^&gt;&gt;&lt;&lt;&lt;&gt;v&gt;&gt;vv&gt;&lt;vv&lt;&gt;&lt;&lt;^&lt;^&lt;&gt;&lt;vv&gt;^^^^&lt;&gt;v&lt;&lt;&lt;&lt;v&gt;vv&lt;&gt;vv^^^&gt;&gt;&lt;&gt;vv^&gt;&lt;&gt;&gt;^vv&lt;&lt;&gt;&lt;^^vv&lt;&gt;v^&gt;&gt;^&lt;&lt;&gt;^&lt;v^&lt;^&gt;v&lt;'

</span><span style="color:blue;">DECLARE </span>@sx <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@sy <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@rx <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@ry <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@useRobot <span style="color:blue;">bit </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@x <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@y <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@i <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@i_max <span style="color:blue;">smallint </span><span style="color:gray;">= </span>0
	<span style="color:gray;">, </span>@char <span style="color:blue;">char</span><span style="color:gray;">(</span>1<span style="color:gray;">)

</span><span style="color:blue;">SELECT </span>@i_max <span style="color:gray;">= </span><span style="color:magenta;">len</span><span style="color:gray;">(</span>@input<span style="color:gray;">)

</span><span style="color:blue;">CREATE TABLE </span>#visited
<span style="color:gray;">(
	</span>x <span style="color:blue;">smallint
	</span><span style="color:gray;">, </span>y <span style="color:blue;">smallint
	</span><span style="color:gray;">, </span><span style="color:blue;">PRIMARY KEY </span><span style="color:gray;">(</span>x<span style="color:gray;">,</span>y<span style="color:gray;">)
)

</span><span style="color:blue;">WHILE </span>@i <span style="color:gray;">&lt;= </span>@i_max <span style="color:blue;">BEGIN
	SELECT </span>@char <span style="color:gray;">= </span><span style="color:magenta;">substring</span><span style="color:gray;">(</span>@input<span style="color:gray;">, </span>@i<span style="color:gray;">, </span>1<span style="color:gray;">)

	</span><span style="color:blue;">IF </span>@i <span style="color:gray;">% </span>2 <span style="color:gray;">= </span>0 <span style="color:gray;">AND </span>@useRobot <span style="color:gray;">= </span>1 <span style="color:blue;">BEGIN
		SELECT </span>@x <span style="color:gray;">= </span>@sx
			<span style="color:gray;">, </span>@y <span style="color:gray;">= </span>@sy
	<span style="color:blue;">END ELSE BEGIN
		SELECT </span>@x <span style="color:gray;">= </span>@rx 
			<span style="color:gray;">, </span>@y <span style="color:gray;">= </span>@ry
	<span style="color:blue;">END

	IF </span>@char <span style="color:gray;">= </span><span style="color:red;">'&gt;' </span><span style="color:blue;">BEGIN
		SELECT </span>@x <span style="color:gray;">= </span>@x <span style="color:gray;">+ </span>1
	<span style="color:blue;">END ELSE IF </span>@char <span style="color:gray;">= </span><span style="color:red;">'&lt;' </span><span style="color:blue;">BEGIN
		SELECT </span>@x <span style="color:gray;">= </span>@x <span style="color:gray;">- </span>1
	<span style="color:blue;">END ELSE IF </span>@char <span style="color:gray;">= </span><span style="color:red;">'^' </span><span style="color:blue;">BEGIN
		SELECT </span>@y <span style="color:gray;">= </span>@y <span style="color:gray;">+ </span>1
	<span style="color:blue;">END ELSE IF </span>@char <span style="color:gray;">= </span><span style="color:red;">'V' </span><span style="color:blue;">BEGIN
		SELECT </span>@y <span style="color:gray;">= </span>@y <span style="color:gray;">- </span>1 
	<span style="color:blue;">END

	IF </span>0 <span style="color:gray;">= (</span><span style="color:blue;">SELECT </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">) </span><span style="color:blue;">FROM </span>#visited <span style="color:blue;">WHERE </span>x <span style="color:gray;">= </span>@x <span style="color:gray;">AND </span>y <span style="color:gray;">= </span>@y<span style="color:gray;">) </span><span style="color:blue;">BEGIN
		INSERT INTO </span>#visited<span style="color:gray;">(</span>x<span style="color:gray;">, </span>y<span style="color:gray;">) </span><span style="color:blue;">SELECT </span>@x<span style="color:gray;">, </span>@y 
	<span style="color:blue;">END

	IF </span>@i <span style="color:gray;">% </span>2 <span style="color:gray;">= </span>0 <span style="color:gray;">AND </span>@useRobot <span style="color:gray;">= </span>1 <span style="color:blue;">BEGIN
		SELECT </span>@sx <span style="color:gray;">= </span>@x
			<span style="color:gray;">, </span>@sy <span style="color:gray;">= </span>@y
	<span style="color:blue;">END ELSE BEGIN
		SELECT </span>@rx <span style="color:gray;">= </span>@x 
			<span style="color:gray;">, </span>@ry <span style="color:gray;">= </span>@y
	<span style="color:blue;">END

	SELECT </span>@i <span style="color:gray;">= </span>@i <span style="color:gray;">+ </span>1
<span style="color:blue;">END

SELECT </span><span style="color:magenta;">COUNT</span><span style="color:gray;">(</span>1<span style="color:gray;">)
</span><span style="color:blue;">FROM </span>#visited 
</pre>
<p>I&#8217;m not the only one doing this with T-SQL.  Andy Warren also posted his very different, yet same results, version in separate posts for the two parts: <a href="http://sqlandy.com/2015/12/advent-of-code-3-part-1/" target="_blank">Day 3 Part 1</a> and <a href="http://sqlandy.com/2015/12/advent-of-code-3-part-2/" target="_blank">Day 3 Part 2</a>.  His came out more complex, but more set-based.</p>
<p>My problem is that I develop T-SQL for the problem at hand with the expected size of the data.  Andy is looking ahead and developed a method that will probably scale much better than mine.  I&#8217;m hedging my bets and hoping the elf in charge of this either sobers up or gets fired.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://simplesqlserver.com/2015/12/24/advent-of-code-day-3/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">40241</post-id>
		<media:content medium="image" url="https://0.gravatar.com/avatar/6fe5305115649bb962b1aa98e8456a07e3f5ea51c3b8df3cccee0c4f2baf2816?s=96&amp;d=identicon&amp;r=PG">
			<media:title type="html">stevehood79</media:title>
		</media:content>
	</item>
	</channel>
</rss>