<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>JimMcLeod.Net</title>
	
	<link>http://www.jimmcleod.net/blog</link>
	<description>Musings about SQL, databases, and my world in general...</description>
	<pubDate>Tue, 06 Oct 2009 10:39:35 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/Jimmcleodnet" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>Welcome, Dashiell!</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/fwVK3Ggk3Bs/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/10/06/welcome-dashiell/#comments</comments>
		<pubDate>Tue, 06 Oct 2009 10:34:36 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/?p=143</guid>
		<description><![CDATA[I’ve been fairly busy recently, and I’m likely to stay that way.&#160; Following on from this event, my second son Dashiell was born today.&#160; More pictures will follow separately for those that are interested (Hi Mum!), but rest assured that he looks almost identical to Lachlan. 
For those of you that are looking for a [...]]]></description>
			<content:encoded><![CDATA[<p>I’ve been fairly busy recently, and I’m likely to stay that way.&#160; Following on from <a href="http://www.jimmcleod.net/blog/index.php/2008/02/17/new-job/">this event</a>, my second son Dashiell was born today.&#160; More pictures will follow separately for those that are interested (Hi Mum!), but rest assured that he looks almost identical to Lachlan. </p>
<p>For those of you that are looking for a SQL-bent to this post, you can consider Dash a logged shipped version of Lachlan, with a latency of 20 months, except his DBAs know what they’re doing this time around.&#160; In another 20 months, they’ll form an active-active cluster and my life will be even more hectic <img src='http://www.jimmcleod.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/10/image.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/10/image-thumb.png" width="244" height="158" /></a></p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/fwVK3Ggk3Bs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/10/06/welcome-dashiell/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/10/06/welcome-dashiell/</feedburner:origLink></item>
		<item>
		<title>SQL Server Agent Scheduler Has No AM/PM Selector</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/pJbvsCAbVaw/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/09/04/sql-server-agent-scheduler-has-no-ampm-selector/#comments</comments>
		<pubDate>Thu, 03 Sep 2009 21:00:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[Articles]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/09/04/sql-server-agent-scheduler-has-no-ampm-selector/</guid>
		<description><![CDATA[I recently came across a situation at a client’s site where I was unable to choose whether a SQL Agent schedule would be AM or PM.&#160; Here’s a screenshot:
 
There is no AM/PM option, and clicking in the area of the control where it should be does nothing.&#160; The first part – the hour – [...]]]></description>
			<content:encoded><![CDATA[<p>I recently came across a situation at a client’s site where I was unable to choose whether a SQL Agent schedule would be AM or PM.&#160; Here’s a screenshot:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/09/image1.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/09/image-thumb1.png" width="244" height="212" /></a> </p>
<p>There is no AM/PM option, and clicking in the area of the control where it should be does nothing.&#160; The first part – the hour – only goes from 1 to 12.&#160; This means it’s only possible to schedule the job to run between midnight and 11:59:59am.</p>
<p>The cause of the problem was in the Standard Operating Environment settings for the organisation, where their default regional settings have the time set to h:mm:ss.&#160; This displays times in 12-hour (instead of 24 hour), and does not display an AM/PM symbol.&#160; This can be solved by changing the regional settings (via the Regional Settings Control Panel) time format on the computer to H:mm:ss (24 hour, no AM/PM) or h:mm:ss tt (12 hour, with AM/PM).&#160; After this is done, reopen the schedule dialog box, and all is well.&#160; </p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/09/image2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/09/image-thumb2.png" width="244" height="149" /></a> </p>
<p>This is an issue with both Management Studio 2005 and 2008, running on Windows XP, Vista and Server 2003, but I think it’s rare that the regional settings will be configured in such a way.</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/pJbvsCAbVaw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/09/04/sql-server-agent-scheduler-has-no-ampm-selector/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/09/04/sql-server-agent-scheduler-has-no-ampm-selector/</feedburner:origLink></item>
		<item>
		<title>Quick Tip #5: Get Column Names</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/dcJSK3J1jLE/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/09/02/quick-tip-5-get-column-names/#comments</comments>
		<pubDate>Tue, 01 Sep 2009 21:00:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[QuickTip]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/09/02/quick-tip-5-get-column-names/</guid>
		<description><![CDATA[(Updated due to noeldr&#8217;s comment being a better option)
We all know that SELECT * is frowned upon when writing queries, but some tables have lots of columns that you don’t want to type out all the column names for.  What’s the fastest way of getting a column list?
I used to script out the CREATE statement [...]]]></description>
			<content:encoded><![CDATA[<p>(Updated due to noeldr&#8217;s comment being a better option)</p>
<p>We all know that SELECT * is frowned upon when writing queries, but some tables have lots of columns that you don’t want to type out all the column names for.  What’s the fastest way of getting a column list?</p>
<p>I used to script out the CREATE statement for the table, and then remove any extra square brackets and types.  This had a lot of editing associated with it, which is relieved with Management Studio 2008 which provides the “SELECT TOP 1000 Rows” when right clicking on the table.  This still provides square brackets, however.  Other options were to put the results into text mode, and copy the header line.  <span style="text-decoration: line-through;">However, the fastest method I’ve found is using the Query Designer, which I’ve never had use for.</span> Another option is to use the Query Designer, which I still have no use for.</p>
<p>To get a list of columns for a table, create a SELECT * statement for it:</p>
<pre class="csharpcode"><span class="kwrd">USE</span> AdventureWorks
<span class="kwrd">GO</span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> Production.Product</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>Next, highlight the SELECT statement, and press Control-Shift-Q or use the menu item “Design Query in Editor…” under the “Query” menu to bring up the Query Designer:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/09/image.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/09/image-thumb.png" border="0" alt="image" width="244" height="141" /></a></p>
<p>Select or deselect the columns you don’t want, and copy the list to the clipboard.  That’s it!</p>
<p>Noeldr&#8217;s option, from the comments below is even better, and works in either Management Studio or Query Analyzer.  Simply open the Object Explorer up to the table, expand the table so that the &#8220;Columns&#8221; node is visible, and drag this item from the Object Explorer into your query window.  I love how you can work with a product for years and still find little tips like this!</p>
<p>Are there any other easy options I’ve missed, or should I always use square brackets when using column names?  Let me know!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/dcJSK3J1jLE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/09/02/quick-tip-5-get-column-names/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/09/02/quick-tip-5-get-column-names/</feedburner:origLink></item>
		<item>
		<title>The Difference Between Index Seeks and Scans</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/LCOsRKjDrSw/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/08/31/the-difference-between-index-seeks-and-scans/#comments</comments>
		<pubDate>Sun, 30 Aug 2009 21:00:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[Articles]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/08/31/the-difference-between-index-seeks-and-scans/</guid>
		<description><![CDATA[When is an index scan not a scan?&#160; When it’s a seek!
Consider the following query:
USE AdventureWorks
GO
SELECT * FROM Production.TransactionHistory

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode [...]]]></description>
			<content:encoded><![CDATA[<p>When is an index scan not a scan?&#160; When it’s a seek!</p>
<p>Consider the following query:</p>
<pre class="csharpcode"><span class="kwrd">USE</span> AdventureWorks
<span class="kwrd">GO</span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> Production.TransactionHistory</pre>
<style type="text/css">
<p>.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>As there is no filter on the table, you would expect to see an index scan.&#160; Turn on the Actual Execution Plan, and SET STATISTICS IO ON, and have a look at the results:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/08/image.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/08/image-thumb.png" width="244" height="67" /></a> </p>
<pre class="csharpcode">(113443 row(s) affected)
Table 'TransactionHistory'. Scan count 1, logical reads 792,
physical reads 0, read-ahead reads 0, lob logical reads 0,
lob physical reads 0, lob read-ahead reads 0.</pre>
<p>Just as expected.&#160; Let’s now consider the following query:</p>
<pre class="csharpcode"><span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> Production.TransactionHistory <span class="kwrd">WHERE</span> TransactionID &gt;= 1</pre>
<style type="text/css">
<p>.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<pre class="csharpcode">(113443 row(s) affected)
Table 'TransactionHistory'. Scan count 1, logical reads 792,
physical reads 0, read-ahead reads 0, lob logical reads 0,
lob physical reads 0, lob read-ahead reads 0.</pre>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/08/image1.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/08/image-thumb1.png" width="244" height="49" /></a>&#160;</p>
<p>The query returns the same set of rows, and has identical performance, but shows up in the execution plan as a seek!</p>
<p>The take-home message here is that just because you can see an index seek in your query does not mean that performance is great.&#160; It means that the table was accessed using a filter you’ve placed on the query.&#160; A lesser message, but sometimes overlooked is that a seek can “scan” a long range of values, if each of the values matches the filter.</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/LCOsRKjDrSw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/08/31/the-difference-between-index-seeks-and-scans/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/08/31/the-difference-between-index-seeks-and-scans/</feedburner:origLink></item>
		<item>
		<title>The Potential Dangers of the Read Committed Snapshot Isolation Level</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/HSlEk6S6dTM/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/08/27/the-potential-dangers-of-the-read-committed-snapshot-isolation-level/#comments</comments>
		<pubDate>Wed, 26 Aug 2009 21:00:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[Articles]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/08/27/the-potential-dangers-of-the-read-committed-snapshot-isolation-level/</guid>
		<description><![CDATA[The Snapshot Isolation Level and Read Committed Snapshot features were a big improvement when SQL Server 2005 came out – finally, SQL Server had optimistic locking as well as pessimistic locking!&#160; This is a feature that allows writers not to block readers, and readers will not block writers – instead, the readers will look at [...]]]></description>
			<content:encoded><![CDATA[<p>The Snapshot Isolation Level and Read Committed Snapshot features were a big improvement when SQL Server 2005 came out – finally, SQL Server had optimistic locking as well as pessimistic locking!&#160; This is a feature that allows writers not to block readers, and readers will not block writers – instead, the readers will look at the most recent row, and ignore the fact that it’s currently being written to.&#160; This sounds great on first inspection – long running transactions won’t block other transactions! If you accidentally have a BEGIN TRANSACTION but you haven’t followed up with a COMMIT or a ROLLBACK, other users won’t be blocked.</p>
<p>It recently came up in discussion that you can just turn on Read Committed Snapshot, and a lot of your blocking will be a thing of the past.&#160; While this is true, I strongly discourage turning on Read Committed Snapshot for existing applications unless you know <strong>exactly</strong> how your application will respond to it.</p>
<p>Read Committed Snapshot is a modification to the Read Committed Isolation level that uses row versioning to read the previous value.&#160; The trouble comes when you realise that Read Committed Snapshot is the default isolation level, and once you’ve turned it on, every single read committed transaction will run as a snapshot.&#160; </p>
<p>If the developers of your application expect that Read Committed will be the default (back when they wrote the application when SQL Server 2000 was the latest version, this was a reasonable assumption), then changing Read Committed’s behaviour can have unintended consequences.</p>
<p>Let’s look at an example.&#160; We’ll create a new database, containing a single table, Inventory.&#160; This is a simple stock control system, and we’ll be looking at the procedure to check out an item. It’s worth noting that there are better and safer methods of writing the stock checkout function to not have this issue (such as using Repeatable Read), but it’s very possible that the developers of your application used something similar, and it worked “good enough”.</p>
<pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">DATABASE</span> ReadCommittedSnapshotTest
<span class="kwrd">GO</span>

<span class="kwrd">USE</span> ReadCommittedSnapshotTest
<span class="kwrd">GO</span>

<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> Inventory (
      ItemID <span class="kwrd">int</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , LocationID <span class="kwrd">int</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , Qty <span class="kwrd">int</span>
    , <span class="kwrd">PRIMARY</span> <span class="kwrd">KEY</span> (ItemID, LocationID)
)
<span class="kwrd">CREATE</span> <span class="kwrd">NONCLUSTERED</span> <span class="kwrd">INDEX</span> ncixLocationItem <span class="kwrd">ON</span> Inventory(LocationID, ItemID)
GO</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>Next, we’ll insert some data.</p>
<pre class="csharpcode"><span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>
INSERT <span class="kwrd">INTO</span> Inventory (ItemID, LocationID, Qty)
<span class="kwrd">VALUES</span>(rand() * 1000 + 1, 1, rand() * 40 + 1)
INSERT <span class="kwrd">INTO</span> Inventory (ItemID, LocationID, Qty)
<span class="kwrd">VALUES</span>(rand() * 1000 + 1, 2, rand() * 40 + 1)
INSERT <span class="kwrd">INTO</span> Inventory (ItemID, LocationID, Qty)
<span class="kwrd">VALUES</span>(rand() * 1000 + 1, 3, rand() * 40 + 1)
<span class="kwrd">GO</span> 2000
<span class="rem">-- Ignore any key violation errors - we'll still get enough data</span>
INSERT <span class="kwrd">INTO</span> Inventory (ItemID, LocationID, Qty) <span class="kwrd">VALUES</span> (796, 1, 5)</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>For the next part, we need to run the following query on two separate sessions at the same time.&#160; I’ve added a 10-second delay in processing the transaction – you can imagine that there may be other processing required to complete the checkout, and this may take a second or two.&#160; Ten seconds is a little unrealistic, but provides enough time to run the query in the other window.</p>
<p>The way this transaction works is that we begin a transaction, and read the number of items that are in stock at the moment.&#160; If there are more than we want to take, we update the Inventory.</p>
<pre class="csharpcode"><span class="kwrd">BEGIN</span> <span class="kwrd">TRANSACTION</span>
<span class="kwrd">DECLARE</span> @QtyRequired <span class="kwrd">int</span>, @QtyRemain <span class="kwrd">int</span>
<span class="kwrd">SELECT</span> @QtyRequired = 4
<span class="kwrd">SELECT</span> @QtyRemain = <span class="kwrd">SUM</span>(QTY) <span class="kwrd">FROM</span> Inventory <span class="kwrd">WHERE</span> ItemID = 796 <span class="kwrd">AND</span> LocationID = 1
<span class="kwrd">IF</span> @QtyRemain - @QtyRequired &gt;= 0
<span class="kwrd">BEGIN</span>
    <span class="kwrd">UPDATE</span> Inventory <span class="kwrd">SET</span> Qty = Qty - @QtyRequired
    <span class="kwrd">WHERE</span> ItemID = 796 <span class="kwrd">AND</span> LocationID = 1
    <span class="rem">-- Do other stuff in other tables or databases to check out the item</span>
    <span class="kwrd">WAITFOR</span> DELAY <span class="str">'00:00:10'</span>
    <span class="kwrd">SELECT</span> <span class="str">'Checkout complete'</span>
<span class="kwrd">END</span>
<span class="kwrd">ELSE</span>
    <span class="kwrd">SELECT</span> <span class="str">'Not enough qty!'</span>
<span class="kwrd">COMMIT</span> <span class="kwrd">TRANSACTION</span>

<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> Inventory <span class="kwrd">WHERE</span> ItemID = 796</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>If you run the two queries together, you’ll notice that both queries take about 10 seconds to run, and the one that ran first will report “Checkout complete”, and the other will report “Not enough qty!”.&#160; This is good – the second query was blocked until the first was finished.</p>
<p>Let’s turn on READ COMMITTED SNAPSHOT and see what the effect will be.&#160; First we’ll replace the items so we can run the same query again.</p>
<pre class="csharpcode"><span class="rem">-- Replace the inventory</span>
<span class="kwrd">UPDATE</span> Inventory <span class="kwrd">SET</span> Qty = 5 <span class="kwrd">WHERE</span> ItemID = 796 <span class="kwrd">AND</span> LocationID = 1

<span class="rem">-- Turn on READ_COMMITTED_SNAPSHOT</span>
<span class="kwrd">ALTER</span> <span class="kwrd">DATABASE</span> ReadCommittedSnapshotTest
<span class="kwrd">SET</span> READ_COMMITTED_SNAPSHOT <span class="kwrd">ON</span>
<span class="rem">-- You may need to disconnect the other session for this to complete.</span></pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>Run the two queries again, side by side.&#160; This time, it still takes 10 seconds to run both queries, but the second query returns a –3 quantity.&#160; Oh dear. </p>
<p>As mentioned, using Repeatable Read would help solve this issue, as you would be guaranteed the data wouldn’t change between the initial stock count check, and the update.&#160; As the two queries are VERY close together, you may never see this issue with the code above, but it still can happen.&#160; As I mentioned, this is an example of “good enough” code.&#160; However, the difference is that the problem only has a narrow window of a few milliseconds to occur with READ COMMITTED, but has 10 seconds to occur with READ COMMITTED SNAPSHOT.</p>
<p>The conclusion of this example is that your application may have unexpected results if you blindly turn on READ COMMITTED SNAPSHOT.&#160; It is a great feature of SQL Server 2005, but unless you know exactly what the effects will be, I don’t recommend turning it on for existing systems.</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/HSlEk6S6dTM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/08/27/the-potential-dangers-of-the-read-committed-snapshot-isolation-level/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/08/27/the-potential-dangers-of-the-read-committed-snapshot-isolation-level/</feedburner:origLink></item>
		<item>
		<title>Melbourne SQL Server User Group Presentation - Encryption</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/LIs8gIABWQE/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/07/24/melbourne-sql-server-user-group-presentation-encryption/#comments</comments>
		<pubDate>Fri, 24 Jul 2009 07:00:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/07/24/melbourne-sql-server-user-group-presentation-encryption/</guid>
		<description><![CDATA[I’m making my debut large-scale SQL Server presentation at the Melbourne SQL Server user group next Tuesday, July 28.&#160; Do come along!
Are your database files and backups and the data inside your tables safe from prying eyes? Or are the doors slightly ajar, or worse - wide open? It is ever more important to secure [...]]]></description>
			<content:encoded><![CDATA[<p>I’m making my debut large-scale SQL Server presentation at the Melbourne SQL Server user group next Tuesday, July 28.&#160; Do come along!</p>
<blockquote><p>Are your database files and backups and the data inside your tables safe from prying eyes? Or are the doors slightly ajar, or worse - wide open? It is ever more important to secure data these days, and the database is at the heart of your organisation.</p>
<p>Join Jim McLeod as he explores the various SQL Server features that can be used to secure your data. In this presentation, we also examine a method of indexing the encrypted data to allow fast retrieval, and to use SQL Server&#8217;s encryption functions for a variety of practical uses.</p>
</blockquote>
<p>Registration is at <a href="http://www.sqlserver.org.au">http://www.sqlserver.org.au</a>.</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/LIs8gIABWQE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/07/24/melbourne-sql-server-user-group-presentation-encryption/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/07/24/melbourne-sql-server-user-group-presentation-encryption/</feedburner:origLink></item>
		<item>
		<title>Oops, didn’t mean to run the entire script…</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/7Se9R5Ag-Zc/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/07/23/oops-didnt-mean-to-run-the-entire-script/#comments</comments>
		<pubDate>Thu, 23 Jul 2009 07:30:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[Articles]]></category>

		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/07/23/oops-forgot-to-highlight-that-statement/</guid>
		<description><![CDATA[When developing T-SQL code, I frequently have a number of stored procedures created and run inside one .sql file.  While I’m generally good at highlighting the single statement I want to run, there are some situations where the highlight is misplaced – usually by me getting complacent with the lack of lag on Remote Desktop.  [...]]]></description>
			<content:encoded><![CDATA[<p>When developing T-SQL code, I frequently have a number of stored procedures created and run inside one .sql file.  While I’m generally good at highlighting the single statement I want to run, there are some situations where the highlight is misplaced – usually by me getting complacent with the lack of lag on Remote Desktop.  Suddenly I’ll get a dose of lag, my highlight command (shift-uparrow) won’t be processed, but my Execute (F5) command will.</p>
<p>To defend against this, I determined that the best option would be to create a severe error at the top of the .sql file to ensure that execution would cease.  Unfortunately, this only stops the execution of the current batch, and as CREATE PROCEDURE must be at the start of the batch, I have many batches in my code.</p>
<p>Five minutes after requesting suggestions from Twitter, <a href="http://msmvps.com/blogs/robfarley/">Rob Farley</a> suggested using SQLCMD mode – which is a feature that I haven’t used very often.  This is activated in Management Studio via the “Query” | “SQLCMD Mode” menu item, and can be specified as the default mode for all new query windows.</p>
<p>Placing the following code at the top of the file results in a fatal error that completely halts execution:</p>
<pre class="csharpcode">:<span class="kwrd">on</span> error <span class="kwrd">exit</span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> SomeTableThatDoesntExist
<span class="kwrd">GO</span>

!!dir
GO</pre>
<p><!--.csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>The !!dir command will list the contents of the current directory.  You can run this line by itself, but this line won’t be executed if you run the entire .sql file.</p>
<p>The drawback to this method is that you lose Intellisense in SQL Server 2008 and the ability to debug your code, but I’m willing to live with this to prevent having to comment out my more dangerous statements!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/7Se9R5Ag-Zc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/07/23/oops-didnt-mean-to-run-the-entire-script/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/07/23/oops-didnt-mean-to-run-the-entire-script/</feedburner:origLink></item>
		<item>
		<title>Applications using Hashing with SQL Server 2005/2008</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/ROPaam3QcK8/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/04/23/applications-using-hashing-with-sql-server-20052008/#comments</comments>
		<pubDate>Thu, 23 Apr 2009 13:39:22 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[Articles]]></category>

		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/04/23/applications-using-hashing-with-sql-server-20052008/</guid>
		<description><![CDATA[In my previous post, I discussed how you can use the HashBytes() function to generate hashes.  I gave a couple of examples where you could use this for your benefit, but a follow-up post is necessary.
Just to recap, a hash is a one-way encryption of a set of data of any size to come up [...]]]></description>
			<content:encoded><![CDATA[<p>In my previous post, I discussed how you can use the HashBytes() function to generate hashes.  I gave a couple of examples where you could use this for your benefit, but a follow-up post is necessary.</p>
<p>Just to recap, a hash is a one-way encryption of a set of data of any size to come up with a smaller (eg, 20 bytes for SHA1) that can almost-uniquely identify the original source.  It is NOT a compressed version of the source – you cannot reconstruct the original source with the hash.</p>
<p>The main reason for using hashes is to see if things have changed.  The MDx hashing algorithms (Message Digest) was originally used to determine if email or Usenet messages had been read.  Instead of sending all messages, you could receive the hashes, and then compare the list of hashes with what you have stored locally, and only download those that you are missing.</p>
<p>A similar use of hashes is used to protect the integrity of executable files available for download.  You can create a hash of the executable, and when the user has downloaded your file, they can re-generate the hash to ensure that nothing has changed it (for example, a virus latching onto the executable).  If the hashes do not match, you would be wary of running the program.</p>
<p>Back in the database world, here are three scenarios where you might like to use hashes:</p>
<ol>
<li>Password and authentication</li>
<li>Data Integrity</li>
<li>Detecting changes to a table</li>
</ol>
<h3>Passwords and authentication</h3>
<p>If you have a website and you’ve rolled your own authentication system, one thing you do not want to do is store passwords in your database.  By running the password through a hashing algorithm, you can store the hash in your database.  When the user tries to log in, you simply hash the password entered, and compare the two hashes to see if they match.  This means that you can never recover the password – you can only reset the password.</p>
<p>This looks great from an initial glance, but has some drawbacks.  If you’ve used a standard hashing algorithm, and an attack can get a copy of the hashed password, it is possible to compare this hash with a known list of hashed passwords, or generate the hashes by brute force until the generated hash matches the hashed password.  Alternatively, if the attacker has write access to your table, they could insert the hash for a known password onto any account.</p>
<p>A way around this is to “salt” the password.  Salting involves adding extra information to the password before hashing, and this works well as long as the attacker does not know what the extra information is.  You could add the username to the password, or add “abcd!%*(&amp;” to the end of the password, and then hash the result.  As long as you do this every time you generate your hash, a standard set of pre-hashed values will not match your system.  Once the attacker has seen your source code, however, they will know how your hashes have been salted.</p>
<h3>Data integrity</h3>
<p>Data Integrity is another useful feature you can create with hashing.  Imagine that you don’t want your database administrators to be able to update tables via Management Studio – you only want them to update the tables via a specific interface, whether by an encrypted stored procedure, or through your application.  You can create a hash column on your table, and create a hash based on the other columns.  For example, if you had a Student table:</p>
<pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> Student (
      StudentID <span class="kwrd">int</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span> <span class="kwrd">IDENTITY</span> <span class="kwrd">PRIMARY</span> <span class="kwrd">KEY</span>
    , FirstName <span class="kwrd">varchar</span>(25) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , Surname <span class="kwrd">varchar</span>(25) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , DateOfBirth datetime <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , EmergencyContactPhone <span class="kwrd">varchar</span>(15) <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , RowHash <span class="kwrd">varchar</span>(50)
)</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>Now, when you insert your Students, you need to supply a value for the RowHash:</p>
<pre class="csharpcode">INSERT <span class="kwrd">INTO</span> Student (FirstName, Surname, DateOfBirth
    , EmergencyContactPhone, RowHash)
<span class="kwrd">VALUES</span> (<span class="str">'Robert'</span>, <span class="str">'Jackson'</span>, <span class="str">'1995-10-21'</span>, <span class="str">'+61399991234'</span>
    , sys.fn_varbintohexstr(HashBytes(<span class="str">'SHA1'</span>,
        <span class="str">'SALTRobertJackson1995-10-21+61399991234'</span>))
)</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>As you can see, the hash is the word SALT plus the FirstName, Surname, DateOfBirth and EmergencyContactPhone concatenated together.  Note that if you have CONCATE_NULL_YIELDS_NULL turned off, you’ll end up with an empty string if you try to add a NULL value to a string.</p>
<p>The next step is to look at the data.  As we’re not encrypting the data, your DBA can happily look at the rows.  However, in your application, you want to check the hash every time you load in the row to see if it has been modified and the hash not updated correctly.  For example, to retrieve our Robert’s row:</p>
<pre class="csharpcode"><span class="kwrd">SELECT</span>
      FirstName, Surname, DateOfBirth
    , EmergencyContactPhone, RowHash
    , sys.fn_varbintohexstr(HashBytes(<span class="str">'SHA1'</span>, <span class="str">'SALT'</span>
        + FirstName + Surname
        + <span class="kwrd">CONVERT</span>(<span class="kwrd">varchar</span>(10), DateOfBirth, 121)
        + EmergencyContactPhone)) <span class="kwrd">AS</span> GeneratedHash
<span class="kwrd">FROM</span> Student
<span class="kwrd">WHERE</span> StudentID = 1</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>We get the existing RowHash, and a GeneratedHash, which should match.  Note that you would only generate this new hash in an encrypted stored procedure, or in your application, otherwise your DBA can see how the hash is constructed and generate a hash for his changed data.</p>
<p>Let’s update the row without updating the hash, and rerun the select query (click the image for a bigger version):</p>
<pre class="csharpcode"><span class="kwrd">UPDATE</span> Student <span class="kwrd">SET</span> FirstName = <span class="str">'Bob'</span> <span class="kwrd">WHERE</span> StudentID = 1</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2009/04/image.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2009/04/image-thumb.png" border="0" alt="image" width="244" height="65" /></a></p>
<p>Oh dear, the hashes don’t match.  Sound an alarm!</p>
<h3>Detecting changes to a table</h3>
<p>Another use for hashing is to detect changes over a large number of rows.  For example, if you have a Students table that also includes their year level, and you want to check monthly to see if new students have been added to that year, you can either have a Modified Date column to store when rows are modified, keep track of any changes via a trigger, or use a hash function.</p>
<p>Create a separate table to hold the hashed values:</p>
<pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> MonthlyStudentHash (
      SampleDate datetime
    , YearLevel <span class="kwrd">int</span>
    , GroupHash <span class="kwrd">varchar</span>(50)
    , <span class="kwrd">PRIMARY</span> <span class="kwrd">KEY</span> (SampleDate, YearLevel)
)</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>Each month, we can run the following query to get a hashed version of the students:</p>
<pre class="csharpcode">INSERT <span class="kwrd">INTO</span> MonthlyStudentHash
<span class="kwrd">SELECT</span> GETDATE(), YearLevel
    , sys.fn_varbintohexstr(HashBytes(<span class="str">'SHA1'</span>
        , <span class="kwrd">CONVERT</span>(<span class="kwrd">varchar</span>(20), NumStudents * SumDateOfBirth )))
<span class="kwrd">FROM</span> (
    <span class="kwrd">SELECT</span> YearLevel
        , <span class="kwrd">COUNT</span>(*) <span class="kwrd">AS</span> NumStudents
        , <span class="kwrd">SUM</span>(<span class="kwrd">CONVERT</span>(<span class="kwrd">int</span>, DateOfBirth)) <span class="kwrd">AS</span> SumDateOfBirth
    <span class="kwrd">FROM</span> Student
    <span class="kwrd">GROUP</span> <span class="kwrd">BY</span> YearLevel
) years</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
<p>In the inner query (years) we get all the students, and group them by year level, counting the number of students in the YearLevel, and adding together their dates of birth, converted to an integer.  In the outer query, we then multiply the number of students by the SumDateOfBirth and hash this.  No salting is necessary in this case.</p>
<p>The NumStudents * SumDateOfBirth doesn’t make any sense, but it can be whatever you like – the aim is to get a unique value that can be hashed.  (Actually, NumStudents * SumDateOfBirth is likely to be small enough that hashing it will just take up more space, but if you want more accuracy (uniqueness), you can concatenate all the students’ names together and hash that!)</p>
<p> </p>
<p>Now, each month, run this query, and compare the current hash value to last month’s hash value.  It will be easy to see if there’s been a change.  This is a great solution if you have no power to modify the source table to add a ModifiedDate column.</p>
<h3>Conclusion</h3>
<p>There are plenty of other uses for hashed values – this article has only mentioned a few uses.  I’d love to hear any other ideas – please leave a comment!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/ROPaam3QcK8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/04/23/applications-using-hashing-with-sql-server-20052008/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/04/23/applications-using-hashing-with-sql-server-20052008/</feedburner:origLink></item>
		<item>
		<title>Hashing Functions in SQL Server 2005/2008</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/w1md5CEEgdk/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/04/22/hashing-functions-in-sql-server-20052008/#comments</comments>
		<pubDate>Tue, 21 Apr 2009 15:13:29 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/04/22/hashing-functions-in-sql-server-20052008/</guid>
		<description><![CDATA[A hashing function is a useful one-way encryption function that can take a large amount of data and create a compact string that can (mostly) uniquely identify the source data.&#160; For example, this entire post could shrink from being 30 KB to just 30 bytes.&#160; I must stress that the conversion is a one-way operation, [...]]]></description>
			<content:encoded><![CDATA[<p>A hashing function is a useful one-way encryption function that can take a large amount of data and create a compact string that can (mostly) uniquely identify the source data.&#160; For example, this entire post could shrink from being 30 KB to just 30 bytes.&#160; I must stress that the conversion is a one-way operation, and the 30 bytes will NOT contain the 30 KB of text.&#160; </p>
<p>So what is this useful for?&#160; A good example is a table/FILESTREAM containing many 5 MB images.&#160; If you are inserting images into your table, but you only want to insert the image if it does not already exist, how can you tell if the image exists?&#160; You can either check metadata on the image – the filename, the modification date, the file size, etc, or you can check the binary data, byte-by-byte.&#160; Alternatively, you can run the 5 MB through a hashing algorithm, and get a string (about 20 characters long) returned that can (mostly) uniquely identify the data.&#160; This hash can then be indexed to look up the photo quickly.</p>
<p>There are a few different hashing algorithms available in SQL Server 2005: MD2, MD4, MD5, SHA, SHA1, with each having pros and cons.</p>
<p>You’ve probably noticed that I’ve mentioned that hashing can “(mostly) uniquely identify” the original data.&#160; This is because it is possible that collisions can occur, when two source strings produce the same hash value.&#160; If this is the case, you can use the hash value to quickly locate rows in your database that match the hash, and then perform a byte-by-byte comparison to double-check that you haven’t got a collision.&#160; The MDx algorithms may have this weakness, so SHA or SHA1 is recommended.&#160; Performance-wise, I didn’t notice any difference between SHA1 and MD5 over a 1000 character source string.</p>
<p>The HashBytes function used to perform a hash:</p>
<pre class="csharpcode"><span class="kwrd">DECLARE</span> @hash varbinary(<span class="kwrd">max</span>)
<span class="kwrd">SELECT</span> @hash = HashBytes(<span class="str">'SHA1'</span>, REPLICATE(<span class="str">N<span class="str">'</span>abcdefABCDEF12345'</span>, 1000))
<span class="kwrd">SELECT</span> sys.fn_varbintohexstr(@hash)</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>Just a quick explanation: the HashBytes function takes in a varchar or nvarchar value.&#160; I’ve used REPLICATE() to create a string of 17,000 characters.&#160; The sys.fn_varbintohextstr() function then converts the varbinary data into a varchar, which can then be stored in the database (SHA and SHA1 should be 20 characters long).</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/w1md5CEEgdk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/04/22/hashing-functions-in-sql-server-20052008/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/04/22/hashing-functions-in-sql-server-20052008/</feedburner:origLink></item>
		<item>
		<title>Finding The Deprecated Statement</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/vIpbV0SFt48/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2009/04/02/finding-the-deprecated-statement/#comments</comments>
		<pubDate>Thu, 02 Apr 2009 12:00:18 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2009/04/02/finding-the-deprecated-statement/</guid>
		<description><![CDATA[Following on from my previous post regarding using Profiler to find the use of deprecated features, I received an email asking if there was a way to determine what the offending statement was that caused the deprecation warning.
While you could trace every statement on a specific SPID to identify when the warnings are coming through, [...]]]></description>
			<content:encoded><![CDATA[<p>Following on from my <a href="http://www.jimmcleod.net/blog/index.php/2009/02/12/deprecated-features/">previous post</a> regarding using Profiler to find the use of deprecated features, I received an email asking if there was a way to determine what the offending statement was that caused the deprecation warning.</p>
<p>While you could trace every statement on a specific SPID to identify when the warnings are coming through, a much better method is to use the SQLHandle column in the Deprecation Announcement Profiler event.&#160; This gives you a SQL Plan Handle that you can then use the SQL Server 2005 dynamic management&#160; views/functions to look up the plan, including the original SQL text.</p>
<p>First, save the trace file to disk, and then run the following query:</p>
<pre class="csharpcode"><span class="kwrd">SELECT</span> Trace.TextData, SqlText.Text, *
<span class="kwrd">FROM</span> fn_trace_gettable(<span class="str">'c:\work\deprecatedtrace.trc'</span>, 1) <span class="kwrd">AS</span> Trace
<span class="kwrd">OUTER</span> APPLY sys.dm_exec_sql_text(Trace.SqlHandle) <span class="kwrd">AS</span> SqlText
<span class="kwrd">WHERE</span> TextData <span class="kwrd">IS</span> <span class="kwrd">NOT</span> NULL</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>There is a drawback to this method, being that the plan must still be in the procedure cache.&#160; If the server has restarted, or the plan has been removed or expired from the cache, you won’t be able to find the plan.</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/vIpbV0SFt48" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2009/04/02/finding-the-deprecated-statement/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2009/04/02/finding-the-deprecated-statement/</feedburner:origLink></item>
	</channel>
</rss>
