<?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:slash="http://purl.org/rss/1.0/modules/slash/" 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>
	<lastBuildDate>Sun, 10 Jul 2011 22:00:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</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" type="application/rss+xml" href="http://feeds.feedburner.com/Jimmcleodnet" /><feedburner:info uri="jimmcleodnet" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Easily removing repeated lines using Excel</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/oRc8wRNN9dM/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/07/11/easily-removing-repeated-lines-using-excel/#comments</comments>
		<pubDate>Sun, 10 Jul 2011 22:00: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/2011/07/11/easily-removing-repeated-lines-using-excel/</guid>
		<description><![CDATA[Imagine that you’ve just used SQL Server Management Studio to generate a script, and it’s done it’s job wonderfully, except for a lot of unsightly GO and blank lines:
GRANT SELECT ON [Person].[Address] TO [SomeUser];
GO

GRANT INSERT ON [Person].[Address] TO [SomeUser];
GO

GRANT UPDATE ON [Person].[Address] TO [SomeUser];
GO

GRANT DELETE ON [Person].[Address] TO [SomeUser];
GO

.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier [...]]]></description>
			<content:encoded><![CDATA[<p>Imagine that you’ve just used SQL Server Management Studio to generate a script, and it’s done it’s job wonderfully, except for a lot of unsightly GO and blank lines:</p>
<pre class="csharpcode"><span class="kwrd">GRANT</span> <span class="kwrd">SELECT</span> <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GO</span>

<span class="kwrd">GRANT</span> INSERT <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GO</span>

<span class="kwrd">GRANT</span> <span class="kwrd">UPDATE</span> <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GO</span>

<span class="kwrd">GRANT</span> <span class="kwrd">DELETE</span> <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GO</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>Although technically correct, this may offend your sense of aesthetics. If there’s only a couple of rows (as in the excerpt above, you can manually remove the unsightly GOs and blank lines, but this is a bigger ask when you have more than about 20 commands.</p>
<p>The simplest method I’ve come across is to use one of the DBA’s most trusted tools – Excel. Although there are other ways of doing this sort of operation, Excel is installed on almost every Windows machine, and this is a quick operation.</p>
<p>First, copy the entire set of commands, and paste into Excel as Column B, and in column A, add a number for each row (essentially an IDENTITY column). Remember that you can just fill out 1 and 2, select both of these, and drag down to the bottom:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image_thumb.png" width="244" height="242" /></a></p>
<p>This number column is used so that we know which order each row was in originally, because we’re about to sort the data. Select columns A and B for all rows, and sort by column B:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image1.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image_thumb1.png" width="244" height="188" /></a></p>
<p>Next, select the rows you don’t want, and delete them.</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image2.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image_thumb2.png" width="244" height="184" /></a></p>
<p>Now, select the remaining data, and sort again, this time on column A, to return to the original sort order (minus the offending rows):</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image3.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/07/image_thumb3.png" width="244" height="199" /></a></p>
<p>Copy the commands back into Management Studio, and you’re done.</p>
<pre class="csharpcode"><span class="kwrd">GRANT</span> <span class="kwrd">SELECT</span> <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GRANT</span> INSERT <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GRANT</span> <span class="kwrd">UPDATE</span> <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];
<span class="kwrd">GRANT</span> <span class="kwrd">DELETE</span> <span class="kwrd">ON</span> [Person].[Address] <span class="kwrd">TO</span> [SomeUser];</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>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/oRc8wRNN9dM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/07/11/easily-removing-repeated-lines-using-excel/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/07/11/easily-removing-repeated-lines-using-excel/</feedburner:origLink></item>
		<item>
		<title>SQL Server Social Melbourne–May 26, 2011</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/VTqw_9oiVio/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/05/23/sql-server-social-melbournemay-26-2011/#comments</comments>
		<pubDate>Mon, 23 May 2011 13:15:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Community]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/05/23/sql-server-social-melbournemay-26-2011/</guid>
		<description><![CDATA[If there’s one thing that really stands out about SQL Server, it’s the fantastic social community behind the product. If you keep an eye out on SQL blogs and Twitter networks, you can see events running every week across the US and the UK. Australia also has quite a strong SQL Server community, with the [...]]]></description>
			<content:encoded><![CDATA[<p>If there’s one thing that really stands out about SQL Server, it’s the fantastic social community behind the product. If you keep an eye out on SQL blogs and Twitter networks, you can see events running every week across the US and the UK. Australia also has quite a strong <a href="http://www.sqlserver.org.au">SQL Server community</a>, with the Melbourne SQL Server user group exceptionally popular. Although 30 minutes is set aside for socialising, I feel it’s just not enough – I’m such a social butterfly, you see. </p>
<p>To that end, <a href="http://www.lukehayler.com/">Luke Hayler</a> (<a href="https://twitter.com/#!/lukehayler">@lukehayler</a>)and I have organised an evening of social drinks. Come along, meet some other SQL Server professionals based in Melbourne, and discuss anything you like: SQL Server, virtualisation, your dog, or just wax lyrical about the quality of the beer available.</p>
<p>More details, and signup information at <a href="http://sqlserversocial.eventbrite.com/">http://sqlserversocial.eventbrite.com/</a></p>
<p><a href="http://lukehayler.com/2011/05/sql-server-social-1/">Luke’s original post</a></p>
<p><a title="My kind of database cluster! Thanks thomas23" href="http://www.flickr.com/photos/livenow/319170229/"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/05/image.png" width="244" height="188" /></a></p>
<p align="center">That’s my kind of database cluster!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/VTqw_9oiVio" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/05/23/sql-server-social-melbournemay-26-2011/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/05/23/sql-server-social-melbournemay-26-2011/</feedburner:origLink></item>
		<item>
		<title>T-SQL Tuesday #17–APPLY in Compatibility 80</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/uHZ20fre6Cw/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/04/12/t-sql-tuesday-17apply-in-compatibility-80/#comments</comments>
		<pubDate>Tue, 12 Apr 2011 11:15:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[T-SQL Tuesday]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/04/12/t-sql-tuesday-17apply-in-compatibility-80/</guid>
		<description><![CDATA[Welcome to T-SQL Tuesday for April 12, 2011. This month is generously hosted by Matt Velic (Blog &#124; Twitter), who poses the topic of APPLY, one of the many excellent additions to the T-SQL language in SQL Server 2005.
If you read other T-SQL Tuesday posts from today, you&#8217;ll get some excellent tips on how to [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://mattvelic.com/tsql-tuesday-17-invite/"><img style="display: inline; float: right" align="right" src="http://jimmcleod.net/images/tsql2sday.jpg" /></a>Welcome to <a href="http://mattvelic.com/tsql-tuesday-17-invite/">T-SQL Tuesday</a> for April 12, 2011. This month is generously hosted by Matt Velic (<a href="http://mattvelic.com/">Blog</a> | <a href="https://twitter.com/#!/mvelic">Twitter</a>), who poses the topic of APPLY, one of the many excellent additions to the T-SQL language in SQL Server 2005.</p>
<p>If you read other T-SQL Tuesday posts from today, you&#8217;ll get some excellent tips on how to use APPLY, and a list of the excellent things you can do with it. I’m going to go in a slightly different direction, and examine what your options are when using APPLY with database compatibility level 80.</p>
<p>Books Online <a href="http://technet.microsoft.com/en-us/library/ms175156.aspx">states</a> that the database must be at least compatibility level 90 to use APPLY. </p>
<p>It turns out that you can create a query that uses APPLY, and have it run in compatibility level 80. Consider the following setup:</p>
<pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">DATABASE</span> Compat80
<span class="kwrd">GO</span>
<span class="kwrd">ALTER</span> <span class="kwrd">DATABASE</span> Compat80 <span class="kwrd">SET</span> COMPATIBILITY_LEVEL=80
<span class="kwrd">GO</span>

<span class="kwrd">USE</span> Compat80
<span class="kwrd">GO</span>
<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>
<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> A (i <span class="kwrd">int</span>, name <span class="kwrd">varchar</span>(20))
<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> B (i <span class="kwrd">int</span>, amount <span class="kwrd">decimal</span>(15,3))
INSERT <span class="kwrd">INTO</span> A (i, name) <span class="kwrd">VALUES</span> (1, <span class="str">'Jack'</span>)
INSERT <span class="kwrd">INTO</span> A (i, name) <span class="kwrd">VALUES</span> (2, <span class="str">'Bob'</span>)
INSERT <span class="kwrd">INTO</span> A (i, name) <span class="kwrd">VALUES</span> (3, <span class="str">'Sally'</span>)
INSERT <span class="kwrd">INTO</span> B (i, amount) <span class="kwrd">VALUES</span> (1, 25.0)
INSERT <span class="kwrd">INTO</span> B (i, amount) <span class="kwrd">VALUES</span> (1, 50.5)
INSERT <span class="kwrd">INTO</span> B (i, amount) <span class="kwrd">VALUES</span> (2, 16.0)
INSERT <span class="kwrd">INTO</span> B (i, amount) <span class="kwrd">VALUES</span> (3, 110.0)
<span class="kwrd">GO</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>
<h3>Can we use an APPLY in Compatibility 80?</h3>
<p>We’ve created a database in compatibility level 80, and created two tables, A and B. Let’s try a query with CROSS APPLY:</p>
<pre class="csharpcode"><span class="kwrd">SELECT</span> A.Name, dv.Amount <span class="kwrd">FROM</span> A
<span class="kwrd">CROSS</span> APPLY (<span class="kwrd">SELECT</span> i, <span class="kwrd">SUM</span>(Amount) Amount
             <span class="kwrd">FROM</span> B <span class="kwrd">WHERE</span> B.i = A.i <span class="kwrd">GROUP</span> <span class="kwrd">BY</span> i) dv </pre>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image2.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image_thumb2.png" width="138" height="82" /></a><br />
<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>
<p>There’s not much to say here, except that I proved Books Online wrong – I can do a CROSS APPLY in Compatibility 80. I don’t know the exact reason why I can do this, but it’s likely to be the simplistic nature of the query (which makes it less interesting). I’ll do a quick little victory dance, and we’ll move on.</p>
<h3>A more useful use of APPLY</h3>
<p>Where APPLY really shines is when functions are used. Let’s go ahead and create a simple function to test with:</p>
<pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">FUNCTION</span> dbo.fn_getB(@i <span class="kwrd">AS</span> <span class="kwrd">int</span>)
<span class="kwrd">RETURNS</span> @<span class="kwrd">result</span> <span class="kwrd">TABLE</span>
(
      i   <span class="kwrd">int</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
    , amount <span class="kwrd">decimal</span>(15,3)
)
<span class="kwrd">AS</span> <span class="kwrd">BEGIN</span>
        INSERT <span class="kwrd">INTO</span> @<span class="kwrd">result</span>
        <span class="kwrd">SELECT</span> i, amount <span class="kwrd">FROM</span> B <span class="kwrd">WHERE</span> i = @i
        <span class="kwrd">RETURN</span>
<span class="kwrd">END</span>
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>The logic for this function is slightly different than the previous, but it only serves to make it more interesting. Let’s go ahead and use this function in an APPLY, remembering that we’re still in Compatibility 80:</p>
<pre class="csharpcode"><span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> A
<span class="kwrd">CROSS</span> APPLY dbo.fn_getB(A.i) dv </pre>
<p>Msg 102, Level 15, State 1, Line 2<br />
  <br />Incorrect syntax near &#8216;.&#8217;.</p>
<p>That’s disappointing, and rather cryptic. Let’s try again after switching to Compatibility 90:</p>
<pre class="csharpcode"><span class="kwrd">ALTER</span> <span class="kwrd">DATABASE</span> Compat80 <span class="kwrd">SET</span> COMPATIBILITY_LEVEL=90
<span class="kwrd">GO</span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> A
<span class="kwrd">CROSS</span> APPLY dbo.fn_getB(A.i) dv
<span class="kwrd">GO</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><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image3.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image_thumb3.png" width="183" height="101" /></a></p>
<p>Success!</p>
<h3>Getting around this restriction</h3>
<p>We’ve seen that APPLY using functions is a handy tool, but can we still use it in databases that are in Compatibility 80? The following is a workaround, but <strong>it’s a bit of a hack</strong>. I just like thinking outside the box.</p>
<p>The limitation here is that we cannot let a Compatibility 80 database execute the query. We can, however, run the query from within a different database, such as Master (ignoring permission issues) by using sp_executesql.</p>
<pre class="csharpcode"><span class="kwrd">EXEC</span> sp_executesql N<span class="str">'USE Master;
        SELECT * FROM Compat80.dbo.A
        CROSS APPLY Compat80.dbo.fn_getB(A.i) dv '</span>
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>The main reason I’d want to use APPLY is to use the DMFs with the DMVs provided by SQL Server 2005, such as sys.dm_exec_*. However, these DMVs typically supply a database_id column, meaning you can run the query from within Master anyway. With that in mind, I can’t think of any <em>real</em> benefit of this technique – it’s almost always a better option to simply upgrade your databases to compatibility 90 or above – we are in 2011, after all.</p>
<p>Thanks again for this month’s edition of T-SQL Tuesday, Matt!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/uHZ20fre6Cw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/04/12/t-sql-tuesday-17apply-in-compatibility-80/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/04/12/t-sql-tuesday-17apply-in-compatibility-80/</feedburner:origLink></item>
		<item>
		<title>SQL Server 2005 out of mainstream support</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/MndJvpCJhIk/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/04/12/sql-server-2005-out-of-mainstream-support/#comments</comments>
		<pubDate>Tue, 12 Apr 2011 03:30:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/04/12/sql-server-2005-out-of-mainstream-support/</guid>
		<description><![CDATA[It&#8217;s happened. After a long adolescence, SQL Server 2005 has finally grown up and left the nest. Today, April 12, 2011, marks the end-of-life date for mainstream support for SQL Server 2005.
As a parent, Microsoft will no longer have to completely support SQL Server 2005 in every way. MS won&#8217;t have to pick it up [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s happened. After a long adolescence, SQL Server 2005 has finally grown up and left the nest. Today, April 12, 2011, marks the end-of-life date for mainstream support for SQL Server 2005.</p>
<p>As a parent, Microsoft will no longer have to completely support SQL Server 2005 in every way. MS won&#8217;t have to pick it up from Saturday night parties, or drive it to sports on Saturday mornings. (Quiz: Which sport would SQL Server 2005 play? Leave a comment!) While MS may spot SQL 2005 if it needs to catch a taxi home after a big night out, this sort of support will be <a href="http://support.microsoft.com/gp/lifepolicy">fewer and far between</a>.</p>
<p>SQL Server 2005 can now smoke if it chooses to, and if it happens to, it&#8217;s up to MS to decide whether to dissuade it, or let it continue. If you do catch your server smoking, I highly recommend putting out the fire immediately.</p>
<p>I think I&#8217;ve beaten that analogy quite enough. Extended support will continue until 12 April 2016, but it&#8217;s definitely time to consider a upgrade (to SQL Server 2008 R2 or SQL11) as part of your three-year plan. If you&#8217;ve still got SQL Server 2000 databases, I&#8217;d still consider those as a top priority though.</p>
<p>Thanks, SQL Server 2005! It&#8217;s been a pleasure, but it&#8217;s time to fly the coop. Good luck! (And yes, I do realise the irony of wishing it luck at the same time telling it to go away and never come back!) </p>
<p><a href="http://support.microsoft.com/lifecycle/search/?sort=PN&amp;alpha=SQL">(Full list of End Of Life dates for SQL Server)</a></p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/MndJvpCJhIk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/04/12/sql-server-2005-out-of-mainstream-support/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/04/12/sql-server-2005-out-of-mainstream-support/</feedburner:origLink></item>
		<item>
		<title>What’s the difference between a filter on the ON and the WHERE clause?</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/P_9ANQYDiW4/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/04/11/whats-the-difference-between-a-filter-on-the-on-and-the-where-clause/#comments</comments>
		<pubDate>Mon, 11 Apr 2011 12:45:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[Articles]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/04/11/whats-the-difference-between-a-filter-on-the-on-and-the-where-clause/</guid>
		<description><![CDATA[The question is, what is the difference between having a filter on the ON clause, or in the WHERE clause? 
Consider the following queries:
USE TempDB
GO
CREATE TABLE A (i int, name varchar(20))
INSERT INTO A (i, name) VALUES (1, 'Jack'), (2, 'Ryan')
    , (3, 'Simon'), (4, 'Sandra'), (5, 'Daryl')
GO 

CREATE TABLE B (i int, [...]]]></description>
			<content:encoded><![CDATA[<p>The question is, what is the difference between having a filter on the ON clause, or in the WHERE clause? </p>
<p>Consider the following queries:</p>
<pre class="csharpcode"><span class="kwrd">USE</span> TempDB
<span class="kwrd">GO</span>
<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> A (i <span class="kwrd">int</span>, name <span class="kwrd">varchar</span>(20))
INSERT <span class="kwrd">INTO</span> A (i, name) <span class="kwrd">VALUES</span> (1, <span class="str">'Jack'</span>), (2, <span class="str">'Ryan'</span>)
    , (3, <span class="str">'Simon'</span>), (4, <span class="str">'Sandra'</span>), (5, <span class="str">'Daryl'</span>)
<span class="kwrd">GO</span> 

<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> B (i <span class="kwrd">int</span>, amount <span class="kwrd">decimal</span>(9,3))
INSERT <span class="kwrd">INTO</span> B (i, amount) <span class="kwrd">VALUES</span> (1, 100.25), (2, 10000)
    , (1, 0.45), (4, 234.23) 

<span class="rem">-- WHERE</span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> A
<span class="kwrd">INNER</span> <span class="kwrd">JOIN</span> B <span class="kwrd">ON</span> A.i = B.i
<span class="kwrd">WHERE</span> A.Name <span class="kwrd">NOT</span> <span class="kwrd">LIKE</span> <span class="str">'S%'</span> 

<span class="rem">-- ON</span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> A
<span class="kwrd">INNER</span> <span class="kwrd">JOIN</span> B <span class="kwrd">ON</span> A.i = B.i <span class="kwrd">AND</span> A.Name <span class="kwrd">NOT</span> <span class="kwrd">LIKE</span> <span class="str">'S%'</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>Consider these two queries are the bottom. The first uses a “A.Name NOT LIKE ‘S%’” filter in the WHERE clause, whereas the second uses the same filter in the ON clause. The results are identical:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image_thumb.png" width="194" height="167" /></a></p>
<p>Where this gets interesting is when we use an OUTER JOIN. For example, consider the following two queries, which are identical to the previous two, but using a LEFT OUTER JOIN:</p>
<pre class="csharpcode"><span class="rem">-- Query A - WHERE filter </span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> A
<span class="kwrd">LEFT</span> <span class="kwrd">JOIN</span> B <span class="kwrd">ON</span> A.i = B.i
<span class="kwrd">WHERE</span> A.Name <span class="kwrd">NOT</span> <span class="kwrd">LIKE</span> <span class="str">'S%'</span> 

<span class="rem">-- Query B - ON filter </span>
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> A
<span class="kwrd">LEFT</span> <span class="kwrd">JOIN</span> B <span class="kwrd">ON</span> A.i = B.i <span class="kwrd">AND</span> A.Name <span class="kwrd">NOT</span> <span class="kwrd">LIKE</span> <span class="str">'S%'</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>And the results:</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image1.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/04/image_thumb1.png" width="208" height="243" /></a></p>
<p>What’s happened here? The only difference was the placement of the A.Name clause, but the second query (the ON) has added Simon and Sandra’s rows.</p>
<p>What’s happened is due to the ordering of the logical query processing steps. Logically, all JOINs are performed as INNER JOINs using the ON filters, and then, as a subsequent step, any OUTER JOIN rows are added back to the necessary side. After all JOINs are complete is the WHERE filter processed.</p>
<p>This means that in Query A (WHERE), the inner join between A and B was completed, then rows 3 (Simon), and 5 (Daryl) were added back in. Then the WHERE was applied, and Simon and Sandra were removed as their name begins with S.</p>
<p>In Query B (ON), A and B were joined together, but any rows in A with a name LIKE ‘S%’ were not joined, as the ON filter ignored them during the initial JOIN phase. The OUTER JOIN phase then added in all rows from the LEFT that weren’t joined, and so Simon and Sandra were added back in.</p>
<p>Is this a problem? I don’t believe it’s much of an issue, as I tend to think of the WHERE clauses as being distinct from the ON clauses. Just remember to keep your join filters (ON) and your row excluders (WHERE) separate, and understand the difference when necessary.</p>
<p>If you ran the sample code, don’t forget to clean up:</p>
<pre class="csharpcode"><span class="kwrd">DROP</span> <span class="kwrd">TABLE</span> A
<span class="kwrd">DROP</span> <span class="kwrd">TABLE</span> B </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>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/P_9ANQYDiW4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/04/11/whats-the-difference-between-a-filter-on-the-on-and-the-where-clause/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/04/11/whats-the-difference-between-a-filter-on-the-on-and-the-where-clause/</feedburner:origLink></item>
		<item>
		<title>Possible Blocking When Rebuilding Indexes Online</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/tTADtclGo_M/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/03/28/possible-blocking-when-rebuilding-indexes-online/#comments</comments>
		<pubDate>Mon, 28 Mar 2011 10:45:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/03/28/possible-blocking-when-rebuilding-indexes-online/</guid>
		<description><![CDATA[A colleague mentioned he received a warning while building indexes online the other day, so I decided to check it out. A quick search in sys.messages for messages LIKE ‘%online%index%’ found the following message:
Warning: Online index operation on table &#8216;%.*ls&#8217; will proceed but concurrent access to the table may be limited due to residual lock [...]]]></description>
			<content:encoded><![CDATA[<p>A colleague mentioned he received a warning while building indexes online the other day, so I decided to check it out. A quick search in sys.messages for messages LIKE ‘%online%index%’ found the following message:</p>
<p>Warning: Online index operation on table &#8216;%.*ls&#8217; will proceed but concurrent access to the table may be limited due to residual lock on the table from a previous operation in the same transaction.</p>
<p>The error message is fairly clear – if you have a transaction open with existing locks, you’ll get this warning, letting you know that your online index rebuild might not be as online as you think it will be.</p>
<p>As a test, let’s try the following code on AdventureWorks2008:</p>
<pre class="csharpcode"><span class="kwrd">BEGIN</span> <span class="kwrd">TRAN</span>
<span class="kwrd">UPDATE</span> Person.Person <span class="kwrd">SET</span> FirstName = <span class="str">'Kenneth'</span> <span class="kwrd">WHERE</span> BusinessEntityID = 1
<span class="rem">-- Rebuild an index on this table</span>
<span class="kwrd">ALTER</span> <span class="kwrd">INDEX</span> IX_Person_LastName_FirstName_MiddleName <span class="kwrd">ON</span> Person.Person
   REBUILD <span class="kwrd">WITH</span> (ONLINE = <span class="kwrd">ON</span>)
<span class="kwrd">ROLLBACK</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>In this case, we get the warning message due to the five extra locks taken as part of the UPDATE command. While this index is being rebuilt, other sessions trying to access that row will be blocked. This is a good warning.</p>
<p>However, what happens if we try to rebuild an index on a different table? Ideally, I’d want the same sort of warning – if I have any locks that could cause blocking, I’d like to know immediately. The following code will test that:</p>
<pre class="csharpcode"><span class="kwrd">BEGIN</span> <span class="kwrd">TRAN</span>
<span class="kwrd">UPDATE</span> Person.Person <span class="kwrd">SET</span> FirstName = <span class="str">'Kenneth'</span> <span class="kwrd">WHERE</span> BusinessEntityID = 1
<span class="rem">-- Rebuild an index on a completely different table</span>
<span class="kwrd">ALTER</span> <span class="kwrd">INDEX</span> IX_Customer_TerritoryID <span class="kwrd">ON</span> Sales.Customer
   REBUILD <span class="kwrd">WITH</span> (ONLINE = <span class="kwrd">ON</span>)
<span class="kwrd">ROLLBACK</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>The result is that we get no warning. Rebuilding an index on the Sales.Customer table has nothing to do with the Person.Person update we performed, so we miss out of the warning.</p>
<p>This goes against what I’d ideally like. In this case, access to the row modified in Person.Person will result in a block until the index rebuild has finished.</p>
<p>This is not much of an issue though. I can’t think of many situations where I’d want to rebuild an index inside a transaction – it’s the type of task where it’s more likely to be run in a very narrow, constrained transaction.</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/tTADtclGo_M" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/03/28/possible-blocking-when-rebuilding-indexes-online/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/03/28/possible-blocking-when-rebuilding-indexes-online/</feedburner:origLink></item>
		<item>
		<title>Book Review: Code: The Hidden Language of Computer Hardware and Software</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/ps5ngo52uio/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/03/15/book-review-code/#comments</comments>
		<pubDate>Tue, 15 Mar 2011 11:30:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Review]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/03/15/book-review-code/</guid>
		<description><![CDATA[Every couple of years, I pull out a particular book that’s been sitting on my bookshelf for the past decade, read it, and am suddenly refilled with the passion of computers and computing science. This book is Code: The Hidden Language of Computer Hardware and Software, authored by Charles Petzold.
This book is perfect for those [...]]]></description>
			<content:encoded><![CDATA[<p>Every couple of years, I pull out a particular book that’s been sitting on my bookshelf for the past decade, read it, and am suddenly refilled with the passion of computers and computing science. This book is <a href="http://www.charlespetzold.com/code/index.html">Code: The Hidden Language of Computer Hardware and Software</a>, authored by <a href="http://www.charlespetzold.com/">Charles Petzold</a>.</p>
<p>This book is perfect for those who never did an EE or CS degree at university, and want to know exactly how and why computers work. I’m sure a lot of DBAs fall into this category! Petzold takes us on a wonderful journey, on how communication is encoded into our lives, and focussing particularly on digital codes – binary.&#160; Starting with the most simple concepts (communicating simple messages with flashlights and Morse code over short range), new concepts are introduced in simple, logical steps. From the flashlights, electricity is introduced, and a telegraph system is designed.</p>
<p>From Morse code, we are introduced to Braille and to UPC barcodes, all tied together with the theme of binary logic – the information can be coded by a series of ones and zeroes. </p>
<p>The book slowly builds up these concepts in an easy to follow fashion, using telegraph relays to build logic gates, through to a machine that can add two binary numbers, and finally culminates in a design for a theoretical general purpose, programmable computer.</p>
<p>From there, the concept of a transistor is introduced, the Intel 8080 and the Motorola 68000 CPUs are examined in detail, and it’s then a whirlwind of fast logical steps from machine code, to assembly, to higher level languages, operating systems and file systems (with a reasonably detailed look at CP/M). Finally, modern (well, 1999) graphical operating systems are examined, along with how various types of information are stored – graphics formats, sound formats, video, and HTML.</p>
<p>The book doesn’t go into networking protocols (except for a brief couple of paragraphs on modems), but it is easy to extrapolate how computers can talk to one another from the previous concepts.</p>
<p>All along the way, Petzold sprinkles in the history of computing, and the entire story is woven in a very engaging tone. Every time I read this book, I’m reawakened to the beauty and underlying simplicity (<strong>underlying</strong> – there’s nothing simple about 10 million transistor processors!) of computing.</p>
<p>Highly recommended!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/ps5ngo52uio" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/03/15/book-review-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/03/15/book-review-code/</feedburner:origLink></item>
		<item>
		<title>Can We Reproduce Columnstore Aggregations in SQL 2008? (T-SQL Tuesday #016)</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/ynRwa0Etr6I/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/03/08/can-we-reproduce-columnstore-aggregations-in-sql-2008-t-sql-tuesday-016/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 03:15:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[T-SQL Tuesday]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/03/08/can-we-reproduce-columnstore-aggregations-in-sql-2008-t-sql-tuesday-016/</guid>
		<description><![CDATA[The subject of T-SQL Tuesday #016, kindly hosted by Jes Borland (blog &#124; twitter), is Aggregation. Although it’s a fine topic, I could not figure out what I could write about aggregation that would capture my interest, and more importantly, dear reader, yours.
I thought about the topic ceaselessly while doing dishes, feeding children, and cleaning [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/come-one-come-all-to"><img style="display: inline; float: right" align="right" src="http://jimmcleod.net/images/tsql2sday.jpg" /></a>The subject of <a href="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/come-one-come-all-to">T-SQL Tuesday #016</a>, kindly hosted by Jes Borland (<a href="http://blogs.lessthandot.com/index.php/DataMgmt/?author=420">blog</a> | <a href="http://twitter.com/#!/grrl_geek">twitter</a>), is Aggregation. Although it’s a fine topic, I could not figure out what I could write about aggregation that would capture my interest, and more importantly, dear reader, yours.</p>
<p>I thought about the topic ceaselessly while doing dishes, feeding children, and cleaning up the house. I considered aggregate functions, and optional grouping parameters. I twisted the topic inside and out, trying to figure out an interesting angle. I considered telling a story about the Tuples vs the Aggregates on their way to Query Station (and spent an embarrassingly long time on that train of thought). Finally, I went out for a run, thought of a topic pretty quickly and started turning the idea over in my mind for the next 7 km. </p>
<h3>Columnstore Indexes</h3>
<p>A columnstore index is a new feature of Denali, whereby an index is stored vertically – one column per page.&#160; Here is the <a href="http://download.microsoft.com/download/8/C/1/8C1CE06B-DE2F-40D1-9C5C-3EE521C25CE9/Columnstore%20Indexes%20for%20Fast%20DW%20QP%20SQL%20Server%2011.pdf">whitepaper download</a> – highly recommended, and only 5 pages of text. Imagine a row of a typical covering index (in this case, AdventureWork’s Person.Person table, and I’ve covered every column):</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/03/image1.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/03/image_thumb1.png" width="244" height="21" /></a></p>
<p>Every row stored in this particular index is stored on the same page. As each row is quite wide, only 5 rows will fit per 8 KB page.</p>
<p>A columnstore index, however, stores each column on a separate set of pages. I am unsure of the ordering of the rows* but each page is compressed. As the column is likely to contain similar data, this means that each page can have excellent compression ratios – a factor of 4-15 is quoted in the whitepaper – and thousands of rows can fit on a single page.</p>
<p><font size="1">* Due to a lack of internals documentation so far. Are they ordered by the column, or by the clustering key? I can see arguments for either way, but haven’t yet dived deep enough to decide one or the other. </font></p>
<p>This allows for fantastic performance gains when performing aggregates, as fewer pages need to be read to service the query, and only the columns actually required by the query need to be accessed.</p>
<p>Howerver, due to the fact that columnstore indexes are read only, they really only have applications in data warehouses.</p>
<h3>Can we reproduce columnstore indexes in SQL Server 2008? </h3>
<p>The question of whether it is possible to reproduce columnstore indexes in SQL Server 2008 popped into my mind, and the short answer is “No, not a chance”. Sorry.</p>
<p>Let’s look at the properties of a columnstore index.</p>
<p><strong>Read Only</strong> – SQL Server 2008 can certainly build index structures on read-only data. And, as it’s read only, separate aggregation steps can be created in 2008 to summarise data ahead of time. However, one of columnstore’s selling points is to remove the need to aggregate. Columnstore gives you a massive performance benefit with little effort.</p>
<p><strong>Highly Compressed</strong> – SQL Server 2008 can compress indexes. However, to get highly compressible indexes, similar to columnstore’s you’ll only want a single column, which makes the index less useful, and forces you to join frequently.</p>
<p><strong>Only select required columns</strong> – A query accessing a columnstore index will only access those columns that are needed by the query. To get the same sort of functionality in 2008, you would need to either create a covering index for each query you want (nothing like columnstore!), or join multiple indexes together (resulting in a large amount of logical reads).</p>
<p><strong>Different table structure</strong> &#8211; Denali is able to use a completely different page structure, particularly as this data is read-only. This is not possible in SQL Server 2008, and still be usable with T-SQL.</p>
<p><strong>No code changes</strong> &#8211; Denali’s implementation allows us to create a single columnstore index on all columns in the table, and we don’t need to modify any queries to use these columnstore indexes. It’s completely seamless.</p>
<p>I tried to create an example where we could create similar compressed indexes in 2008, but I was never able to merge multiple indexes together to query the data efficiently. Denali has specific optimisations available for columnstore indexes, and these cannot be reproduced. About the best we can do is to create a read-only compressed covering index, which is nowhere&#160; near impressive these days. </p>
<p>After a few hours considering this, my conclusion is that you are better off optimising your data warehouse for those queries that you know are going to come, and ad-hoc queries will need to continue to run slowly, at least until you’ve upgraded to Denali. Frequently run queries will benefit from extra customisation (pre-aggregation, specialised indexes with compression), but there no chance of a similar payoff inside the database engine. </p>
<p>Not too long to wait now, and you’ll be able to take advantage of columnstore indexes!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/ynRwa0Etr6I" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/03/08/can-we-reproduce-columnstore-aggregations-in-sql-2008-t-sql-tuesday-016/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/03/08/can-we-reproduce-columnstore-aggregations-in-sql-2008-t-sql-tuesday-016/</feedburner:origLink></item>
		<item>
		<title>Out of Identity Values – Are There Performance Considerations for Re-seeding?</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/LFY_rqhm3GA/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/03/01/helpout-of-identity-values/#comments</comments>
		<pubDate>Tue, 01 Mar 2011 11:30:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[Articles]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/index.php/2011/03/01/helpout-of-identity-values/</guid>
		<description><![CDATA[It’s finally happened. The table you created five years ago with an clustered integer IDENTITY column is about to hit 231 values (2,147,483,648). Five years ago, you were inexperienced with SQL Server, and had no idea that this table would grow this large &#8211; or that you’d be around to see it!
First, you reseed the [...]]]></description>
			<content:encoded><![CDATA[<p>It’s finally happened. The table you created five years ago with an clustered integer IDENTITY column is about to hit 2<sup>31</sup> values (2,147,483,648). Five years ago, you were inexperienced with SQL Server, and had no idea that this table would grow this large &#8211; or that you’d be around to see it!</p>
<p>First, you reseed the IDENTITY value, back to -2<sup>31</sup>. </p>
<pre class="csharpcode"><span class="kwrd">DBCC</span> CHECKIDENT (LargeTable, RESEED, -2147483647)</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>This should allow you to get another couple of years out of the table, and you will have enough time to implement a better table structure.</p>
<h3>A flash of inspiration</h3>
<p>A few hours later, you’re relaxing after work, quietly pleased about the bullet you’ve dodged today. A thought suddenly strikes you like a lightning bolt. You recall that inserts into a clustered index work best when inserting into an ever increasing value, at the end of the index. Can you&#160; have made a massive error in judgement in forcing all inserts to always happen just before identity value 1? </p>
<p>While you toss and turn about this all night, feel free to consider the issue before reading on. What do you think will happen? Will you have an enormous number of page splits are you constantly run into identity 1? This is obviously a frequently inserted table, given that it has had 2 billion rows added in the past five years (1.1 million per day).</p>
<h3>Testing the hypothesis</h3>
<p>Here’s a nice block of code to test the problem. We create a table with 1000 rows approximately 1000 bytes long, to fit 8 rows per page.</p>
<pre class="csharpcode"><span class="kwrd">CREATE</span> <span class="kwrd">DATABASE</span> IdentityTest
<span class="kwrd">GO</span>
<span class="kwrd">USE</span> IdentityTest
<span class="kwrd">GO</span>

<span class="rem">-- Create a test table, just under 1000 bytes per row. We can fit 8 rows per page.</span>
<span class="kwrd">CREATE</span> <span class="kwrd">TABLE</span> T (i <span class="kwrd">INT</span> <span class="kwrd">IDENTITY</span> <span class="kwrd">PRIMARY</span> <span class="kwrd">KEY</span>, j <span class="kwrd">char</span>(960))
<span class="kwrd">GO</span>

<span class="kwrd">SET</span> NOCOUNT <span class="kwrd">ON</span>

<span class="rem">-- Backup the database so we have a consistent log to examine</span>
<span class="kwrd">ALTER</span> <span class="kwrd">DATABASE</span> IdentityTest <span class="kwrd">SET</span> RECOVERY <span class="kwrd">FULL</span>
<span class="kwrd">BACKUP</span> <span class="kwrd">DATABASE</span> IdentityTest <span class="kwrd">TO</span> <span class="kwrd">DISK</span> = <span class="str">'C:\Work\IdentityTest_Full.bak'</span> <span class="kwrd">WITH</span> INIT
<span class="kwrd">GO</span>

<span class="rem">-- Seed the table with 1000 rows with identity ranges from 1-1000</span>
INSERT <span class="kwrd">INTO</span> T (j) <span class="kwrd">VALUES</span> (<span class="str">'AAAAAAA'</span>)
<span class="kwrd">GO</span> 1000

<span class="rem">-- Reseed back to a large negative number</span>
<span class="kwrd">DBCC</span> CHECKIDENT (T, RESEED, -2147483647)
<span class="kwrd">GO</span>

<span class="rem">-- Add another 100 rows with negative identities counting towards positive</span>
INSERT <span class="kwrd">INTO</span> T (j) <span class="kwrd">VALUES</span> (<span class="str">'BBBBBBB'</span>)
<span class="kwrd">GO</span> 100

<span class="rem">-- Examine the transaction log to see page split operations.</span>
<span class="rem">-- Note the &quot;CheckIdent&quot; Transaction Name around row 1136 where we reseeded.</span>
<span class="kwrd">SELECT</span> [<span class="kwrd">Transaction</span> Name], * <span class="kwrd">FROM</span> fn_dblog(<span class="kwrd">null</span>, <span class="kwrd">null</span>)
<span class="kwrd">WHERE</span> [<span class="kwrd">Transaction</span> Name] <span class="kwrd">IS</span> <span class="kwrd">NOT</span> <span class="kwrd">NULL</span>
<span class="kwrd">ORDER</span> <span class="kwrd">BY</span> [<span class="kwrd">Current</span> LSN]
<span class="kwrd">GO</span>

<span class="rem">-- Cleanup</span>
<span class="kwrd">USE</span> master
<span class="kwrd">GO</span>
<span class="kwrd">DROP</span> <span class="kwrd">DATABASE</span> IdentityTest
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>We can now clearly see the page split pattern in the transaction log. As expected, during inserts prior to the re-seeding, we have a pattern of 8 inserts before a Page Split occurs, signalling that no more rows will fit onto this page.</p>
<p><a href="http://www.jimmcleod.net/blog/wp-content/uploads/2011/03/image.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://www.jimmcleod.net/blog/wp-content/uploads/2011/03/image_thumb.png" width="188" height="244" /></a></p>
<h3>Analysis of the results</h3>
<p>After we run the CheckIdent operation (row 1136), we can see that the next insert operation causes a page split, because we are trying to insert onto the first page of the clustered index. This already has 8 rows on it (IDs 1-8), so ID -2,147,483,646 won’t fit.&#160; The new page ends up with the new row, plus IDs 1, 2, and 3. This leaves room for four more rows to be inserted.</p>
<p>As expected, the fifth insert after the CheckIdent (row 1142) causes another page split. This time, the four negative ID rows are moved to one page, and rows 1,2,3 are on the other page.</p>
<p>So far, the pattern seems bad. We’ve had two splits after inserting five rows, and we get another split on the ninth insert (row 1147). In this case, however, we have not bumped up against the positive IDs. We currently have a single page holding the previous 8 negative ID rows, and the ninth won’t fit at the end, so a new page is allocated with this single row.</p>
<p>Now we’re into a stable state. Every 8 inserts, a new page is allocated, and the inserts never again touch the pages containing the first positive rows.</p>
<h3>Conclusion</h3>
<p>The results of this test are pretty clear – the page splitting algorithms will quickly re-stabilise after re-seeding. You’ll no longer be inserting into the end of the clustered index B-tree, but you will be inserting into a consistent point, which is separated from the start of the values, and so you should not see any performance degradation.</p>
<p>You were stressing for nothing. Relax!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/LFY_rqhm3GA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/03/01/helpout-of-identity-values/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/03/01/helpout-of-identity-values/</feedburner:origLink></item>
		<item>
		<title>How I Study for Microsoft Certification Exams</title>
		<link>http://feedproxy.google.com/~r/Jimmcleodnet/~3/bFogIdUedBo/</link>
		<comments>http://www.jimmcleod.net/blog/index.php/2011/02/15/how-i-study-for-microsoft-certification-exams/#comments</comments>
		<pubDate>Tue, 15 Feb 2011 10:20:00 +0000</pubDate>
		<dc:creator>Jim</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Certification]]></category>

		<guid isPermaLink="false">http://www.jimmcleod.net/blog/?p=210</guid>
		<description><![CDATA[I&#8217;ve done five SQL Server Microsoft Certification exams in the past few years, passing each on the first try, and only using an Exam Preparation book for the first exam. I believe there are two methods of studying for an exam &#8211; either cramming and hoping you retain enough knowledge during the exam, or creating [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve done five SQL Server Microsoft Certification exams in the past few years, passing each on the first try, and only using <a href="http://www.microsoft.com/learning/en/us/book.aspx?ID=9364">an Exam Preparation book</a> for the first exam. I believe there are two methods of studying for an exam &#8211; either cramming and hoping you retain enough knowledge during the exam, or creating a targeted list of topics, and living and breathing the subject until you know it intimately.</p>
<p>This article will talk about the second method, which I believe results in a much deeper understanding of the exam contents.</p>
<h3>Daily Practice </h3>
<p>Use SQL Server daily. This is the most important step. If you are wanting to take an exam on SQL Server, you should be using it daily (alright, you can have weekends off!). The point of this is to be continually using what you&#8217;ve been learning, reinforcing it, and completely internalising it. If you aren&#8217;t using SQL Server, why get certified in it?&#160; </p>
<p>I recommend spending <strong>30-60 minutes each day </strong>using SQL Server in addition to your day job. Create a database in your development environment, and practice the techniques and commands you are learning, and then utilise them in production where appropriate (and approved). Even if you&#8217;re doing routine work and have no current need to use the new features that will be on the exam, you should still aim to practice each day. This study period can be at any time of the day &#8211; whatever works for you. I recommend either during lunch, or before starting work in the morning. Personally, evenings don&#8217;t work for me, as I&#8217;m less alert at the end of the day and can&#8217;t get started until 9:30pm due to family commitments.</p>
<p>If you don&#8217;t have the correct environment at work (for example, you&#8217;re studying for SQL Server 2008 certifications, but you only use 2000 at work), it may be harder to get the necessary practice. In this case, I recommend discussing the options with your boss. It&#8217;s in his interests to get you skilled in the latest version of SQL Server, due to the amount of technical debt growing with older versions of SQL Server. If this is not possible, buy your own copy of SQL Server 2008 Developer Edition, and install it on your own computer (it <a href="http://noelmckinney.com/2010/07/sql-server-on-a-netbook-part-1/">runs fine on a netbook &#8211; just ask Noel McKinney!</a>)</p>
<h3>What&#8217;s Going To Be Examined?</h3>
<p>How do you know which topics will be on the exam? The title of the exam is often not a very good indicator, and goes into no detail, but the exact requirements of the exam are freely available on the Microsoft Learning web site. For example, scan through the list of requirements for the <a href="http://www.microsoft.com/learning/en/us/exam.aspx?ID=70-433#tab2">70-433 exam, available on the “Skills Measured” tab</a>.</p>
<p>You&#8217;ll see that there are seven major sections, with four or five dot-points against each.&#160; This tells you exactly which topics may be included, and as long as you have a good handle on each of these, you&#8217;re good to go!</p>
<p>I like to make a copy of this list, and cut out the topics that I already know well, leaving behind a list of subjects that I believe I need to work on. I then take one of these subjects, and throw myself into learning the topic. </p>
<p>This can be from:</p>
<ul>
<li>reading Books Online</li>
<li>searching for online articles or blog posts on the topic</li>
<li>using one of the Exam Preparation guides</li>
<li>watching videos (e.g. from <a href="http://www.sqlshare.com">www.sqlshare.com</a>)</li>
</ul>
<h3>Reproduce to Reinforce</h3>
<p>When reading material with examples, reproduce each example in your own environment. <strong>Type out</strong> the code each time, run it, and check that it works. </p>
<p>After a period of time (an hour, a day), reproduce the example without looking at the original code. If you get stuck, use Books Online for syntax assistance. </p>
<p>This method of following the examples has two effects. Firstly, you are more likely to retain the syntax if you&#8217;re actively typing it, rather than passively copying and pasting. Secondly, by reproducing the example with minimal assistance, you&#8217;re proving to yourself that you now know the material.</p>
<h3>Apply Your Knowledge</h3>
<p>Finally, consider the pearls of wisdom that you&#8217;re learning, and see if you can apply it back to your own situation at work. If you&#8217;re learning about mirroring, and you currently have a cluster at work, consider the benefits of your current cluster, and contrast these with the benefits of switching to mirroring. You don’t actually have to change to a mirror, but consider how things would be different if you did. How does your DR plan change?</p>
<p>If you&#8217;re studying performance, and you don&#8217;t have a performance baseline for your servers, look into setting one up, and start to monitor performance more closely.</p>
<p>If you have a colleague available (preferably one that already knows this material) take them out for coffee and take them through your reasoning. The act of explaining your thoughts will solidify the concepts in your head, and they can suggest things you might have missed. </p>
<p>If no colleagues are available, you can throw your ideas to the Internet. Find a SQL Server forum, search for related threads, and if none are found, start your own. Or, utilise the #sqlhelp tag on Twitter.</p>
<h3>Rinse and Repeat</h3>
<p>Once you&#8217;ve completed the above steps for a specific topic, go back and choose another topic. Once you&#8217;ve worked your way through the list, you should be ready for the exam. Good luck, and if these tips are helpful, please let me know!</p>
<img src="http://feeds.feedburner.com/~r/Jimmcleodnet/~4/bFogIdUedBo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jimmcleod.net/blog/index.php/2011/02/15/how-i-study-for-microsoft-certification-exams/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jimmcleod.net/blog/index.php/2011/02/15/how-i-study-for-microsoft-certification-exams/</feedburner:origLink></item>
	</channel>
</rss>

