<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>SQL in the Wild</title>
	<atom:link href="http://sqlinthewild.co.za/index.php/feed/" rel="self" type="application/rss+xml" />
	<link>http://sqlinthewild.co.za</link>
	<description>A discussion on SQL Server</description>
	<lastBuildDate>Sun, 09 Jun 2013 21:54:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.2</generator>
		<item>
		<title>2011 Book review</title>
		<link>http://sqlinthewild.co.za/index.php/2012/01/01/2011-book-review/</link>
		<comments>http://sqlinthewild.co.za/index.php/2012/01/01/2011-book-review/#comments</comments>
		<pubDate>Sun, 01 Jan 2012 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1370</guid>
		<description><![CDATA[Another year over and much as I did last year, I&#8217;m going to briefly go over the books I read this last year. I will freely admit, very few of these could be considered &#8216;classic literature&#8217;, most is a mix of sci-fi, fantasy or adventure fiction. That&#8217;s just what I like to read. Book total [...]]]></description>
			<content:encoded><![CDATA[<p>Another year over and much as I did last year, I&#8217;m going to briefly go over the books I read this last year.</p>
<p>I will freely admit, very few of these could be considered &#8216;classic literature&#8217;, most is a mix of sci-fi, fantasy or adventure fiction. That&#8217;s just what I like to read.</p>
<p>Book total this year was 53, up from the 45 I managed in 2010 and above the 50 that I aimed for. Part of this is that I travelled more (and hence had time with nothing to do but read), part is because I took a couple of small vacations (and spent time reading) and part is due to getting an iPad and loading a couple of book apps on there.</p>
<p>The iPad is never going to replace real, physical, paper books for me. I love the smell of new books, the feel of the book (and you can&#8217;t read an iPad in the bath without significant risk). That said, it is convenient when travelling and for carrying a few books easily. It&#8217;s especially nice when getting books from Amazon. 6 week shipping vs immediate delivery. No contest there.</p>
<p>My choice for best books of the year:</p>
<ol>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=jim-butcher&amp;now_reading_title=first-lords-fury-codex-alera">First Lord’s Fury (Codex Alera)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=jim-butcher">Jim Butcher</a>. This is the climax of the Alera series and definitely the best of the bunch. Fast moving, tense, full of action and altogether an excellent ending for an excellent series. One thing I really like about this one: It doesn&#8217;t end with &#8216;happily ever after&#8217;.</li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=brandon-sanderson&amp;now_reading_title=elantris">Elantris</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=brandon-sanderson">Brandon Sanderson</a>. This is a bit of a surprise. I got this on sale without too much in the way of expectations. Not to say I don&#8217;t like Brandon Sanderson, I&#8217;ve enjoyed everything of his that I&#8217;ve read, but this was his first published book and so I was willing to give it a little leeway. Not necessary. Good characters (though I&#8217;m sure I recognise that headstrong princess from a few places), good plot without too many holes and an intriguing mystery that all comes together logically in a way that leaves you saying &#8216;But, of course that&#8217;s the problem&#8217;. Definitely recommend and looking forward to more of his work.</li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack&amp;now_reading_title=star-trek-destiny-gods-of-night">Star Trek: Destiny</a></strong> (trilogy). Yes, I&#8217;m recommending Star Trek novels. The world has not ended. I find most Star Trek novels are quickly churned out, mediocre novels. Average writing, average plotting (at best) and usually a reset button to return the universe to the way it was at the end. This trilogy is none of those. The plot works, it&#8217;s intertwined over three books and about four time-periods and the crew of at least four ships, and it works. It also leaves the universe dramatically changed (in a way that I did not foresee coming). Finally it&#8217;s one of the few time travel tales I&#8217;ve read that doesn&#8217;t leave me cringing.</li>
</ol>
<p>Sooo… books per month.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2012/01/BookList.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="BookList" src="http://sqlinthewild.co.za/wp-content/uploads/2012/01/BookList_thumb.png" alt="BookList" width="480" height="244" border="0" /></a></p>
<p>You can almost see from that which months I was travelling or on holiday. June – trip to UK and a few days at leisure. Oct – trip to Pass and lots of time to read while travelling. Nov – Week away in the middle of nowhere.</p>
<p>Lastly, books per genre. Yes, I read a lot of fantasy. (note, these links go to the library pages on this blog, there&#8217;s a link to the Amazon page from there)</p>
<p>Science Fiction</p>
<ol>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=peter-david&amp;now_reading_title=the-long-night-of-centauri-prime-babylon-5-legions-of-fire-book-1">The Long Night of Centauri Prime (Babylon 5: Legions of Fire, Book 1)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=peter-david">Peter David</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=arthur-c-clarke&amp;now_reading_title=the-light-of-other-days">The Light of Other Days</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=arthur-c-clarke">Arthur C. Clarke</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack&amp;now_reading_title=star-trek-destiny-3-lost-souls">Star Trek: Destiny #3: Lost Souls</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack">David Mack</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack&amp;now_reading_title=star-trek-destiny-2-mere-mortals">Star Trek: Destiny #2: Mere Mortals</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack">David Mack</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=tanya-huff&amp;now_reading_title=a-confederation-of-valor-omnibus">A Confederation of Valor (omnibus)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=tanya-huff">Tanya Huff</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack&amp;now_reading_title=star-trek-destiny-gods-of-night">Star Trek: Destiny #1: Gods of Night</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=david-mack">David Mack</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=earthfall-homecoming">Earthfall (Homecoming)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green&amp;now_reading_title=deathstalker">Deathstalker</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green">Simon R. Green</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=arthur-c-clarke&amp;now_reading_title=childhoods-end-del-rey-impact">Childhood’s End </a></strong>by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=arthur-c-clarke">Arthur C. Clarke</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=arthur-c-clarke&amp;now_reading_title=songs-of-distant-earth">Songs of Distant Earth</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=arthur-c-clarke">Arthur C. Clarke</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=h-g-wells&amp;now_reading_title=the-time-machine-sf-masterworks">The Time Machine (SF Masterworks)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=h-g-wells">H. G. Wells</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=the-call-of-earth">The Call of Earth</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
</ol>
<p>Fantasy</p>
<ol>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=mercedes-lackey-james-mallory&amp;now_reading_title=the-phoenix-transformed-the-enduring-flame">The Phoenix Transformed (The Enduring Flame)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=mercedes-lackey-james-mallory">Mercedes Lackey, James Mallory</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green&amp;now_reading_title=nightingales-lament-nightside-book-3">Nightingale’s Lament (Nightside, Book 3)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green">Simon R. Green</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=brandon-sanderson&amp;now_reading_title=elantris">Elantris</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=brandon-sanderson">Brandon Sanderson</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=stephen-king&amp;now_reading_title=the-gunslinger-the-dark-tower">The Gunslinger (The Dark Tower)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=stephen-king">Stephen King</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green&amp;now_reading_title=agents-of-light-and-darkness-nightside-book-2">Agents of Light and Darkness (Nightside, Book 2)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green">Simon R. Green</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=melanie-rawn&amp;now_reading_title=the-dragon-token-dragon-star-book-2">The Dragon Token (Dragon Star, Book 2)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=melanie-rawn">Melanie Rawn</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green&amp;now_reading_title=something-from-the-nightside-nightside-book-1">Something from the Nightside (Nightside, Book 1)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green">Simon R. Green</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=j-gregory-keyes&amp;now_reading_title=a-calculus-of-angels-the-age-of-unreason-book-2">A Calculus of Angels (The Age of Unreason, Book 2)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=j-gregory-keyes">J. Gregory Keyes</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=melanie-rawn&amp;now_reading_title=stronghold-dragon-star-book-1">Stronghold (Dragon Star, Book 1)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=melanie-rawn">Melanie Rawn</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=the-crystal-city-the-tales-of-alvin-maker-book-6">The Crystal City (The Tales of Alvin Maker, Book 6)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green&amp;now_reading_title=guards-of-haven-the-adventures-of-hawk-and-fisher">Guards of Haven: The Adventures of Hawk and Fisher</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green">Simon R. Green</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan&amp;now_reading_title=the-last-olympian-percy-jackson-and-the-olympians-book-5">The Last Olympian (Percy Jackson and the Olympians, Book 5)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan">Rick Riordan</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan&amp;now_reading_title=the-battle-of-the-labyrinth-percy-jackson-and-the-olympians-book-4">The Battle of the Labyrinth (Percy Jackson and the Olympians, Book 4)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan">Rick Riordan</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan&amp;now_reading_title=the-titans-curse-percy-jackson-and-the-olympians-book-3">The Titan’s Curse (Percy Jackson and the Olympians, Book 3)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan">Rick Riordan</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan&amp;now_reading_title=the-sea-of-monsters-percy-jackson-and-the-olympians-book-2">The Sea Of Monsters (Percy Jackson and the Olympians, Book 2)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan">Rick Riordan</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan&amp;now_reading_title=the-lightning-thief-percy-jackson-and-the-olympians-book-1">The Lightning Thief (Percy Jackson and the Olympians, Book 1)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=rick-riordan">Rick Riordan</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=heartfire-the-tales-of-alvin-maker-book-5">Heartfire (The Tales of Alvin Maker, Book 5)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=alvin-journeyman-tales-of-alvin-maker-book-4">Alvin Journeyman (Tales of Alvin Maker, Book 4)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=prentice-alvin-the-tales-of-alvin-maker-book-3">Prentice Alvin (The Tales of Alvin Maker, Book 3)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=red-prophet-tales-of-alvin-maker-book-2">Red Prophet (Tales of Alvin Maker, Book 2)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card&amp;now_reading_title=seventh-son-tales-of-alvin-maker-book-1">Seventh Son (Tales of Alvin Maker, Book 1)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=orson-scott-card">Orson Scott Card</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=raymond-e-feist&amp;now_reading_title=rides-a-dread-legion-book-one-of-the-demonwar-saga">Rides a Dread Legion: Book One of the Demonwar Saga</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=raymond-e-feist">Raymond E. Feist</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=jim-butcher&amp;now_reading_title=first-lords-fury-codex-alera">First Lord’s Fury (Codex Alera)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=jim-butcher">Jim Butcher</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=steven-brust&amp;now_reading_title=issola-vlad-taltos">Issola (Vlad Taltos)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=steven-brust">Steven Brust</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=jim-butcher&amp;now_reading_title=turn-coat-the-dresden-files-book-11">Turn Coat (The Dresden Files, Book 11)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=jim-butcher">Jim Butcher</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green&amp;now_reading_title=hawk">Hawk </a></strong>by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=simon-r-green">Simon R. Green</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=stephen-r-lawhead&amp;now_reading_title=taliesin">Taliesin</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=stephen-r-lawhead">Stephen R. Lawhead</a></li>
</ol>
<p>Other Fiction</p>
<ol>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum-patrick-larkin&amp;now_reading_title=robert-ludlums-the-lazarus-vendetta-a-covert-one-novel">Robert Ludlum’s The Lazarus Vendetta: A Covert-One Novel</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum-patrick-larkin">Robert Ludlum, Patrick Larkin</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum-gayle-lynds&amp;now_reading_title=robert-ludlums-the-altman-code-a-covert-one-novel">Robert Ludlum’s The Altman Code: A Covert-One Novel</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum-gayle-lynds">Robert Ludlum, Gayle Lynds</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum&amp;now_reading_title=the-bourne-identity-a-novel">The Bourne Identity: A Novel</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum">Robert Ludlum</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=jasper-fforde&amp;now_reading_title=the-eyre-affair-a-thursday-next-novel-thursday-next-novels-penguin-books">The Eyre Affair: A Thursday Next Novel (Thursday Next Novels (Penguin Books))</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=jasper-fforde">Jasper Fforde</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=conn-iggulden&amp;now_reading_title=lords-of-the-bow">Lords of the Bow</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=conn-iggulden">Conn Iggulden</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum-philip-shelby&amp;now_reading_title=robert-ludlums-the-cassandra-compact-a-covert-one-novel">Robert Ludlum’s The Cassandra Compact: A Covert-One Novel</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum-philip-shelby">Robert Ludlum, Philip Shelby</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum&amp;now_reading_title=robert-ludlums-the-hades-factor-a-covert-one-novel">Robert Ludlum’s The Hades Factor: A Covert-One Novel</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=robert-ludlum">Robert Ludlum</a></li>
</ol>
<p>Non-fiction</p>
<ol>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=lee-smolin&amp;now_reading_title=three-roads-to-quantum-gravity">Three Roads to Quantum Gravity</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=lee-smolin">Lee Smolin</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=adam-machanic-hugo-kornelis-lara-rubbelke&amp;now_reading_title=expert-sql-server-2005-development">Expert SQL Server 2005 Development</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=adam-machanic-hugo-kornelis-lara-rubbelke">Adam Machanic, Hugo Kornelis, Lara Rubbelke</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=bill-bryson&amp;now_reading_title=notes-from-a-small-island">Notes From a Small Island</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=bill-bryson">Bill Bryson</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=michael-howard-david-leblanc-john-viega&amp;now_reading_title=19-deadly-sins-of-software-security-programming-flaws-and-how-to-fix-them-security-one-off">19 Deadly Sins of Software Security: Programming Flaws and How to Fix Them (Security One-off)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=michael-howard-david-leblanc-john-viega">Michael Howard, David LeBlanc, John Viega</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=frederick-p-brooks&amp;now_reading_title=the-mythical-man-month-essays-on-software-engineering-anniversary-edition-2nd-edition">The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=frederick-p-brooks">Frederick P. Brooks</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=joseph-silk&amp;now_reading_title=on-the-shores-of-the-unknown-a-short-history-of-the-universe">On the Shores of the Unknown: A Short History of the Universe</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=joseph-silk">Joseph Silk</a></li>
<li><strong><a href="http://sqlinthewild.co.za/index.php?now_reading_author=chuck-pfarrer&amp;now_reading_title=warrior-soul-the-memoir-of-a-navy-seal">Warrior Soul: The Memoir of a Navy Seal</a></strong> by <a href="http://sqlinthewild.co.za/index.php?now_reading_author=chuck-pfarrer">Chuck Pfarrer</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2012/01/01/2011-book-review/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>SQL University: Advanced Indexing &#8211; Indexing Strategies</title>
		<link>http://sqlinthewild.co.za/index.php/2011/11/11/sql-university-advanced-indexing-indexing-strategies/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/11/11/sql-university-advanced-indexing-indexing-strategies/#comments</comments>
		<pubDate>Fri, 11 Nov 2011 15:00:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Indexes]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1325</guid>
		<description><![CDATA[Right, I know it&#8217;s Friday and everyone&#8217;s tired and looking forward to the weekend, but I do need to finish off this indexing section and I&#8217;ll try to keep this short and interesting and hopefully keep everyone awake. There&#8217;s no shortage of information available on how to create indexes. Hell, I&#8217;ve written a copious amount [...]]]></description>
			<content:encoded><![CDATA[<p>Right, I know it&#8217;s Friday and everyone&#8217;s tired and looking forward to the weekend, but I do need to finish off this indexing section and I&#8217;ll try to keep this short and interesting and hopefully keep everyone awake.</p>
<p>There&#8217;s no shortage of information available on how to create indexes. Hell, I&#8217;ve written a copious amount myself. Most of these many articles however are written from the point of indexing single queries. What you chose for a where clause, what has to go into the include to create the perfect index for this query. Now that&#8217;s all well and good, but I&#8217;ve never met a system that had only one query per table (maybe there is such a system out there, but I&#8217;ve never found it)</p>
<p>So what I&#8217;m going to try to do today is address the topic of a strategy for indexing. How to approach indexing, not for a single query, but for the system as a whole. I won&#8217;t be able to cover this in-depth, this is material worthy of an entire book chapter, if not an entire book, but I can at least touch on the essential portions.</p>
<p>Now, there&#8217;s two main positions that we could be in when considering indexing strategies for an entire system<br />
1) A brand new system that&#8217;s still in development<br />
2) An existing system that&#8217;s being used actively.</p>
<p>One at a time&#8230;</p>
<h3>Indexing strategies for a brand new system</h3>
<p>Start by choosing a good clustered index. What makes a good clustered index? Well, it depends <img src='http://sqlinthewild.co.za/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<ul>
<li><a title="http://www.scarydba.com/2011/04/04/sql-universityrecommendations-for-a-clustered-index/" href="http://www.scarydba.com/2011/04/04/sql-universityrecommendations-for-a-clustered-index/">http://www.scarydba.com/2011/04/04/sql-universityrecommendations-for-a-clustered-index/</a></li>
<li><a title="http://www.sqlskills.com/BLOGS/KIMBERLY/post/GUIDs-as-PRIMARY-KEYs-andor-the-clustering-key.aspx" href="http://www.sqlskills.com/BLOGS/KIMBERLY/post/GUIDs-as-PRIMARY-KEYs-andor-the-clustering-key.aspx">http://www.sqlskills.com/BLOGS/KIMBERLY/post/GUIDs-as-PRIMARY-KEYs-andor-the-clustering-key.aspx</a></li>
<li><a title="http://www.sqlskills.com/BLOGS/KIMBERLY/post/The-Clustered-Index-Debate-Continues.aspx" href="http://www.sqlskills.com/BLOGS/KIMBERLY/post/The-Clustered-Index-Debate-Continues.aspx">http://www.sqlskills.com/BLOGS/KIMBERLY/post/The-Clustered-Index-Debate-Continues.aspx</a></li>
<li><a title="http://www.sqlskills.com/BLOGS/KIMBERLY/post/Ever-increasing-clustering-key-the-Clustered-Index-Debateagain!.aspx" href="http://www.sqlskills.com/BLOGS/KIMBERLY/post/Ever-increasing-clustering-key-the-Clustered-Index-Debateagain!.aspx">http://www.sqlskills.com/BLOGS/KIMBERLY/post/Ever-increasing-clustering-key-the-Clustered-Index-Debateagain!.aspx</a></li>
<li><a title="http://www.sqlskills.com/BLOGS/KIMBERLY/post/The-Clustered-Index-Debate-again!.aspx" href="http://www.sqlskills.com/BLOGS/KIMBERLY/post/The-Clustered-Index-Debate-again!.aspx">http://www.sqlskills.com/BLOGS/KIMBERLY/post/The-Clustered-Index-Debate-again!.aspx</a></li>
<li><a title="http://www.sqlservercentral.com/articles/Indexing/68563/" href="http://www.sqlservercentral.com/articles/Indexing/68563/">http://www.sqlservercentral.com/articles/Indexing/68563/</a></li>
<li><a title="http://technet.microsoft.com/en-us/sqlserver/gg508879.aspx" href="http://technet.microsoft.com/en-us/sqlserver/gg508879.aspx">http://technet.microsoft.com/en-us/sqlserver/gg508879.aspx</a> (video)</li>
</ul>
<p>The clustered index is the base, it will affect each and every nonclustered index, and it&#8217;s not trivial to change once the system is in use, so chose carefully. I&#8217;m not saying another word on the subject of a clustered index, not today.</p>
<p>Once that&#8217;s done…</p>
<p><span id="more-1325"></span></p>
<p>Design any unique constraints, unique indexes or primary key constraints that are required by the database design. If the primary key should go on the column(s) that were chosen as the clustering key, great, the primary key gets created clustered. If not, then the clustered index goes on the column(s) chosen for the clustered index and the primary key gets created as nonclustered.</p>
<p>Index the foreign keys. These may end up not being the final indexes on these columns, but it&#8217;s an excellent place to start and it&#8217;s something that&#8217;s left out far, far too often.</p>
<p>That&#8217;s the absolute bare minimum that must be done. That can all be done with just the database design. The rest is going to require some knowledge of the queries that will be running against the server.</p>
<p>Speak to the developers, see what queries they&#8217;re going to be sending to the database, speak to the business analysts (or users) and see what think will be the commonly used aspects. Bear in mind that both the developers and business analysts (or users) may well be wrong. Not intentionally wrong, but wrong because they&#8217;re looking at things from a different perspective.</p>
<p>To give an example of that, a system I worked on some time back had custom security built-in to the DB (tables storing access rights to various sets of data). The users swore that the most accessed portion of the system was the address book. The developers claimed that the account balances procedure would have the heaviest impact. A trace showed that the custom security ran far more frequently than anything else.</p>
<p>Hence, if you can trace a workload (from automated testing, user testing or acceptance testing is the best) you should. Combine that with what the developers and business analysts say and that should be reasonably accurate.</p>
<p>Find the most critical queries, the ones that are going to run often. These may be &#8216;housekeeping&#8217; queries like the custom security, or maybe they&#8217;ll be queries run when the user opens the app. It&#8217;s going to differ for everyone.</p>
<p>Create a minimal set of indexes to support the most critical, most frequent queries. Do not, at this point, try to index everything. It&#8217;s going to be a waste of time without accurate stats of how the users really use the system. Create just a minimum of indexes to start with. You want enough so that the system runs acceptably but not so many that you&#8217;ll be cleaning unused indexes off for months after the system goes live.</p>
<p>I&#8217;m not going to go into detail here on how to create indexes, see all the links that I gave in the <a href="http://sqlinthewild.co.za/index.php/2011/11/07/sql-university-advanced-indexing-sorting-and-grouping/">first part of this series</a>. There are, however, a few things to keep in mind</p>
<ul>
<li>Fewer, wide indexes are better in general than lots of narrow indexes (<a title="http://sqlinthewild.co.za/index.php/2010/09/14/one-wide-index-or-multiple-narrow-indexes/" href="http://sqlinthewild.co.za/index.php/2010/09/14/one-wide-index-or-multiple-narrow-indexes/">http://sqlinthewild.co.za/index.php/2010/09/14/one-wide-index-or-multiple-narrow-indexes/</a>)</li>
<li>Selectivity should not be the first thing you consider when choosing column order for indexes, especially indexes that are going to support multiple queries. (<a title="http://sqlinthewild.co.za/index.php/2009/01/19/index-columns-selectivity-and-equality-predicates/" href="http://sqlinthewild.co.za/index.php/2009/01/19/index-columns-selectivity-and-equality-predicates/">http://sqlinthewild.co.za/index.php/2009/01/19/index-columns-selectivity-and-equality-predicates/</a> and <a title="http://sqlinthewild.co.za/index.php/2009/02/06/index-columns-selectivity-and-inequality-predicates/" href="http://sqlinthewild.co.za/index.php/2009/02/06/index-columns-selectivity-and-inequality-predicates/">http://sqlinthewild.co.za/index.php/2009/02/06/index-columns-selectivity-and-inequality-predicates/</a>)</li>
</ul>
<p>Once done, come back to the indexes after the system is in use and re-evaluate.</p>
<h3>Indexing strategies for an established system</h3>
<p>This one depends heavily on how badly the system is indexed.</p>
<p>If tables are missing clustered indexes or primary keys that should be the first priority. This is harder than for a new system as, in the absence of enforced constraints, duplicate values could have crept into the supposedly unique columns.</p>
<p>If primary keys can&#8217;t easily be added (due to errant data), the clustered indexes should still be considered. All the same considerations as for a new system apply here. It&#8217;s worth noting that adding clustered indexes to huge tables is not a quick exercise.</p>
<p>There are two factors to fixing the indexing for an existing system</p>
<ul>
<li>creating or widening indexes to support the current workload</li>
<li>removing indexes that are not used</li>
</ul>
<p><span style="font-weight: bold;">Creating or widening indexes</span></p>
<p>This should be done based on the workload. Either SQLTrace or Extended Events can be used to capture the queries running against the server. This can be examined manually or it can be submitted to the Database Tuning Adviser (DTA).</p>
<p>If using DTA, that must not be the entire story. DTA recommendations have to be carefully examined and tested and implemented only if they make sense (and improve performance). Be wary of accidentally creating redundant indexes this way (<a href="http://www.sqlskills.com/BLOGS/KIMBERLY/post/UnderstandingDuplicateIndexes.aspx">How can you tell if an index is REALLY a duplicate?</a>)</p>
<p>If manually tuning, the same cautions as mentioned in the &#8216;Indexing a new system&#8217; apply.</p>
<p>Also, be aware that you can&#8217;t create perfect indexes for all queries (except maybe in a data warehouse, but likely not even then). Index for the important (frequent or high priority) queries, create indexes that can support multiple queries. Tune the system, not the individual query. See <a href="http://www.sqlskills.com/BLOGS/KIMBERLY/post/Indexes_JustBecauseUCan_NO.aspx">Indexes: just because you can, doesn&#8217;t mean you should!</a> (Kimberly Tripp).</p>
<p>If the less important, less frequently run queries aren&#8217;t quite as optimal as they could be (but are still in the acceptable range), that&#8217;s fine. Be very careful of over-indexing.</p>
<p><strong>Removing indexes</strong></p>
<p>To be honest, this is fraught with peril. Telling that an index is unused is not as easy as it may seem.</p>
<p>Start with the sys.dm_db_index_usage_stats DMV. Indexes that have no seeks, no scans and just updates, or ones that don&#8217;t appear in there at all may appear to be unused. Whether they are really unused however is another question.</p>
<p>The sys.dm_db_index_usage_stats DMV is cleared by a restart of SQL or any time the database is closed (beware auto_close). So if the SQL instance has only been running for three days, then the best that can be said about indexes that appear in sys.dm_db_index_usage_stats with no seeks and no scans is that they haven&#8217;t been used in three days.</p>
<p>Before deciding to drop any indexes monitor that DMV over a period of time. How long? Depends on your application. If it&#8217;s an app that has a steady and consistent usage, maybe not long. If it&#8217;s an app that has radically different usage patterns at different times of the month/year, then long enough that you capture them all.</p>
<p>Also, make sure you keep documentation and preferably scripts of any indexes dropped, so that you can easily recreate them should it be necessary.</p>
<h3>In conclusion</h3>
<p>Comprehensive indexing strategies are not exactly easy things to write short posts on, but I hope this has given some idea on how to fit all the pieces together. For another view on this topic, be sure to watch Kimberly&#8217;s video on indexing strategies: <a href="http://technet.microsoft.com/en-us/sqlserver/gg545006.aspx">http://technet.microsoft.com/en-us/sqlserver/gg545006.aspx</a></p>
<p>Oh, I almost forgot the answer to Wednesday&#8217;s homework (for anyone that&#8217;s still awake). There are likely many answers, but this one satisfies the requirements:</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_HomeworkAnswer
ON dbo.CallLog (Severity, LastUpdateDate, CallStatus)
INCLUDE (AssignedTo, LogDate)
WHERE LastUpdateDate IS NOT NULL AND Severity IN (1,2)</pre>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/11/11/sql-university-advanced-indexing-indexing-strategies/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>SQL University: Advanced Indexing &#8211; Filtered Indexes</title>
		<link>http://sqlinthewild.co.za/index.php/2011/11/09/sql-university-advanced-indexing-filtered-indexes-2/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/11/09/sql-university-advanced-indexing-filtered-indexes-2/#comments</comments>
		<pubDate>Wed, 09 Nov 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Indexes]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1351</guid>
		<description><![CDATA[Welcome back to day 2 of Advanced Indexing. Today we&#8217;re going to look at a feature that was added in SQL Server 2008 &#8211; filtered indexes. In versions previous, indexes were always on the entire table. An index would always have the same number of rows as the table it was built on did (which [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome back to day 2 of Advanced Indexing. Today we&#8217;re going to look at a feature that was added in SQL Server 2008 &#8211; filtered indexes.</p>
<p>In versions previous, indexes were always on the entire table. An index would always have the same number of rows as the table it was built on did (which is why COUNT(*) can just scan the smallest index on the table)</p>
<p>With filtered indexes, it&#8217;s possible to have an index that&#8217;s built on a subset of the rows in the table. The definition for a filtered index contains a WHERE clause predicate that determines if a row in the table will be in the index or not.</p>
<p>This can be a major advantage on really large tables where most queries are only interested in a small fraction of the table. A normal index would be based on the entire table regardless of the fact that most of the table is of no interest, meaning the index would be larger than necessary, deeper than necessary and take up more space than would be ideal. With a filtered index on just the interesting portion of the table, the index size is kept to a minimum, meaning it&#8217;s shallower than an index on the entire table and hence more efficient.</p>
<p>A simple example of a filtered index would be</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_Example
ON Account (AccountNumber)
WHERE Active = 1;</pre>
<p>There are two main uses of a filtered index:<br />
1) Enforcing moderately complex uniqueness requirements<br />
2) Supporting queries that filter on common subsets of a table</p>
<h3>Filtered indexes and unique columns</h3>
<p>One very interesting use of filtered indexes is in enforcing uniqueness over portions of a table. One requirement that come up again and again is to have a nullable column that must have unique entries in it, but whose entries are optional. Basically, the column must be unique or null. Sounds easy, but the problem is that a unique index allows only one null. So much for nulls not being equal to anything including other nulls.</p>
<p>Prior to SQL 2008 implementing such a constraint required computed columns, indexed views or triggers. With SQL 2008&#8242;s filtered indexes, it&#8217;s trivial.</p>
<p><span id="more-1351"></span></p>
<pre class="brush: sql; title: ; notranslate">CREATE UNIQUE NONCLUSTERED INDEX idx_SomeTable_SomeColumn
ON SomeTable (SomeColumn)
WHERE SomeColumn IS NOT NULL;</pre>
<p>It has to be a unique index not a unique constraint as indexes can be filtered, constraints cannot.</p>
<p>This can be extended to various forms of moderately complex unique requirements and is certainly an improvement over using indexed views or complex calculated columns (or just trusting the application to do things right).</p>
<h3>Supporting queries</h3>
<p>The really interesting use of filtered indexes though is for supporting queries. Here filtered indexes are very useful in cases where a queries against a table frequently include a specific filter.</p>
<p>A couple common cases of this are table that flag rows as active or inactive and most queries are interested in only the active rows, or in a database design where deletes are logical (an IsDeleted column) and almost every query filters for rows not marked as deleted.</p>
<p>Let&#8217;s have a look at a couple examples here. I&#8217;m not using AdventureWorks because the database design doesn&#8217;t include these kinds of patterns. The table design is given at the end of the post and a SQLDataGenerator project file is attached.</p>
<p>First let&#8217;s look at a simple example. This table stores support calls, and this query is looking for all recent open calls logged to one of the support people.</p>
<pre class="brush: sql; title: ; notranslate">SELECT CallID, LogDate, AssignedTo
FROM dbo.CallLog AS cl
WHERE CallStatus = 'Open'
AND AssignedTo = 42
AND LogDate &gt; DATEADD(ww,-1,GETDATE());</pre>
<p>Now, we could create a normal nonclustered index with CallStatus, AssignedTo and LogDate in the key, but let&#8217;s say that while the AssignedTo and LogDate filters change for this query, the filter is always, always, always for CallStatus = &#8216;Open&#8217;. This table has around 200 open calls and 100000 closed calls. Creating an index with the closed calls as well is just wasting space and time, no one&#8217;s interested. So, what I can do is this:</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_CallLog_AssignedToLogDate
ON dbo.CallLog (AssignedTo, LogDate)
WHERE CallStatus = 'Open';</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="FilteredIndex1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex1_thumb.png" alt="FilteredIndex1" width="364" height="120" border="0" /></a></p>
<p>One thing to note here is that if the query filter exactly matches the index filter, the index doesn&#8217;t need to have that column as either a key or include column. It&#8217;s not being selected and the filter is entirely taken care of with the indexes filter.</p>
<p>It&#8217;s worth noting that there&#8217;s bug relating to this, specifically around filtered indexes with an IS NULL filter. See <a href="http://connect.microsoft.com/SQLServer/feedback/details/454744/filtered-index-not-used-and-key-lookup-with-no-output/" target="_blank">http://connect.microsoft.com/SQLServer/feedback/details/454744/filtered-index-not-used-and-key-lookup-with-no-output/</a> The bug is still unfixed in the latest CTP of SQL Server 2012.</p>
<p>Let&#8217;s have a look at a second example, where the query&#8217;s filter is a subset of the index&#8217;s filter.</p>
<p>Let&#8217;s say that a very frequent query is for the urgent or urgent and high priority calls (severity 1 and 2). So these are two common queries:</p>
<pre class="brush: sql; title: ; notranslate">SELECT CallID, LogDate, CallStatus, Severity
FROM dbo.CallLog AS cl
WHERE Severity &lt; 3 -- urgent and high
AND AssignedTo = 1;

SELECT CallID, LogDate, CallStatus, Severity
FROM dbo.CallLog AS cl
WHERE Severity = 1  -- urgent
AND AssignedTo = 1;</pre>
<p>So, given that, I can create a filtered index on the larger of those ranges</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_CallLog_Severity
ON dbo.CallLog (AssignedTo,    Severity)
INCLUDE (CallStatus, LogDate)
WHERE Severity &lt; 3</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="FilteredIndex2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex2_thumb.png" alt="FilteredIndex2" width="364" height="206" border="0" /></a></p>
<p>In this case the filtered index can be used, but the column that&#8217;s being filtered on must also be in the index key, because the second query is filtering for a subset of the rows that the index contains.</p>
<p>An examination of the properties of the index seeks shows that for the first query there&#8217;s only one seek predicate – AssignedTo, whereas for the second query there are two seek predicates – AssignedTo and Severity</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex3.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="FilteredIndex3" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex3_thumb.png" alt="FilteredIndex3" width="204" height="314" border="0" /></a> <a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex4.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="FilteredIndex4" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex4_thumb.png" alt="FilteredIndex4" width="194" height="314" border="0" /></a></p>
<p>There are some limitations around filtered indexes and the matching of the filters. There are cases where a query and a filtered index have predicates that are logically equivalent, but where the filtered index can&#8217;t be used.</p>
<p>An example of this is not hard to generate. Let&#8217;s try a table that has an IsDeleted bit column (defined as not nullable)</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE Users (
UserID INT IDENTITY PRIMARY KEY,
UserName VARCHAR(50),
DepartmentID INT NOT NULL,
IsDeleted BIT NOT NULL DEFAULT 0
);

CREATE NONCLUSTERED INDEX idx_Users_DepartmentID
ON dbo.Users (DepartmentID)
INCLUDE (UserName)
WHERE IsDeleted = 0;</pre>
<p>The IsDeleted column is a non-nullable bit column. Hence it can only have two possible values, 0 and 1. Hence, these two queries are completely equivalent in their results</p>
<pre class="brush: sql; title: ; notranslate">SELECT UserName, DepartmentID
FROM dbo.Users
WHERE IsDeleted = 0 AND DepartmentID = 3;

SELECT UserName, DepartmentID
FROM dbo.Users
WHERE IsDeleted != 1 AND DepartmentID = 3;</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex71.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="FilteredIndex7" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex7_thumb.png" alt="FilteredIndex7" width="364" height="157" border="0" /></a></p>
<p>The first one uses the filtered index, the second does not. The second one scans the clustered index because despite the filter essentially being the same as the index filter, it’s not the same and the query hence cannot use the filtered index.</p>
<p>Another limitation has to do with parametrisation. If the query is passed in a parametrised form, or is subject to simple or forced parametrisation, then by the time that the optimiser gets the query there may not be sufficient information to tell that a filtered index is usable or not.</p>
<p>If we imagine the case of the table with the IsDeleted column again, a query that has a filter IsDeleted = 0 is definitely capable of using a filtered index that has the predicate IsDeleted = 0, but if the query gets parametrised and arrives at the optimiser in the form IsDeleted = @p1, there&#8217;s no way that query can match the filtered index because the value of @p1 on future executions could be 0, 1, NULL or 42, and in any case other than 0, if the cached plan used the filtered index it would produce incorrect results.</p>
<p>We can see this by setting the database parametrisation to forced and re-running an earlier example.</p>
<p>With parametrisation simple and the index with a filter on severity &lt; 3, these two queries produce different execution plans</p>
<pre class="brush: sql; title: ; notranslate">SELECT CallID, LogDate, CallStatus, Severity
FROM dbo.CallLog AS cl
WHERE Severity = 1
AND AssignedTo = 1;

SELECT CallID, LogDate, CallStatus, Severity
FROM dbo.CallLog AS cl
WHERE Severity = 4
AND AssignedTo = 1;</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex5.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="FilteredIndex5" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex5_thumb.png" alt="FilteredIndex5" width="364" height="207" border="0" /></a></p>
<p>However if the database is set for forced parameterisation, then the query is only seen in its parametrised form, and both queries have the same plan, one that does not use the filtered index</p>
<pre class="brush: sql; title: ; notranslate">ALTER DATABASE Testing SET PARAMETERIZATION FORCED;
GO

SELECT CallID, LogDate, CallStatus, Severity
FROM dbo.CallLog AS cl
WHERE Severity = 1
AND AssignedTo = 1;

SELECT CallID, LogDate, CallStatus, Severity
FROM dbo.CallLog AS cl
WHERE Severity = 4
AND AssignedTo = 1;</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex6.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="FilteredIndex6" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndex6_thumb.png" alt="FilteredIndex6" width="364" height="192" border="0" /></a></p>
<p>Right, so, homework for today. Given the table design below and the following queries, design one filtered index that both queries can use effectively (a filtered index that doesn’t filter out any rows is not an acceptable answer). Assume those are fixed queries that are frequently run with exactly that structure and exactly those values.</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE CallLog (
CallID INT IDENTITY PRIMARY KEY,
CallStatus CHAR(6) NOT NULL,
LogDate DATETIME NOT NULL,
LastUpdateDate DATETIME,
Title VARCHAR(500),
Severity TINYINT,
AssignedTo INT,
UserID INT
);

SELECT CallID, CallStatus, AssignedTo
FROM dbo.CallLog
WHERE CallStatus = 'Open' AND LastUpdateDate IS NOT NULL AND Severity = 1;

SELECT CallID, LogDate, LastUpdateDate FROM dbo.CallLog
WHERE LastUpdateDate &lt; DATEADD(dd,7,GETDATE()) AND Severity IN (1,2);</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/FilteredIndexes.zip">FilteredIndexes</a> (SQL DataGenerator project)</p>
<p>Answers for Monday&#8217;s homework:</p>
<p>1) Yes, this can use an index for both filter and group by. Index key columns would be (TransactionType, ReferenceOrderID, ProductID), in that order, and index include columns would be (Quantity, ActualCost)</p>
<p>2) No, because of the inequality we can use an index to support filtering or aggregating but not both. So there will either be a plan with an index seek and a hash aggregate (or sort and stream aggregate) or a plan with an index scan and a stream aggregate, but there&#8217;s no way to get a seek and stream aggregate without a sort</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/11/09/sql-university-advanced-indexing-filtered-indexes-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>SQL University: Advanced indexing &#8211; Sorting and Grouping</title>
		<link>http://sqlinthewild.co.za/index.php/2011/11/07/sql-university-advanced-indexing-sorting-and-grouping/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/11/07/sql-university-advanced-indexing-sorting-and-grouping/#comments</comments>
		<pubDate>Mon, 07 Nov 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Indexes]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1321</guid>
		<description><![CDATA[Good day everyone and welcome to another week of SQL University. I know we’re getting close to the end of the year and everyone’s looking forward to a nice long vacation soaking up the sun at the beach, but a little bit of attention would be nice. Thank you. This week is Advanced Indexing, and [...]]]></description>
			<content:encoded><![CDATA[<p>Good day everyone and welcome to another week of SQL University. I know we’re getting close to the end of the year and everyone’s looking forward to a nice long vacation soaking up the sun at the beach, but a little bit of attention would be nice. Thank you.</p>
<p>This week is Advanced Indexing, and I mean advanced, none of that selectivity, SARGable, predicate stuff that gets repeated all over the place. If you need a refresher on the basics before we get started, the following can be considered pre-requisite reading for this course</p>
<ul>
<li><a href="http://www.scarydba.com/2010/07/19/sql-university-indexes-part-the-first/">Introduction to Indexes, Part the First</a></li>
<li><a href="http://www.scarydba.com/2010/07/21/sql-university-introduction-to-indexes-part-the-second/">Introduction to Indexes, Part the Second</a></li>
<li><a href="http://www.scarydba.com/2010/07/23/sql-university-introduction-to-indexes-part-the-third/">Introduction to Indexes, Part the Third</a></li>
<li><a href="http://www.scarydba.com/2011/04/04/sql-universityrecommendations-for-a-clustered-index/">Recommendations for a Clustered Index</a></li>
<li><a href="http://www.scarydba.com/2011/04/06/sql-university-index-usage/">Index Usage</a></li>
</ul>
<p>There’s also some additional background material available for more enthusiastic students:</p>
<ul>
<li><a href="http://www.sqlservercentral.com/articles/Indexing/68439/">Introduction to Indexes</a></li>
<li><a href="http://www.sqlservercentral.com/articles/Indexing/68563/">Introduction to Indexes: Part 2 – The clustered index</a></li>
<li><a href="http://www.sqlservercentral.com/articles/Indexing/68636/">Introduction to Indexes: Part 3 – The nonclustered index</a></li>
<li><a href="http://technet.microsoft.com/en-us/sqlserver/gg508878.aspx">Index Internals</a> (Video)</li>
<li><a href="http://technet.microsoft.com/en-us/sqlserver/gg508879.aspx">The Clustered Index Debate</a> (Video)</li>
<li><a href="http://sqlserverpedia.com/wiki/Index_Selectivity_and_Column_Order">Index Selectivity and Column Order</a></li>
</ul>
<p>Right, now that the admin has been handled, let&#8217;s get straight into things. Nothing like starting at the deep end…</p>
<p>Most people would rightly associate indexes with where clause predicates and joins, after all, the main usage of an index is to reduce the rows in consideration for a query as fast as possible. However there’s another portion of your queries that indexes can, if appropriately designed, help with – grouping and sorting.</p>
<p>Sorting is an extremely expensive operation, especially on large numbers of rows. For the academics in the audience, the algorithmic complexity of sorting is above linear, the time to sort a set of data increases faster than the number of items in the list. The common <a href="http://en.wikipedia.org/wiki/Sorting_algorithm">sorting algorithms</a> have an average time complexity of O(n log n). It’s better than O(n<sup>2</sup>), but it can still hurt at the higher row counts.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/On2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="O(n^2)" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/On2_thumb.png" alt="O(n^2)" width="214" height="204" border="0" /></a> <a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Onlogn.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="O(n log n)" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Onlogn_thumb.png" alt="O(n log n)" width="210" height="204" border="0" /></a></p>
<p>O(n<sup>2</sup>) on the left, O(n log n) on the right (Thanks to <a href="http://www.quickmath.com">QuickMath</a>)</p>
<p>Right, the non-academics can wake up now.</p>
<p>The other reason that sorting hurts is that it needs a memory grant to do the sort. If there’s a shortage of memory the query could have to wait a while for the memory to be granted and, if the optimiser mis-estimates the number of rows to be sorted, the memory grant could be insufficient and the sort would have to spill into TempDB. You don’t want that happening.</p>
<p>Finally, sort is a blocking operator in the execution plan (all rows must have been fetched from the source before any output can be returned), and so the client won’t start seeing rows returned until the entire sort has been completed. This can make the query feel like it takes longer than it really does.</p>
<p>Grouping and aggregation are much the same. To aggregate one set of values based on another set of values, SQL has to get all the like values of the grouping columns together so that it can do the aggregation. That sounds suspiciously like a sort doesn’t it?</p>
<p>SQL doesn’t always sort to do aggregations, but the alternative – hash tables – isn’t exactly free (homework exercise – read up on hash tables)</p>
<p>So for both sorting and grouping, the query processor’s job would be a lot easier if there was some way that it could get the data ordered by the sorting or grouping columns without having to do the work of actually sorting. Sounds impossible? No.</p>
<p><span id="more-1321"></span></p>
<p>Enter indexes. The b-tree structure of an index makes it great for quickly finding matching rows, but today we’re more interested in the leaf-level of the index than the upper levels. The leaf level of an index is logically ordered by the index key (not physically ordered, let’s stop that myth right here please).</p>
<p>The leaf level of an index is logically ordered by the index key columns. So, if the index leaf level is read, it can return the data in the order of the index key. If that order is what the query processor needs in order to process a sort or grouping, then there’s no need for an expensive sort to be done, the underlying order can be leveraged.</p>
<p>Now, before someone misquotes me and goes off crying out that data is always returned in the order of the index used, no it is not. If there’s no order by on a query, there’s no guarantee of order regardless of indexes. However if there is an order by, the query optimiser and query processor may be able to utilise the underlying index order and avoid the cost of actually sorting the data.</p>
<p>Ok, enough theory for now, let’s look at some practical examples. We’ll use AdventureWorks here and I’m going to use the TransactionHistoryArchive table (in the Product schema)</p>
<p>Firstly a simple (and silly) example:</p>
<pre class="brush: sql; title: ; notranslate">SELECT ProductID, TransactionDate, ActualCost
 FROM Production.TransactionHistoryArchive
 ORDER BY ActualCost</pre>
<p>This one’s not very realistic, but it’s a nice simple one to start with. Give me all the rows in the table ordered by the ActualCost column. There’s no filter here so many would say that indexes can’t help here, and it’s true that an index can’t help with finding rows (because all are required), but it can help.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample1.png"><img style="display: inline; border-width: 0px;" title="SortExample1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample1_thumb.png" alt="SortExample1" width="484" height="160" border="0" /></a></p>
<blockquote><p>Table &#8216;TransactionHistoryArchive&#8217;. Scan count 1, logical reads 1419, physical reads 0.</p>
<p>SQL Server Execution Times:<br />
CPU time = 421 ms,  elapsed time = 4855 ms.</p></blockquote>
<p>421ms of CPU time, with an estimated cost of 93% for the sort. Let’s see if we can make this any better.</p>
<p>If I create an index on ActualCost, that gives SQL the option of scanning the index (scan, because there’s no seek predicate) to retrieve the data in the order of the ActualCost column. I’m going to have to make it a covering index, as there is no way at all that SQL will willingly do key lookups for every single row of the table.</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_TransactionHistoryArchive_ACtualCost
 ON Production.TransactionHistoryArchive (ActualCost)
 INCLUDE (ProductID, TransactionDate)</pre>
<p>Let’s see how that’s changed the query’s execution.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample2.png"><img style="display: inline; border-width: 0px;" title="SortExample2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample2_thumb.png" alt="SortExample2" width="364" height="94" border="0" /></a></p>
<blockquote><p>Table &#8216;TransactionHistoryArchive&#8217;. Scan count 1, logical reads 682, physical reads 0.</p>
<p>SQL Server Execution Times:<br />
CPU time = 47 ms,  elapsed time = 4073 ms.</p></blockquote>
<p>The reads have dropped slightly, because we’re now scanning a nonclustered index which is smaller than the clustered index, but that’s not the main point here. The CPU usage has dropped by around a factor of 8. 421ms down to 47ms. If this was a critical query that ran several times a minute then this change could make a nice improvement to throughput and overall CPU usage.</p>
<p>That was a silly example, let’s try for something a little more complex:</p>
<pre class="brush: sql; title: ; notranslate">SELECT  ProductID ,
TransactionDate ,
TransactionType ,
Quantity ,
ActualCost
FROM Production.TransactionHistoryArchive
WHERE TransactionType = 'S'
AND ReferenceOrderID = 51739
ORDER BY TransactionDate</pre>
<p>If we look at the execution plan, there’s already an index in use. There’s an index on ReferenceOrderID, but it’s clearly not as good as it could be.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample3.png"><img style="display: inline; border-width: 0px;" title="SortExample3" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample3_thumb.png" alt="SortExample3" width="484" height="144" border="0" /></a></p>
<blockquote><p>Table &#8216;TransactionHistoryArchive&#8217;. Scan count 1, logical reads 222, physical reads 0.</p>
<p>SQL Server Execution Times:<br />
CPU time = 0 ms,  elapsed time = 1 ms.</p></blockquote>
<p>Well, it’s not exactly taking ages on the CPU (it&#8217;s a tiny resultset), but this can still be better than it is. The key lookup is there because the existing index is only on two columns – ReferenceOrderID and ReferenceOrderLineID. I can’t modify that index for this query without potentially breaking some other query’s use of it, so I’ll create a new index.</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_TransactionTypeReferenceOrderID
ON Production.TransactionHistoryArchive (TransactionType, ReferenceOrderID)
INCLUDE (ProductID, TransactionDate, Quantity, ActualCost)</pre>
<p>Why TransactionType first? Come back on Friday and I’ll be discussing that.</p>
<p>Plan’s now much simpler, the key lookup has gone, and now the sort is the majority of the cost</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample4.png"><img style="display: inline; border-width: 0px;" title="SortExample4" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample4_thumb.png" alt="SortExample4" width="484" height="122" border="0" /></a></p>
<p>The key to getting rid of that sort is to understand that a multi-column index is sorted by the entire key. So, if we have an index on Col1, Col2, then for rows where Col1 has the same value, those rows are listed within the index ordered by Col2. In other words, if we read the leaf level of that index it would look like this:</p>
<pre>Col1      Col2
A         1
A         3
A         8
B         4
B         8
B         9
B         14
C         0
\C         1</pre>
<p>And so on and so on. Hence, if I filtered that by Col1 = B, the resulting rows could be returned ordered by Col2 with no additional work.</p>
<p>So in the above case, if I add the sort column (TransactionDate) as a key column in the index (it’s currently an Include column), then once the filter on TransactionType and ReferenceOrderID is done, the qualifying rows can be read in order of TransactionDate.</p>
<p>Let’s drop the index we created and create one with TransactionDate as an additional key column.</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_TransactionTypeReferenceOrderID
ON Production.TransactionHistoryArchive (TransactionType, ReferenceOrderID, TransactionDate)
INCLUDE (ProductID, Quantity, ActualCost)</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample5.png"><img style="display: inline; border-width: 0px;" title="SortExample5" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/SortExample5_thumb.png" alt="SortExample5" width="364" height="100" border="0" /></a></p>
<p>Success, the sort has gone!</p>
<p>One last example, then we’ll take a brief look at group by before finishing up for the day.</p>
<pre class="brush: sql; title: ; notranslate">SELECT  ProductID ,
TransactionDate ,
TransactionType ,
Quantity ,
ActualCost
FROM Production.TransactionHistoryArchive
WHERE ActualCost &gt; 2500
ORDER BY TransactionDate</pre>
<p>Can I use the same technique here to remove the need for a sort?</p>
<p>Let&#8217;s look at that simplistic index that I described earlier and see how the inequality would play out.</p>
<pre>Col1      Col2
A         1
A         3
A         8
B         4
B         8
B         9
B         14
C         0
C         1</pre>
<p>If I filter that for Col1 &gt; &#8216;A&#8217;, are the resultant rows ordered by Col2?</p>
<p>No, because the filter on Col1 is an inequality, a filter that returns multiple different values of Col1, the results aren&#8217;t ordered by the second column and hence we can&#8217;t  use an index here to both support the filter and the order.</p>
<p>Given this one, we could create an index to support the filter and have SQL sort the rows that qualified or we could create an index to support the order by and have SQL scan that and filter out rows that don&#8217;t match.</p>
<p>In general, the first option is the one that will be best in the majority of cases. The primary use of an index is to locate rows and filter resultsets. There are cases where the alternative may be appropriate, if the vast majority of the rows in the table qualify for the filter then it may be more optimal to support the sort and let SQL scan and filter. Definitely not the usual case though.</p>
<p>That about wraps up sorts, now for a quick look at group by.</p>
<p>As mentioned earlier, SQL has two ways to process grouping, it can do what is called a Stream Aggregate or it can use a hash table and do a Hash Aggregate. Stream Aggregate requires that the resultset be sorted in the order of the grouping columns. Well, given that fact and that we&#8217;ve spend a lot of time showing how to use indexes to support a sort, this should be quick and easy.</p>
<p>Let&#8217;s dive straight into some examples, because the theory is the same as for the sort described earlier</p>
<pre class="brush: sql; title: ; notranslate">SELECT ProductID, SUM(ActualCost) AS TotalCostPerProduct
FROM Production.TransactionHistoryArchive
GROUP BY ProductID</pre>
<p>This currently executes as a hash aggregate.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Grouping1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Grouping1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Grouping1_thumb.png" alt="Grouping1" width="364" height="84" border="0" /></a></p>
<blockquote><p>Table &#8216;Worktable&#8217;. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0.</p>
<p>Table &#8216;TransactionHistoryArchive&#8217;. Scan count 1, logical reads 1419, physical reads 0.</p>
<p>SQL Server Execution Times:</p>
<p>CPU time = 47 ms,  elapsed time = 46 ms.</p></blockquote>
<p>If we want a stream aggregate, then we need to get the rows entering the aggregation ordered by ProductID. SQL is not going to willingly sort the entire resultset (the hash aggregate is cheaper), so the only way we&#8217;re going to get a stream aggregate (other than with a hint) is by adding an index so that the data can be read from the index already ordered. There&#8217;s no filter here so it&#8217;s a straightforward index</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_TransactionHistoryArchive_ProductID
ON Production.TransactionHistoryArchive (ProductID)
INCLUDE (ActualCost)</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Grouping2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Grouping2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Grouping2_thumb.png" alt="Grouping2" width="364" height="73" border="0" /></a></p>
<blockquote><p>Table &#8216;TransactionHistoryArchive&#8217;. Scan count 1, logical reads 482, physical reads 0.</p>
<p>SQL Server Execution Times:</p>
<p>CPU time = 31 ms,  elapsed time = 34 ms.</p></blockquote>
<p>Not a massive improvement, but the principle is there.</p>
<p>Now, for homework, take these two queries and see firstly if it is possible to create an index to support both the filter and the group by and, if so, identify what that index is.</p>
<p><strong>Question 1</strong></p>
<pre class="brush: sql; title: ; notranslate">SELECT  ReferenceOrderID
ProductID ,
SUM(Quantity) AS TotalQuantity ,
SUM(ActualCost) AS TotalCost
FROM Production.TransactionHistoryArchive
WHERE TransactionType = 'S'
 GROUP BY ReferenceOrderID, ProductID</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Homework1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Homework1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Homework1_thumb.png" alt="Homework1" width="484" height="78" border="0" /></a></p>
<p><strong>Question 2</strong></p>
<pre class="brush: sql; title: ; notranslate">SELECT ReferenceOrderID ,MIN(ActualCost)|
FROM Production.TransactionHistoryArchive AS tha
WHERE TransactionDate &gt; '2004-01-01'
GROUP BY ReferenceOrderID</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Homework2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Homework2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/11/Homework2_thumb.png" alt="Homework2" width="484" height="78" border="0" /></a></p>
<p>Edit: And (as a late clarification) assume that the filter on transaction date is not always the same date.</p>
<p>Enough for today. Same time, same place Wednesday for a look at indexes on part of a table.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/11/07/sql-university-advanced-indexing-sorting-and-grouping/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>24 Hours of PASS Questions</title>
		<link>http://sqlinthewild.co.za/index.php/2011/09/27/24-hours-of-pass-questions/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/09/27/24-hours-of-pass-questions/#comments</comments>
		<pubDate>Tue, 27 Sep 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1286</guid>
		<description><![CDATA[I finally found the time to work through the questions from the 24 Hours of PASS session that I did. Thanks to everyone that attended the event Q1: Can you filter execution plans for sort warnings? No. The sort and hash warnings don’t appear in the execution plan. You’d have to trace for the hash [...]]]></description>
			<content:encoded><![CDATA[<p>I finally found the time to work through the questions from the 24 Hours of PASS session that I did. Thanks to everyone that attended the event</p>
<p><span style="color: #fbe3bd;">Q1: Can you filter execution plans for sort warnings?</span></p>
<p>No. The sort and hash warnings don’t appear in the execution plan. You’d have to trace for the hash and sort warning events and correlate that with either batch/statement started and completed events or with the run-time plan events.</p>
<p><span style="color: #fbe3bd;">Q2: To get the query executions can you just add the statement completed or batch completed events?</span></p>
<p>The Statement Completed and Batch completed events have durations and can be filtered on that duration. The problem however is that the execution plan events (eg showplan all, showplan xml, statistics profile, statistics xml) have no duration column. Hence the execution plan events can’t be filtered on duration even though the statement_completed and batch_completed events can.</p>
<p><span style="color: #fbe3bd;">Q3: If you run a Profiler trace and all the plans are being pulled from cache will that mean that no ShowPlan event data will be shown in the trace?</span></p>
<p>Depends which event is being traced. There are events for query compile (showplan all for query compile and showplan xml for query compile) that only fire when the query compiles, so those will not fire if the plan is being pulled from cache. The other execution plan events are all fired each time the query executes.</p>
<p><span style="color: #fbe3bd;">Q4: Why can you sometimes get nulls or blank strings for the query plan from sys.dm_exec_query_plan</span></p>
<p>From Books Online:</p>
<blockquote><p>Under the following conditions, no Showplan output is returned in the query_plan column of the returned table for sys.dm_exec_query_plan:</p>
<ul>
<li>If the query plan that is specified by using plan_handle has been evicted from the plan cache, the query_plan column of the returned table is null.</li>
<li>Some Transact-SQL statements are not cached, such as bulk operation statements or statements containing string literals larger than 8 KB in size. XML Showplans for such statements cannot be retrieved by using sys.dm_exec_query_plan unless the batch is currently executing because they do not exist in the cache.</li>
</ul>
</blockquote>
<p><span style="color: #fbe3bd;">Q5: Do the execution plans from the DMVs contain execution information?</span></p>
<p>No. The plans extracted from cache contain compile-time information only.</p>
<hr />
<p>If you enjoyed this and are going to be at PASS Summit this year, it&#8217;s not too late to sign up for the all-day precon (<a href="http://www.sqlpass.org/summit/2011/Speakers/CallForSpeakers/SessionDetail.aspx?sid=1245">All about Execution Plans</a>) that Grant Fritchey (<a href="http://www.scarydba.com/">blog</a>|<a href="https://twitter.com/#!/GFritchey">twitter</a>) and I are doing.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/09/27/24-hours-of-pass-questions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>On hiatus</title>
		<link>http://sqlinthewild.co.za/index.php/2011/09/06/on-hiatus/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/09/06/on-hiatus/#comments</comments>
		<pubDate>Tue, 06 Sep 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1279</guid>
		<description><![CDATA[I&#8217;m taking a break from blogging while I finish off my thesis. With the pile of outstanding things that need doing and the deadlines for them, something has to give, and I figure the blog is the least impact of the lot. Other than possible 24 Hours of PASS and PASS Summit feedback and some [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m taking a break from blogging while I finish off my thesis. With the pile of outstanding things that need doing and the deadlines for them, something has to give, and I figure the blog is the least impact of the lot.</p>
<p>Other than possible 24 Hours of PASS and PASS Summit feedback and some promised SQL University posts, the next blog post here will be a discussion of how not to go about doing a research degree while working full-time, which will be posted sometime after I get the thesis submitted.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/09/06/on-hiatus/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Compiles and recompiles</title>
		<link>http://sqlinthewild.co.za/index.php/2011/08/16/compiles-and-recompiles/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/08/16/compiles-and-recompiles/#comments</comments>
		<pubDate>Tue, 16 Aug 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1254</guid>
		<description><![CDATA[I want to spend some time over the next few months looking at query compilation and the plan cache, and there&#8217;s a couple concepts that I want to get cleared up beforehand. The first of those is around two terms that are often used interchangeably, compile and recompile. Compile A compile occurs when a query [...]]]></description>
			<content:encoded><![CDATA[<p>I want to spend some time over the next few months looking at query compilation and the plan cache, and there&#8217;s a couple concepts that I want to get cleared up beforehand. The first of those is around two terms that are often used interchangeably, compile and recompile.</p>
<h3>Compile</h3>
<p>A compile occurs when a query is given to the query optimiser and, when it does a lookup into the plan cache, no matching plan is found. The optimism must then compile the query, generating an execution plan, must add that plan to the plan cache (in most cases) and then must pass that plan onto the query execution engine so that the query can be executed. (<a href="http://technet.microsoft.com/en-us/library/Cc966425">http://technet.microsoft.com/en-us/library/Cc966425</a>)</p>
<h3>Recompile</h3>
<p>A recompile is something slightly different. For a recompile, the optimiser must find a matching plan when it queries the plan cache, must hand that cached plan over to the query execution engine and then while doing validation checks the execution engine must determine that then query plan is no longer valid and request the optimiser to partially or completely recompile the query. (<a href="http://technet.microsoft.com/en-us/library/Cc966425">http://technet.microsoft.com/en-us/library/Cc966425</a>)</p>
<p>Subtle difference. Both cases result in the optimiser generating an execution plan, but the reasons can be different. Also worth noting is that a compile results in a new plan in the cache, a recompile simply replaces an existing plan.</p>
<p>Another difference since SQL 2005 &#8211; a compile is for the entire batch, but a recompile can be for just a single statement within the batch.</p>
<hr />
<p>Now the theory&#8217;s dealt with, let&#8217;s look at some examples and see how we can track these two events and try and get a better understanding of which occurs when and how they look.</p>
<p>The tools I&#8217;m going to use to track these are performance monitor with the compiles/sec and recompiles/sec counters and SQL Profiler with the event SP:StmtRecompile event (there&#8217;s no profiler event for compilation). I&#8217;ll also check what&#8217;s in the plan cache after each test.</p>
<p>The first one’s going to be very simplistic, a query run against an empty plan cache.</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>What we get from that is a non-zero value for SQL Compilations/sec (perfmon) and the following from profiler (The SQL Recompiles/sec remains 0)</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles1.png"><img style="display: inline; border-width: 0px;" title="Compiles1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles1_thumb.png" border="0" alt="Compiles1" width="484" height="52" /></a></p>
<p>and the plan cache now contains one plan with one use. (for more info on how the CacheMiss and CacheInsert events work, see <a href="http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/">http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/</a> and <a href="http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/">http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/</a>)</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles2.png"><img style="display: inline; border-width: 0px;" title="Compiles2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles2_thumb.png" border="0" alt="Compiles2" width="484" height="24" /></a></p>
<p>In this case, I hope it was clear, we had a compile occur (empty plan cache before, new plan added to cache).</p>
<p>Now what happens if, with no clearing of the cache nor anything else being done, I mark that procedure for recompile and run it again?</p>
<p><span id="more-1254"></span></p>
<pre class="brush: sql; title: ; notranslate">EXEC sp_recompile OutStandingTotalByStatus
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>This is interesting. We don’t see a non-zero value for SQLRecompiles/sec (as might have been expected), instead, as with the previous case we get a spike for SQLCompiles/sec. The profiler output shows why</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles3.png"><img style="display: inline; border-width: 0px;" title="Compiles3" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles3_thumb.png" border="0" alt="Compiles3" width="484" height="122" /></a></p>
<p>Note the SP:CacheRemove being run for the stored procedure’s plan. So sp_recompile does not mark a plan as invalid and needing recompilation. It removes it from the cache entirely. The next time that procedure runs SQL does a cache lookup, doesn’t find a matching plan and compiles a new one (compile, not recompile)</p>
<p>Onwards… What about if I alter the procedure?</p>
<p>I’m not going to clear the cache first, the procedure has a plan in there from the last test, we can use that to see the effects.</p>
<pre class="brush: sql; title: ; notranslate">ALTER PROCEDURE OutStandingTotalByStatus
AS
-- This has changed!
SELECT 1;

SELECT o.OrderStatus, SUM(UnitPrice*Quantity) AS TotalOutstanding
FROM dbo.Orders o
INNER JOIN dbo.OrderDetails od ON o.OrderID = od.OrderID
WHERE ShippingDate IS NULL
GROUP BY OrderStatus;
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>Again perfmon shows a bump in Compiles/sec and the recompiles/sec is still at 0. Again, profiler shows why.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles4.png"><img style="display: inline; border-width: 0px;" title="Compiles4" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles4_thumb.png" border="0" alt="Compiles4" width="484" height="116" /></a></p>
<p>Same as when I ran the sp_recompile, the ALTER PROCEDURE triggered a SP:CacheRemove. The plan was gone from cache at the point the stored procedure ran again, so this was considered a compile, not a recompile.</p>
<p>btw, for those curious, if I query the plan cache between altering the procedure and running it again, the procedure’s plan really has gone.</p>
<p>Let’s try something more complex, an alteration of one of the base tables. I’m going to clear the cache first, for reasons that will later be clear</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO

ALTER TABLE dbo.OrderDetails ADD Filler CHAR(10)
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>Ha! This time perfmon shows a non-zero value for SQLRecompiles/sec. The profiler trace has a different form too</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles5.png"><img style="display: inline; border-width: 0px;" title="Compiles5" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles5_thumb.png" border="0" alt="Compiles5" width="484" height="92" /></a></p>
<p>There’s no SP:CacheRemove. No SP:CacheInsert either.</p>
<p>The alteration of the table did not result in SQL going through the plan cache and removing plans for dependant objects. That makes sense if you think about it. There could be millions of plans in cache, for SQL to check each and every one to see if it has a dependency on the table just changed would be terribly time consuming, so it’s not done. What happens instead is that the next time the procedure runs SQL looks in the cache, finds the procedure’s plan (the SP:CacheHit), does the pre-execution validations and finds that one of the base objects has changed since that plan was compiled. The plan is then sent to the optimiser to recompile that plan (the SP:Recompile event)</p>
<p>Here’s the other interesting thing, from a query of the plan cache</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles6.png"><img style="display: inline; border-width: 0px;" title="Compiles6" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles6_thumb.png" border="0" alt="Compiles6" width="484" height="38" /></a></p>
<p>The usecounts is 2, not 1. I ran that procedure once before altering the table, once after. The alter of the table forced SQL to recompile the plan on the second execution, but the usecount for the plan reflects both executions, the one from before the recompile and the one from after.</p>
<p>In all the previous cases, when the plan was actually removed from cache and then compiled and inserted the usecount was only 1 because when the plan was removed all related information was removed with it. With a recompile it’s not removed, so the usecounts aren’t reset to 0.</p>
<p>This is important to note. There are lots of articles (and books even) that say to check plan usecounts as a low usecount is a sign of frequent recompiles. As we can now see, it’s not. It’s a sign of frequent compiles, of the plans being removed from cache and later re-added, recompiles don’t affect the usecount (well, at least not in this case, there may be cases where it does)</p>
<p>Now try a couple more common cases, see what they show, starting with rebuilding an index.</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO

ALTER INDEX PK_OrderDetails ON dbo.OrderDetails REBUILD
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>Any guesses?</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles7.png"><img style="display: inline; border-width: 0px;" title="Compiles7" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles7_thumb.png" border="0" alt="Compiles7" width="484" height="78" /></a></p>
<p>It’s a recompile (and the perfmon counters confirm that). As with the previous case, the usecount shown in sys.dm_exec_cached_plans is 2 for the procedure’s plan, not 1</p>
<p>Lastly, a statistics update. Can anyone guess what’s going to happen here? Anyone?</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO

-- Insert a few more rows into OrderDetails at this point

UPDATE STATISTICS dbo.OrderDetails WITH FULLSCAN
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles9.png"><img style="display: inline; border-width: 0px;" title="Compiles9" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles9_thumb.png" border="0" alt="Compiles9" width="484" height="90" /></a></p>
<p>Yup, it’s a recompile.</p>
<p>It is worth nothing that (in my tests anyway), if at least one row isn’t changed, the stats update has no effect. Perhaps an optimisation that checks whether there have been modifications or not before updating? I’m not sure what’s causing that.</p>
<p>There is a lot more I could test, SET option changes, memory pressure, configuration changes, etc, but I think I’ve done enough that anyone interested can play themselves for further info.</p>
<p>Ok, but what’s the use? Was this just a dive into internals with no practical usage? I don’t think so.</p>
<p>One thing that’s important when investigating SQL problems is to understand what the various counters, events and columns are saying and what they mean. If one is faced with a server with very high SQLCompiles/sec, it’s a waste of time to go looking at whether automatic or manual stats updates are happening too often. Similarly if the SQLRecompiles/sec is high, investigating the usage of WITH RECOMPILE and OPTION(RECOMPILE) is completely off track and is going to waste time and not help with solving the problem.</p>
<p>That’s why I believe it’s important to understand what the various counters, events and columns actually means and what occurrences within SQL affects them. None of us have time to spend a couple days busy investigating something that’s not related to what’s actually wrong with the server based on an incorrect understanding of what SQL is showing us.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/08/16/compiles-and-recompiles/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>All about Execution Plans</title>
		<link>http://sqlinthewild.co.za/index.php/2011/08/09/all-about-execution-plans/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/08/09/all-about-execution-plans/#comments</comments>
		<pubDate>Tue, 09 Aug 2011 14:30:55 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Conferences]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1262</guid>
		<description><![CDATA[Coming to the PASS Summit in October this year? Excellent! I say excellent, because Grant (blog&#124;twitter) and I are presenting a full-day seminar on the Monday, all about execution plans. Excited already? If not, let me give you a taste of what we&#8217;ll be discussing. Grant will be kicking things off with a look at [...]]]></description>
			<content:encoded><![CDATA[<p>Coming to the <a href="http://www.sqlpass.org/summit/2011/">PASS Summit</a> in October this year? Excellent!</p>
<p>I say excellent, because Grant (<a href="http://www.scarydba.com/">blog</a>|<a href="http://twitter.com/GFritchey">twitter</a>) and I are presenting a full-day seminar on the Monday, <a href="http://www.sqlpass.org/summit/2011/Speakers/CallForSpeakers/SessionDetail.aspx?sid=1245">all about execution plans</a>. Excited already? If not, let me give you a taste of what we&#8217;ll be discussing.</p>
<p>Grant will be kicking things off with a look at how the optimiser works. Not 500 level deep internals (that&#8217;d take all day by itself), but an overview of how the optimiser turns this</p>
<pre class="brush: sql; title: ; notranslate">SELECT t.Title, u.UserName, u.UserLevel, t.LastModified, t.LastPoster
FROM Threads t
INNER JOIN Posts p on t.ThreadID = p.ThreadID
INNER JOIN Users u on p.Poster = u.UserID
WHERE t.ThreadID = 42</pre>
<p>into this</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/QueryIntoExecPlan.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="QueryIntoExecPlan" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/QueryIntoExecPlan_thumb.png" border="0" alt="QueryIntoExecPlan" width="484" height="180" /></a></p>
<p>as well as some DMVs that give a view on what the optimiser is doing.</p>
<p>After that, I&#8217;ll be digging into what the plan cache is, how it&#8217;s structured and how plans are matched to incoming queries. I&#8217;ll also cover how you might go about monitoring SQL&#8217;s usage of the cache.</p>
<p>After that we&#8217;ll discuss the myriad and varied ways to get execution plans out of SQL and what the various different options for that return and what some of the terms involved really mean (estimated execution plans anyone?).</p>
<p>Once all that&#8217;s out there (which will probably take the entire morning) it&#8217;ll be onto the fun of reading the execution plans, what those various icons represent and what all their arcane properties are telling you. Maybe, just maybe we&#8217;ll also have a look at the raw XML form of the plan, just for fun.</p>
<p>And because theory&#8217;s not very useful without something to apply it to, we&#8217;ll be ending off the day by seeing how you can use the information from the execution plan to tune your queries. After all, everyone wants better performance, right?</p>
<p>Still not completely convinced? Then check out the <a href="http://www.sqlpass.org/24hours/fall2011/">24 hours of PASS</a> coming in September. Both Grant and I are doing a pre-con preview. I&#8217;m doing <a href="http://www.sqlpass.org/24hours/fall2011/SessionsbySchedule/ExtractingExecutionplans.aspx">extracting execution plans</a> and Grant&#8217;s doing a portion of the <a href="http://www.sqlpass.org/24hours/fall2011/SessionsbySchedule/AllAboutExecutionPlansReadingExecutionPlans.aspx">reading execution plans</a> section.</p>
<p>Right, now that everyone&#8217;s dying to attend this, I&#8217;m going to toss out a little bit of a challenge. If you&#8217;re coming to our precon and you have an execution plan that you don&#8217;t understand (maybe a strange operator, maybe a property with a value you&#8217;ve never seen), mail it to me (gail@&lt;blog domain&gt;) with a note as to what you want clarifying and we might discuss it during the pre-con. Some rules, just to keep things sane</p>
<ul>
<li>No confidential stuff. I don&#8217;t want legal trouble, neither do you.</li>
<li>If the exec plan takes 5 minutes to open in SSMS, I&#8217;m deleting it.</li>
<li>If it&#8217;s multiple pages in SSMS, I&#8217;m deleting it.</li>
<li>I don&#8217;t promise to look at anyone&#8217;s plans, it depends how we&#8217;re doing on time.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/08/09/all-about-execution-plans/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Goodbye IsNumeric hell</title>
		<link>http://sqlinthewild.co.za/index.php/2011/07/26/goodbye-isnumeric-hell/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/07/26/goodbye-isnumeric-hell/#comments</comments>
		<pubDate>Tue, 26 Jul 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1223</guid>
		<description><![CDATA[A well overdue feature introduced in Denali CTP 3 is that of the Try_Parse and Try_Convert functions. These are great for dealing with the something that’s frustrated SQL developers for years – data type conversions. Let’s imagine a rather nasty case, a file with some values in it that once imported into SQL (as character [...]]]></description>
			<content:encoded><![CDATA[<p>A well overdue feature introduced in Denali CTP 3 is that of the Try_Parse and Try_Convert functions. These are great for dealing with the something that’s frustrated SQL developers for years – data type conversions.</p>
<p>Let’s imagine a rather nasty case, a file with some values in it that once imported into SQL (as character data) looks something like this:</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/BadNumerics.png"><img style="display: inline; border-width: 0px;" title="BadNumerics" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/BadNumerics_thumb.png" border="0" alt="BadNumerics" width="128" height="145" /></a></p>
<p>Ewww… that’s a mess. Let’s see if we can identify which of the values can be converted into a numeric data type. The function prior to Denali for that was ISNUMERIC.</p>
<pre class="brush: sql; title: ; notranslate">SELECT ANumber, ISNUMERIC(ANumber)
FROM BadNumerics   </pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/IsNumeric.png"><img style="display: inline; border-width: 0px;" title="IsNumeric" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/IsNumeric_thumb.png" border="0" alt="IsNumeric" width="226" height="143" /></a></p>
<p>Great, so other than the obvious one, they’re all numeric data. Time to get converting.</p>
<pre class="brush: sql; title: ; notranslate">SELECT CAST(ANumber as Numeric(18,6))
  FROM BadNumerics
  WHERE ISNUMERIC(ANumber) = 1;</pre>
<blockquote><p><span style="color: #ff0000;">Msg 8114, Level 16, State 5, Line 1<br />
Error converting data type varchar to numeric.</span></p></blockquote>
<p>Err, so they’re numeric but can’t be converted to numeric. That’s fun. Maybe another of the numeric data types will work…</p>
<p><span id="more-1223"></span></p>
<pre class="brush: sql; title: ; notranslate">SELECT CAST(ANumber as FLOAT)
  FROM BadNumerics
  WHERE ISNUMERIC(ANumber) = 1;</pre>
<blockquote><p><span style="color: #ff0000;">Msg 8114, Level 16, State 5, Line 1<br />
Error converting data type varchar to float.</span></p></blockquote>
<p>Not that one either. Money perhaps, it’s usually more forgiving than float or numeric.</p>
<pre class="brush: sql; title: ; notranslate">SELECT CAST(ANumber as MONEY)
  FROM BadNumerics
  WHERE ISNUMERIC(ANumber) = 1;</pre>
<blockquote><p><span style="color: #ff0000;">Msg 235, Level 16, State 0, Line 1<br />
Cannot convert a char value to money. The char value has incorrect syntax.</span></p></blockquote>
<p>Different error, same problem. The issue here is that there’s no single data type that all of those messy values can convert to. 23,90 can convert to money, not float or numeric. 2.34e-3 and 2d6 both convert fine to float, not to money or numeric.</p>
<p>So ISNUMERIC is a bit of a misleading function. All it tells you is that the value can be cast to one of the numeric data types in SQL, it’s up to you to figure out which one.</p>
<p>Pre-Denali a data set like that would be a terrible mess to get converted (though I’d probably just send it back to where it came from and ask for it to be cleaned at the source). In Denali though, it’s not all that bad.</p>
<pre class="brush: sql; title: ; notranslate">SELECT ANumber, TRY_CONVERT(NUMERIC(18,6), ANumber)
  FROM BadNumerics;</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/TryConvert1.png"><img style="display: inline; border-width: 0px;" title="TryConvert1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/TryConvert1_thumb.png" border="0" alt="TryConvert1" width="229" height="141" /></a></p>
<p>Now that’s more like it… We can do the same with the other numeric data types</p>
<pre class="brush: sql; title: ; notranslate">SELECT ANumber,
    TRY_CONVERT(NUMERIC(18,6), ANumber) as TryNumeric,
    TRY_CONVERT(FLOAT, ANumber) AS TryFloat,
    TRY_CONVERT(MONEY, ANumber) AS TryMoney
  FROM BadNumerics;</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/TryNumeric2.png"><img style="display: inline; border-width: 0px;" title="TryNumeric2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/TryNumeric2_thumb.png" border="0" alt="TryNumeric2" width="283" height="130" /></a></p>
<p>Other than the one that’s clearly not a number of any form, the rest convert to at least one of the data types, and now I have an error-free way to convert everything that converts and ignore the rest without having to write nasty validation functions of my own.</p>
<pre class="brush: sql; title: ; notranslate">WITH ConvertAMess (Original, ConvertedNumber) AS (
  SELECT ANumber,
      COALESCE(TRY_CONVERT(NUMERIC(18,6), ANumber),TRY_CONVERT(FLOAT, ANumber),TRY_CONVERT(MONEY, ANumber))
    FROM BadNumerics
)
SELECT  Original, ConvertedNumber
  FROM ConvertAMess
  WHERE ConvertedNumber IS NOT NULL;</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/UltimateTry.png"><img style="display: inline; border-width: 0px;" title="UltimateTry" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/UltimateTry_thumb.png" border="0" alt="UltimateTry" width="201" height="121" /></a></p>
<p>Not perfect, but a darn side better than what we started with. That’s going to simplify the kind of data-cleansing imports that I’ve been seeing recently.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/07/26/goodbye-isnumeric-hell/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>SQLSkills Immersion training in London</title>
		<link>http://sqlinthewild.co.za/index.php/2011/07/22/sqlskills-immersion-training-in-london/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/07/22/sqlskills-immersion-training-in-london/#comments</comments>
		<pubDate>Fri, 22 Jul 2011 14:00:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1235</guid>
		<description><![CDATA[Just short of the winter solstice, I bailed out of a freezing cold Johannesburg for warmer climates; well, actually for London where the weather could almost have been mistaken for a South African winter, except much wetter. However I wasn’t going to London for the weather (fortunately), nor even for some sightseeing; I was going [...]]]></description>
			<content:encoded><![CDATA[<p>Just short of the winter solstice, I bailed out of a freezing cold Johannesburg for warmer climates; well, actually for London where the weather could almost have been mistaken for a South African winter, except much wetter.</p>
<p>However I wasn’t going to London for the weather (fortunately), nor even for some sightseeing; I was going to London for some SQL training. Not just any SQL training, but some seriously deep training from among the best trainers in the world, one of <a href="http://sqlskills.com/blogs/paul/">Paul Randal</a> and <a href="http://www.sqlskills.com/blogs/kimberly/">Kimberly Tripp</a>’s five day immersion courses, and immersion it most certainly was.</p>
<p>It gives a good feel for the intensity and depth of the course when, after all the introductions and housekeeping <a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/clip_image002.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; float: right; padding-top: 0px; border-width: 0px;" title="clip_image002" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/clip_image002_thumb.png" border="0" alt="clip_image002" hspace="12" width="324" height="244" align="right" /></a>on the Monday morning were done, Paul started off by diving straight into the structure of pages and rows. Start slowly, where’s the fun in that? By lunchtime I had on the order of 20 pages of notes. The reasoning behind starting with that is that the internal structures come up again and again and again in the later material, so a firm understanding of how things are put together at the lowest level makes it easier to understand the features that are built on top.</p>
<p>The material covered is detailed on the SQLSkills website (<a href="http://www.sqlskills.com/T_ImmersionInternalsDesign.asp">http://www.sqlskills.com/T_ImmersionInternalsDesign.asp</a>), so I’m not going <a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/clip_image004.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; float: left; padding-top: 0px; border-width: 0px;" title="clip_image004" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/clip_image004_thumb.png" border="0" alt="clip_image004" hspace="12" width="324" height="244" align="left" /></a>to waste space by listing it all again. The material was all covered in incredible detail and any point could be and frequently was expanded on if any of the students asked.</p>
<p>On the subject of questions, there was not a single one, over all five days, that Paul and Kimberly could not answer with at most a couple of minutes of research (mostly for things like kb article numbers). Questions and comments were encouraged and often the discussions were as valuable as the main course material.</p>
<p>By the Friday, all the students were looking a little worn out. Paul, of course, was still as vibrant as ever, to the point of heckling (in good fun naturally) someone who crawled in late on the Friday morning.</p>
<p>All in all that was a fantastic experience, and that was just week one out of the four. I’d really like to thank Paul and Kimberly for coming across to London and giving people who can’t make it to the US classes an opportunity to take one of their Immersion Courses. I certainly hope that they plan to make a return visit soon.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/07/clip_image006.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-top: 0px; border-width: 0px;" title="clip_image006" src="http://sqlinthewild.co.za/wp-content/uploads/2011/07/clip_image006_thumb.png" border="0" alt="clip_image006" width="184" height="244" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/07/22/sqlskills-immersion-training-in-london/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
