<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Pseudo-Random</title><link>http://www.sqlservercentral.com/blogs/michael_coles/default.aspx</link><description>Michael Coles</description><dc:language>en</dc:language><generator>CommunityServer 2007.1 (Build: 20917.1142)</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/Pseudo-random" type="application/rss+xml" /><feedburner:browserFriendly></feedburner:browserFriendly><item><title>SQL 2008 RTM'd</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/08/07/sql-2008-rtm-d.aspx</link><pubDate>Thu, 07 Aug 2008 15:22:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:9013</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=9013</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=9013</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/08/07/sql-2008-rtm-d.aspx#comments</comments><description>&lt;p&gt;SQL Server 2008 was RTM&amp;#39;d yesterday (Aug. 6th).&amp;nbsp; It&amp;#39;s currently available for download on MSDN and TechNet.&amp;nbsp; More info here: &lt;a href="http://www.microsoft.com/presspass/press/2008/aug08/08-06SQLServer2008PR.mspx"&gt;http://www.microsoft.com:80/presspass/press/2008/aug08/08-06SQLServer2008PR.mspx&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=9013" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2008/default.aspx">SQL Server 2008</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Katmai/default.aspx">Katmai</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Development/default.aspx">Development</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/RTM/default.aspx">RTM</category></item><item><title>Why SELECT Queries Are Not DML</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/03/08/why-select-queries-are-not-dml.aspx</link><pubDate>Sun, 09 Mar 2008 04:09:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:8688</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=8688</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=8688</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/03/08/why-select-queries-are-not-dml.aspx#comments</comments><description>In this post I&amp;#39;ll look at why SELECT queries are not DML statements and why they are not read/write transactional....(&lt;a href="http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/03/08/why-select-queries-are-not-dml.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=8688" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/queries/default.aspx">queries</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/ACID/default.aspx">ACID</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/isolation+levels/default.aspx">isolation levels</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server/default.aspx">SQL Server</category></item><item><title>SQL Server 2008 Feb. CTP (CTP-6) Available</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/02/20/sql-server-2008-feb-ctp-ctp-6-available.aspx</link><pubDate>Wed, 20 Feb 2008 05:57:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:8661</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=8661</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=8661</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/02/20/sql-server-2008-feb-ctp-ctp-6-available.aspx#comments</comments><description>&lt;p&gt;Ladies and Gentlemen--Start Your VMs!&amp;nbsp; Microsoft has released SQL 2008 CTP 6 for download at the Connect Website:&amp;nbsp;&lt;a href="https://connect.microsoft.com/SQLServer/content/content.aspx?ContentID=5395"&gt;https://connect.microsoft.com/SQLServer/content/content.aspx?ContentID=5395&lt;/a&gt;.&amp;nbsp; There&amp;#39;s a whole laundry list of new features in this CTP:&lt;/p&gt;
&lt;p&gt;- iFTS&lt;br /&gt;- filtered indexes&lt;br /&gt;- MS Word rendering&lt;br /&gt;- sparse column support&lt;br /&gt;- SQL Audit&lt;br /&gt;- data compression&lt;br /&gt;- performance data collection&lt;br /&gt;- and more...&lt;/p&gt;
&lt;p&gt;The download is about 1.45 GB for the executable and this one is supposed to be &amp;quot;feature complete&amp;quot;.&amp;nbsp; Apparently they&amp;#39;re giving away an XBox or something&amp;nbsp;in some bug-reporting contest.&amp;nbsp; I suppose you can only count on people to locate and report bugs to you for free for so many years.&amp;nbsp; Unfortunately the links to the additional information pages (like the big-reporting contest page) on the SQL 2008 download page appear to be broken right now.&amp;nbsp; Anyway, if you&amp;#39;re interested in checking out the new features and functionality in 2008, now&amp;#39;s your chance.&lt;/p&gt;
&lt;p&gt;P.S. - I also checked CodePlex and it looks like there&amp;#39;s no update to AdventureWorks for the Feb. CTP; the latest version of AdventureWorks is for the Nov. CTP.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&amp;nbsp; The new version of AdventureWorks is now available at: &lt;/strong&gt;&lt;a href="http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=10901"&gt;&lt;strong&gt;http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=10901&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=8661" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2008/default.aspx">SQL Server 2008</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/CTP+6/default.aspx">CTP 6</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/New+Features/default.aspx">New Features</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Katmai/default.aspx">Katmai</category></item><item><title>Bye Bye FIPS... We Hardly Knew Ye...</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/02/18/bye-bye-fips-we-hardly-knew-ye.aspx</link><pubDate>Mon, 18 Feb 2008 18:55:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:8654</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=8654</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=8654</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/02/18/bye-bye-fips-we-hardly-knew-ye.aspx#comments</comments><description>&lt;p&gt;SQL Server has an option called SET FIPS_FLAGGER that many developers don&amp;#39;t even know about. This option warns you if your code does not comply with the SQL-92 levels of compliance specified by FIPS 127-2. This used to be a requirement if you wanted to sell your DBMS to the government. Effective February 5, 2008, FIPS 127-2 has been rescinded. Apparently the National Institute of Standards and Technology (NIST) wants to get out of the &amp;quot;software testing business&amp;quot;.&lt;/p&gt;
&lt;p&gt;So the question is now that FIPS 127-2 no longer exists, how long will it be before vendors stop testing for code compliance with a 16 year-old standard?&amp;nbsp; I&amp;#39;m sort of expecting to see a deprecation notice pop up on the SQL 2008 SET FIPS_FLAGGER page at some point: &lt;a href="http://msdn2.microsoft.com/en-us/library/ms189781.aspx"&gt;http://msdn2.microsoft.com/en-us/library/ms189781.aspx&lt;/a&gt;, although they may save deprecation for a future version of SQL Server.&amp;nbsp; On the other hand, it may get carried forward until it&amp;#39;s testing against a 30-year-old standard...&amp;nbsp; I guess you never can tell.&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=8654" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2005/default.aspx">SQL Server 2005</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/T-SQL/default.aspx">T-SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Standards/default.aspx">Standards</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/FIPS/default.aspx">FIPS</category></item><item><title>This Just In...</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/02/05/this-just-in.aspx</link><pubDate>Tue, 05 Feb 2008 15:55:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:7614</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=7614</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=7614</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/02/05/this-just-in.aspx#comments</comments><description>&lt;p&gt;I was as surprised as anyone to watch the Giants beat the Pats on Sunday.&amp;nbsp; I take that back, apparently I wasn&amp;#39;t as surprised as these folks:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;img height="500" alt="" src="http://blogs.sqlservercentral.com/blogs/michael_coles/19-0.jpg" width="500" border="0" /&gt;&lt;/p&gt;
&lt;p&gt;I doubt I lost as much money as they probably did.&amp;nbsp; Hell, I&amp;#39;d buy a copy of this book just to have it as a collector&amp;#39;s item.&amp;nbsp; Do you think they have any lying around in a warehouse somewhere?&amp;nbsp; &amp;quot;Sports Publishing&amp;quot;, are you reading this?&lt;/p&gt;
&lt;p&gt;Truman would be proud to see history repeating itself:&lt;/p&gt;
&lt;p&gt;&lt;img height="299" alt="" src="http://blogs.sqlservercentral.com/blogs/michael_coles/dewey_defeats_truman.jpg" width="410" border="0" /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Of course that&amp;#39;s not entirely fair since the Chicago Daily Tribune didn&amp;#39;t cover their rear end with a disclaimer like &amp;quot;To be published only in the event of a win.&amp;quot;&amp;nbsp; What a way to start the day :)&lt;/p&gt;
&lt;p&gt;FOLLOW-UP:&amp;nbsp; It looks like the publisher has replaced this initial publication with a new &amp;quot;Giants World Champions&amp;quot; book sometime between last night and&amp;nbsp;this morning&amp;nbsp;at&amp;nbsp;&lt;a href="http://www.amazon.com/s/ref=nb_ss_gw/102-7534258-3982567?url=search-alias%3Daps&amp;amp;field-keywords=19+-+0"&gt;http://www.amazon.com/s/ref=nb_ss_gw/102-7534258-3982567?url=search-alias%3Daps&amp;amp;field-keywords=19+-+0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ONE MORE TIME: Apparently you can still purchase the title &amp;quot;New England Patriots: 2008 Super Bowl Champions&amp;quot; on Amazon, at&amp;nbsp;&lt;a href="http://www.amazon.com/New-England-Patriots-Super-Champions/dp/1596703067/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1202366284&amp;amp;sr=1-1"&gt;http://www.amazon.com/New-England-Patriots-Super-Champions/dp/1596703067/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1202366284&amp;amp;sr=1-1&lt;/a&gt;. Surely not as good a read as &amp;quot;19 - 0&amp;quot;, and the title&amp;#39;s not as catchy, but I think I need one for my bookshelf.&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=7614" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Random/default.aspx">Random</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Leadership/default.aspx">Leadership</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/books/default.aspx">books</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/job+satisfaction/default.aspx">job satisfaction</category></item><item><title>Stop Using OPENXML (Please...)</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/01/20/stop-using-openxml-please.aspx</link><pubDate>Mon, 21 Jan 2008 01:33:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:4735</guid><dc:creator>Michael Coles</dc:creator><slash:comments>2</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=4735</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=4735</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/01/20/stop-using-openxml-please.aspx#comments</comments><description>&lt;p&gt;As we all know by now, the xml data type is one of the new features introduced in SQL Server 2005. The xml data type has a handy feature for shredding XML data, the nodes() method; but based on newsgroup postings&amp;nbsp;and various articles on SQL Server 2005 XML it seems that developers are still stuck in an OPENXML state of mind. OPENXML is a rowset provider that was sort of thrown onto the tail end of SQL Server 2000, along with a couple of&amp;nbsp;XML-specific system stored procedures,&amp;nbsp;to implement XML document shredding. OPENXML shredding, circa 2000, looked like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;font color="#0000ff" size="2"&gt;DECLARE&lt;/font&gt;&lt;font size="2"&gt; @xml &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;8000&lt;/font&gt;&lt;font color="#808080" size="2"&gt;);&lt;br /&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&lt;br /&gt;-- Character representation of the XML&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;SET&lt;/font&gt;&lt;font size="2"&gt; @xml &lt;/font&gt;&lt;font color="#808080" size="2"&gt;=&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;&amp;lt;capitals&amp;gt;&lt;br /&gt;&amp;lt;state name=&amp;quot;Alabama&amp;quot; &lt;br /&gt;&amp;nbsp; abbreviation=&amp;quot;AL&amp;quot;&lt;br /&gt;&amp;nbsp;&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;nbsp;capital=&amp;quot;Montgomery&amp;quot; &lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;flag=&amp;quot;AL.gif&amp;quot; &lt;br /&gt;&amp;nbsp; date=&amp;quot;December 14, 1819&amp;quot; &lt;br /&gt;&amp;nbsp; fact=&amp;quot;Rosa Parks refused to give up her seat on a Montgomery bus in 1955. The Montgomery Bus Boycott kicked off the Civil Rights era a few days later.&amp;quot; &lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;address=&amp;quot;600 Dexter Ave&amp;quot; &lt;br /&gt;&amp;nbsp; zip=&amp;quot;36130&amp;quot; &lt;br /&gt;&amp;nbsp; long=&amp;quot;-86.301963&amp;quot; &lt;br /&gt;&amp;nbsp; lat=&amp;quot;32.377189&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;state name=&amp;quot;Alaska&amp;quot; &lt;br /&gt;&amp;nbsp; abbreviation=&amp;quot;AK&amp;quot; &lt;br /&gt;&amp;nbsp; capital=&amp;quot;Juneau&amp;quot; &lt;br /&gt;&amp;nbsp; flag=&amp;quot;AK.gif&amp;quot; &lt;br /&gt;&amp;nbsp; date=&amp;quot;January 3, 1959&amp;quot; &lt;br /&gt;&amp;nbsp; fact=&amp;quot;In 1867 United States Secretary of State William H. Seward offered Russia $7,200,000, or two cents per acre, for Alaska.&amp;quot; &lt;br /&gt;&amp;nbsp; address=&amp;quot;120 4th Street&amp;quot; &lt;br /&gt;&amp;nbsp; zip=&amp;quot;99801&amp;quot; &lt;br /&gt;&amp;nbsp; long=&amp;quot;-134.410699&amp;quot; &lt;br /&gt;&amp;nbsp; lat=&amp;quot;58.301072&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;state name=&amp;quot;Arizona&amp;quot; &lt;br /&gt;&amp;nbsp; abbreviation=&amp;quot;AZ&amp;quot; &lt;br /&gt;&amp;nbsp; capital=&amp;quot;Phoenix&amp;quot; &lt;br /&gt;&amp;nbsp; flag=&amp;quot;AZ.gif&amp;quot; &lt;br /&gt;&amp;nbsp; date=&amp;quot;February 14, 1912&amp;quot; &lt;br /&gt;&amp;nbsp; fact=&amp;quot;Arizona is home of the Grand Canyon National Park.&amp;quot; &lt;br /&gt;&amp;nbsp; address=&amp;quot;1700 West Washington St&amp;quot; &lt;br /&gt;&amp;nbsp; zip=&amp;quot;85007&amp;quot; &lt;br /&gt;&amp;nbsp; long=&amp;quot;-112.095704&amp;quot; &lt;br /&gt;&amp;nbsp; lat=&amp;quot;33.448543&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;/capitals&amp;gt;&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;-- Internal handle for the XML DOM document&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;DECLARE&lt;/font&gt;&lt;font size="2"&gt; @iDoc &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;INT&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;-- Create an XML DOM representation of the document&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;EXEC&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#800000" size="2"&gt;sp_xml_preparedocument&lt;/font&gt;&lt;font size="2"&gt; @iDoc &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;OUTPUT&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; @xml&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&lt;br /&gt;-- Shred the document with OPENXML&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;SELECT&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#808080" size="2"&gt;*&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;FROM&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;OPENXML&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;@iDoc&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;/capitals/state&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;WITH&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Name&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@name&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Abbreviation &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;2&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@abbreviation&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Capital &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@capital&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Flag &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@flag&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Date &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@date&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Fact &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;500&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@fact&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp; Address&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@address&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; ZIP &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;5&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@zip&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Long &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;FLOAT&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@long&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Lat &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;FLOAT&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@lat&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;);&lt;br /&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&lt;br /&gt;-- You have to manually remove the DOM document from memory&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;EXEC&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#800000" size="2"&gt;sp_xml_removedocument&lt;/font&gt;&lt;font size="2"&gt; @iDoc&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;/p&gt;&lt;/font&gt;&lt;/code&gt;
&lt;p&gt;The XML document used here is borrowed from a sample I created for&amp;nbsp;an article a while back.&amp;nbsp;Apart from the complexity involved with the OPENXML syntax, on SQL 2000 you also had to manually manage memory allocation. The sp_xml_preparedocument procedure invoked MSXML through COM, and automatically allocated 1/8th of the server&amp;#39;s total memory to the XML cache. If your server has 2 GB of RAM, the short sample above will allocate 250 MB (!) to process the 1,200 byte document. At the end of processing, you have to call sp_xml_removedocument to free up the allocated memory. Dealing with XML documents larger than 8,000 bytes on SQL 2000 was a seriously convoluted undertaking. The xml data type nodes() method eliminates the sp_xml_preparedocument and sp_xml_removedocument&amp;nbsp;steps,&amp;nbsp;while&amp;nbsp;easily handling&amp;nbsp;very large documents (up to 2.1 GB). The xml data type nodes() method is shown below:&lt;/p&gt;&lt;font color="#0000ff" size="2"&gt;&lt;code&gt;
&lt;p&gt;&lt;font size="2"&gt;DECLARE @xml &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;XML&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;&lt;br /&gt;--&amp;nbsp;Populate the&amp;nbsp;XML variable&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;SET&lt;/font&gt;&lt;font size="2"&gt; @xml &lt;/font&gt;&lt;font color="#808080" size="2"&gt;=&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;&amp;lt;capitals&amp;gt;&lt;br /&gt;&amp;lt;state name=&amp;quot;Alabama&amp;quot; &lt;br /&gt;&amp;nbsp; abbreviation=&amp;quot;AL&amp;quot;&lt;br /&gt;&amp;nbsp;&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;nbsp;capital=&amp;quot;Montgomery&amp;quot; &lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;flag=&amp;quot;AL.gif&amp;quot; &lt;br /&gt;&amp;nbsp; date=&amp;quot;December 14, 1819&amp;quot; &lt;br /&gt;&amp;nbsp; fact=&amp;quot;Rosa Parks refused to give up her seat on a Montgomery bus in 1955. The Montgomery Bus Boycott kicked off the Civil Rights era a few days later.&amp;quot; &lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;address=&amp;quot;600 Dexter Ave&amp;quot; &lt;br /&gt;&amp;nbsp; zip=&amp;quot;36130&amp;quot; &lt;br /&gt;&amp;nbsp; long=&amp;quot;-86.301963&amp;quot; &lt;br /&gt;&amp;nbsp; lat=&amp;quot;32.377189&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;state name=&amp;quot;Alaska&amp;quot; &lt;br /&gt;&amp;nbsp; abbreviation=&amp;quot;AK&amp;quot; &lt;br /&gt;&amp;nbsp; capital=&amp;quot;Juneau&amp;quot; &lt;br /&gt;&amp;nbsp; flag=&amp;quot;AK.gif&amp;quot; &lt;br /&gt;&amp;nbsp; date=&amp;quot;January 3, 1959&amp;quot; &lt;br /&gt;&amp;nbsp; fact=&amp;quot;In 1867 United States Secretary of State William H. Seward offered Russia $7,200,000, or two cents per acre, for Alaska.&amp;quot; &lt;br /&gt;&amp;nbsp; address=&amp;quot;120 4th Street&amp;quot; &lt;br /&gt;&amp;nbsp; zip=&amp;quot;99801&amp;quot; &lt;br /&gt;&amp;nbsp; long=&amp;quot;-134.410699&amp;quot; &lt;br /&gt;&amp;nbsp; lat=&amp;quot;58.301072&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;state name=&amp;quot;Arizona&amp;quot; &lt;br /&gt;&amp;nbsp; abbreviation=&amp;quot;AZ&amp;quot; &lt;br /&gt;&amp;nbsp; capital=&amp;quot;Phoenix&amp;quot; &lt;br /&gt;&amp;nbsp; flag=&amp;quot;AZ.gif&amp;quot; &lt;br /&gt;&amp;nbsp; date=&amp;quot;February 14, 1912&amp;quot; &lt;br /&gt;&amp;nbsp; fact=&amp;quot;Arizona is home of the Grand Canyon National Park.&amp;quot; &lt;br /&gt;&amp;nbsp; address=&amp;quot;1700 West Washington St&amp;quot; &lt;br /&gt;&amp;nbsp; zip=&amp;quot;85007&amp;quot; &lt;br /&gt;&amp;nbsp; long=&amp;quot;-112.095704&amp;quot; &lt;br /&gt;&amp;nbsp; lat=&amp;quot;33.448543&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;/capitals&amp;gt;&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;SELECT&lt;/font&gt;&lt;font size="2"&gt; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@name&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(100)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Name&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@abbreviation&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(2)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; Abbreviation&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;font size="2"&gt;Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@capital&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(100)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; Capital&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@flag&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(100)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; Flag&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@date&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(100)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; Date&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@address&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(100)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Address&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@zip&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;VARCHAR(5)&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; ZIP&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@long&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;FLOAT&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; Long&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;value&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;@lat&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;FLOAT&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; Lat&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;FROM&lt;/font&gt;&lt;font size="2"&gt; @xml&lt;/font&gt;&lt;font color="#808080" size="2"&gt;.&lt;/font&gt;&lt;font size="2"&gt;nodes&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#ff0000" size="2"&gt;&amp;#39;/capitals/state&amp;#39;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; TempXML &lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;Node&lt;/font&gt;&lt;font color="#808080" size="2"&gt;);&lt;/font&gt;&lt;/code&gt; 
&lt;p&gt;This produces the exact same results as the OPENXML method, but the syntax is much cleaner and the code is more manageable. You also don&amp;#39;t have to worry about COM and the manual memory management.&lt;/p&gt;
&lt;p&gt;The nodes() method simply generates a tabular result set that contains a row for each XML node that matches the path expression. In this example it returns a row for each &amp;#39;/capitals/state&amp;#39; node. Then the value() method is used to retrieve scalar values for each attribute in the row. You could also use the query() method to retrieve nodes instead of scalar values.&lt;/p&gt;
&lt;p&gt;As for speed, the difference between using the xml data type nodes() method and OPENXML is negligible. If you&amp;#39;re shredding XML data stored in a table, however, a primary XML index cuts the processing time down by&amp;nbsp;nearly 50%.&lt;/p&gt;
&lt;p&gt;For more manageable code and more robust and scalable applications, use the nodes() method to shred your XML instead of OPENXML.&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=4735" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Other/default.aspx">Other</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/XML/default.aspx">XML</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2005/default.aspx">SQL Server 2005</category></item><item><title>Stoopid Tech Interviewer Tricks</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/01/16/stoopid-tech-interviewer-tricks.aspx</link><pubDate>Wed, 16 Jan 2008 06:19:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:3370</guid><dc:creator>Michael Coles</dc:creator><slash:comments>9</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=3370</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=3370</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/01/16/stoopid-tech-interviewer-tricks.aspx#comments</comments><description>OK, so you’ve got your suit fresh from the drycleaners. Your shirt is pressed and your shoes are shined. You’ve gone over the job requirements a dozen times, and even took the time to scan BOL for obscure tips the interviewer might try to turn into trick...(&lt;a href="http://www.sqlservercentral.com/blogs/michael_coles/archive/2008/01/16/stoopid-tech-interviewer-tricks.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=3370" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Job-hunting/default.aspx">Job-hunting</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Stuff+and+Nonsense/default.aspx">Stuff and Nonsense</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Other/default.aspx">Other</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Leadership/default.aspx">Leadership</category></item><item><title>"Exact Numerics" Re-visited</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/21/quot-exact-numerics-quot-re-visited.aspx</link><pubDate>Sun, 21 Oct 2007 04:12:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:3043</guid><dc:creator>Michael Coles</dc:creator><slash:comments>8</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=3043</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=3043</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/21/quot-exact-numerics-quot-re-visited.aspx#comments</comments><description>&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;Wow, seems like I have a lot to write about today.&amp;nbsp; I just noticed a blog by SQL Server guru Hugo Kornelis about the difference between &amp;quot;exact&amp;quot; and &amp;quot;approximate&amp;quot; numeric data types.&amp;nbsp; (Hugo&amp;#39;s blog entry is here:&amp;nbsp; &lt;/font&gt;&lt;a href="http://sqlblog.com/blogs/hugo_kornelis/archive/2007/10/17/so-called-exact-numerics-are-not-at-all-exact.aspx"&gt;&lt;font face="Tahoma" size="2"&gt;http://sqlblog.com/blogs/hugo_kornelis/archive/2007/10/17/so-called-exact-numerics-are-not-at-all-exact.aspx&lt;/font&gt;&lt;/a&gt;&lt;font face="Tahoma" size="2"&gt;).&amp;nbsp; This is a topic that seems to pop up on the radar regularly on the SQL Server newsgroups, and Hugo attempts to dispel the myth of &amp;quot;exact&amp;quot; data types.&amp;nbsp; I appreciate Hugo&amp;#39;s opinion on the matter, but I do disagree with some of his points and conclusions, as I&amp;#39;ll try to explain below.&amp;nbsp; For those who aren&amp;#39;t familiar with the differences between &amp;quot;exact&amp;quot; and &amp;quot;approximate&amp;quot; data types, here&amp;#39;s the short version:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;* Approximate data types are the FLOAT and REAL data types.&amp;nbsp; These are floating point data types, and they are stored and manipulated according to the IEEE-754 IEEE Standard for Binary Floating-Point Arithmetic.&amp;nbsp; (A good overview of IEEE-754 is available here: &lt;/font&gt;&lt;a href="http://en.wikipedia.org/wiki/IEEE_floating-point_standard"&gt;&lt;font face="Tahoma" size="2"&gt;http://en.wikipedia.org/wiki/IEEE_floating-point_standard&lt;/font&gt;&lt;/a&gt;&lt;font face="Tahoma" size="2"&gt;).&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;* Exact data types include the integer types (INT, SMALLINT, BIGINT, etc.) and fixed-point NUMERIC/DECIMAL types.&amp;nbsp; Exact NUMERIC and DECIMAL data types are stored and manipulated in a Binary-Coded Decimal (BCD for all you old school Assembly Language programmers out there), or possibly packed BCD format.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;Approximate data types are converted to the IEEE-754 exponent-biased sign-magnitude format for storage and manipulation.&amp;nbsp; This can result in significant, often unexpected&amp;nbsp;rounding errors during processing and computation.&amp;nbsp; Exact data types, however,&amp;nbsp;are guaranteed to store an exact representation of any &lt;em&gt;decimal value (this is a very important concept - contrast with the word fraction) &lt;/em&gt;with precision and scale equal to, or less than, the precision and scale specified for the data type.&amp;nbsp; In other words, if I store the constant value 1234.5678 in a NUMERIC(10, 4) variable or column, there will be no rounding errors or data loss during the assignment and storage process.&amp;nbsp; FLOAT and REAL do not make this guarantee.&amp;nbsp; For instance, consider the following code:&lt;/font&gt;&lt;/p&gt;&lt;font color="#0000ff" size="2"&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;DECLARE&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; @f &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;REAL&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;SET&lt;/font&gt;&lt;font size="2"&gt; @f &lt;/font&gt;&lt;font color="#808080" size="2"&gt;=&lt;/font&gt;&lt;font size="2"&gt; 1234.5678&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;SELECT&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#ff00ff" size="2"&gt;CAST&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;@f &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AS&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;NUMERIC&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;20&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; 15&lt;/font&gt;&lt;font color="#808080" size="2"&gt;));&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;&lt;font face="Tahoma"&gt;This query returns &lt;font size="1"&gt;&lt;font size="2"&gt;1234.567749023437500, due to the approximate REAL representation.&amp;nbsp; As Hugo said, however, exact numeric types can also suffer from rounding errors.&amp;nbsp; Here are the&amp;nbsp;four situations in which exact numeric types can be rounded:&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;&lt;font face="Tahoma"&gt;&lt;font size="1"&gt;&lt;font size="2"&gt;1) If you try to store a decimal value with greater scale than the declared NUMERIC or DECIMAL data type the value will be rounded to fit.&amp;nbsp; Consider the following code:&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;&lt;font face="Tahoma"&gt;&lt;font size="1"&gt;&lt;font color="#0000ff" size="2"&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;DECLARE&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; @d &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;DECIMAL&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;10&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font size="2"&gt; 4&lt;/font&gt;&lt;font color="#808080" size="2"&gt;);&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font face="Courier New"&gt;SET&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; @d &lt;/font&gt;&lt;font color="#808080" size="2"&gt;=&lt;/font&gt;&lt;font size="2"&gt; 1234.56785&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&lt;font face="Courier New"&gt;SELECT&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; @d&lt;/font&gt;&lt;font color="#808080" size="2"&gt;;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;The DECIMAL variable is declared with 4 digits after the decimal point, but we&amp;#39;re trying to assign 5 digits after the decimal point.&amp;nbsp; SQL Server resolves this issue by rounding the assigned value up to 1234.5679.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;2) If you try to store an irrational number like the &lt;font size="3"&gt;&lt;font size="2"&gt;√2&lt;/font&gt; &lt;/font&gt;&lt;font size="2"&gt;or π or a recurring decimal like 0.333333... in an exact data type.&amp;nbsp; It seems obvious that you cannot store a number with an infinite number of digits in the finite memory of a computer.&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;&lt;em&gt;General rule of thumb #0 - You cannot store 100&amp;nbsp;gallons of water in a 2 liter bottle.&lt;/em&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;3) If you perform a calculation that results in an irrational number, a recurring decimal, or a result with greater scale than was declared for the exact numeric type it will be rounded before being stored.&amp;nbsp; This includes calculations like 1/3, 22/7, etc.&amp;nbsp;&amp;nbsp;I would challenge anyone with a lot of spare time and nothing better to do to provide an exact decimal value for 1/3; once it&amp;#39;s delivered, I&amp;#39;ll personally demonstrate how to put that exact decimal value in a SQL Server exact numeric type for them with no rounding issues.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;&lt;em&gt;General rule of thumb #1 - A decimal number is not a fraction, although decimal numbers and fractions can be converted to one another.&amp;nbsp; This process often requires rounding of some sort, whether you do it with a computer or with a pen and paper.&lt;/em&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;4) If you CAST or&amp;nbsp;CONVERT a numeric value to another data type with lower scale, it will be rounded according to the rules described in BOL.&amp;nbsp; If you use SQL rounding fuctions like ROUND your numeric value will be rounded as well.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;Another misconception that people seem to have is that the exact numeric data types in SQL are designed to store something other than decimal data.&amp;nbsp; People throw out fractions like 1/3 and 1/6, and then claim that SQL exact numeric types are not exact because they can&amp;#39;t store them.&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;SQL exact numeric types are not designed to store or manipulate fractional representations of numbers.&amp;nbsp; They are designed to store and manipulate integer and fixed-point decimal representations of data; therefore they suffer from the same limitations as the decimal system.&amp;nbsp; One of those limitations is that there are certain fractions, irrational numbers, and recurring decimals that absolutely cannot be represented exactly in the decimal system.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;&lt;em&gt;General rule of thumb #2 - if you can&amp;#39;t represent a decimal number exactly with a pen and paper, then&amp;nbsp;a computer cannot accurately represent it either.&lt;/em&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080" size="2"&gt;&lt;font color="#000000"&gt;The main problem here is more semantic than anything else.&amp;nbsp; As Hugo points out:&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;blockquote dir="ltr"&gt;
&lt;p&gt;&lt;font color="#808080"&gt;&lt;font color="#000000"&gt;&lt;font size="2"&gt;&lt;em&gt;&lt;strong&gt;It [SQL Server Books Online] also says that “floating point data is approximate; therefore, not all values in the data type range can be represented exactly”, thereby suggesting that other numeric data types are capable of representing all values in the data type range. The latter is of course not true, for there is no way that values such as 1/3, π, or √2 can ever be represented exactly in any of SQL Server’s data types.&lt;/strong&gt;&lt;/em&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p dir="ltr"&gt;&lt;font color="#808080"&gt;&lt;font color="#000000"&gt;&lt;font size="2"&gt;This makes a big assumption that&amp;nbsp;fractions and irrational numbers are part of the &amp;quot;data type range&amp;quot; for a given exact numeric type.&amp;nbsp; However, exact numeric data type ranges are specifically decimal numbers with the precision and scale specified for the data type.&amp;nbsp; No more, no less.&amp;nbsp; A TINYINT, for example, can store the numbers 0 to 255 with no decimal places.&amp;nbsp; Does its inability to store 254.8765463 or -12345 make the TINYINT data type &amp;quot;inexact&amp;quot;?&amp;nbsp; Similarly, the DECIMAL (2, 1) data type can store values between -9.9 and 9.9.&amp;nbsp; Does its inability to store -10.0 or -9.987654321 make it &amp;quot;inexact&amp;quot;?&amp;nbsp; What about complex numbers?&amp;nbsp; Like fractions, they represent a whole class of numbers that can&amp;#39;t be represented in SQL Server&amp;#39;s numeric data types -&amp;nbsp;so how does that fit into the exactness formula?&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p dir="ltr"&gt;&lt;font color="#808080"&gt;&lt;font color="#000000"&gt;&lt;font size="2"&gt;&lt;em&gt;General rule of thumb #3 - SQL data types cannot be&amp;nbsp;more exact than the subset of the decimal system they are designed to represent.&lt;/em&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p dir="ltr"&gt;&lt;font color="#808080"&gt;&lt;font color="#000000" size="2"&gt;There are also some who are upset that the SQL Server Books Online documentation team chose to use the words &amp;quot;exact&amp;quot; and &amp;quot;approximate&amp;quot; to describe these data types.&amp;nbsp; They seem to think it is an error that Microsoft should resolve.&amp;nbsp; That probably won&amp;#39;t happen, however, since the BOL team took their cue from the ISO SQL Standard, which specifically defines these data types as - what else? -&amp;nbsp;&amp;quot;exact&amp;quot; and &amp;quot;approximate&amp;quot;.&amp;nbsp; Of course the ISO SQL Standard borrowed their terminology from the IEEE-754 standard, which differentiates between &amp;quot;exact&amp;quot; and &amp;quot;approximate&amp;quot; numeric types as well.&amp;nbsp; So it&amp;#39;s unlikely Microsoft will change their terminology anytime in the near future.&lt;/font&gt;&lt;/font&gt;&lt;font color="#808080"&gt;&lt;/p&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=3043" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2005/default.aspx">SQL Server 2005</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/data+types/default.aspx">data types</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/Programming/default.aspx">Programming</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/T-SQL/default.aspx">T-SQL</category></item><item><title>The Problem With Joins</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/20/the-problem-with-joins.aspx</link><pubDate>Sat, 20 Oct 2007 18:46:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:3039</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=3039</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=3039</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/20/the-problem-with-joins.aspx#comments</comments><description>&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;I recently came up against a pretty big problem with outer joins on SQL Server, and thought I&amp;#39;d talk about it a bit.&amp;nbsp; First let me give you a little background on the problem.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;&lt;strong&gt;&amp;lt;Begin Background&amp;gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;It&amp;#39;s common in data warehousing and datamarts to &amp;quot;band&amp;quot; data.&amp;nbsp; By &amp;quot;banding&amp;quot; I&amp;#39;m referring to the practice of assigning identifiers to ranges of values, like dollar amounts.&amp;nbsp; As a simple example, you might classify your sales dollar amounts into four categories like the ones represented in this sample AmountRange table:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" size="2"&gt;AmountRange Table&lt;br /&gt;Id&amp;nbsp;&amp;nbsp; MinAmount&amp;nbsp;&amp;nbsp;&amp;nbsp; MaxAmount&lt;br /&gt;--&amp;nbsp;&amp;nbsp; ---------&amp;nbsp;&amp;nbsp;&amp;nbsp; ---------&lt;br /&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0.00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5.00&lt;br /&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5.00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10.00&lt;br /&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;10.00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;25.00&lt;br /&gt;4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 25.00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 50.00&lt;br /&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;50.00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100.00&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;The idea is to determine the pair of (MinAmount, MaxAmount) that a given dollar amount falls between and return that Id number.&amp;nbsp; A simple WHERE clause to do this might look like the following:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;WHERE&lt;/font&gt; SalesAmount &lt;font color="#808080"&gt;&amp;gt;=&lt;/font&gt; AmountRange.MinAmount&lt;br /&gt;&amp;nbsp; &lt;font color="#808080"&gt;AND&lt;/font&gt; SalesAmount &lt;font color="#808080"&gt;&amp;lt;&lt;/font&gt; AmountRange.MaxAmount&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;A dollar amount of 8.17 will get an Id of 2, while 58.93 will return an Id of 5.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;&lt;strong&gt;&amp;lt;End Background&amp;gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;So back to the problem at hand.&amp;nbsp; I was attempting to do this for two dollar amount values using LEFT OUTER JOINs.&amp;nbsp; The query I used looked like the following:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New" size="2"&gt;SELECT&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Id &lt;font color="#0000ff"&gt;AS&lt;/font&gt; ProductId&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;cr&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;.&lt;/font&gt;Id &lt;font color="#0000ff"&gt;AS&lt;/font&gt; CostRangeId&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp; pr&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080"&gt;.&lt;/font&gt;Id &lt;font color="#0000ff"&gt;AS&lt;/font&gt; PriceRangeId&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New"&gt;FROM&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; Products p&lt;br /&gt;&lt;/font&gt;&lt;font color="#808080"&gt;&lt;font face="Courier New"&gt;LEFT&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; &lt;font color="#808080"&gt;OUTER&lt;/font&gt; &lt;font color="#808080"&gt;JOIN&lt;/font&gt; &lt;font color="#000000"&gt;AmountRange&lt;/font&gt; cr&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New"&gt;&amp;nbsp; ON&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Cost &lt;font color="#808080"&gt;&amp;gt;=&lt;/font&gt; cr&lt;font color="#808080"&gt;.&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt;MinAmount&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080"&gt;&amp;nbsp; AND&lt;/font&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Cost &lt;font color="#808080"&gt;&amp;lt;&lt;/font&gt; cr&lt;font color="#808080"&gt;.&lt;/font&gt;MaxAmount&lt;br /&gt;&lt;/font&gt;&lt;font color="#808080"&gt;&lt;font face="Courier New"&gt;LEFT&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; &lt;font color="#808080"&gt;OUTER&lt;/font&gt; &lt;font color="#808080"&gt;JOIN&lt;/font&gt; &lt;font color="#000000"&gt;AmountRange&lt;/font&gt; pr&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New"&gt;&amp;nbsp; ON&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Price &lt;font color="#808080"&gt;&amp;gt;=&lt;/font&gt; cr&lt;font color="#808080"&gt;.&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;MinAmount&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080"&gt;&amp;nbsp; AND&lt;/font&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Price &lt;font color="#808080"&gt;&amp;lt;&lt;/font&gt; cr&lt;font color="#808080"&gt;.&lt;/font&gt;MaxAmount&lt;font color="#808080"&gt;;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" color="#000000" size="2"&gt;With a Products table that contains 19,000,000 rows, SQL Server comes up with a query plan like this (edited for readability):&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;|--Nested Loops(Left Outer Join, WHERE:([p].[Price]&amp;gt;=[pr].[MinAmount] AND [p].[Price]&amp;lt;[pr].[MaxAmount])&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;|--Nested Loops(Left Outer Join, WHERE:([p].[Cost]&amp;gt;=[cr].[MinAmount] AND [p].[Cost]&amp;lt;[cr].[MaxAmount])&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; | |--Clustered Index Scan(OBJECT:([p].[PK_Products]))&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp; | |--Clustered Index Scan(OBJECT:([cr].[PK_AmountRange]))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([pr].[PK_AmountRange]))&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;Not a bad plan overall, until you start looking at the cost and cardinality estimates.&amp;nbsp; Cardinality estimates are the optimizer&amp;#39;s estimates of&amp;nbsp;how many rows are generated between steps and for the final result set.&amp;nbsp; In this case we know that we started with 19,000,000 rows, and we will end up with 19,000,000 rows.&amp;nbsp; But SQL Server estimates the first join will generate an intermediate result set of 26,576,500 rows, and the second join will generate a final result set of 31,360,300 rows!&amp;nbsp; The total cost is 899.479.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;Apparently SQL Server does not keep good enough statistics to figure out that the ranges defined in the AmountRange table do not overlap one another.&amp;nbsp; While we know that LEFT OUTER JOINs like these could only generate more rows than we started with if the ranges in the AmountRange table actually did overlap.&amp;nbsp; The SQL optimizer apparently isn&amp;#39;t smart enough to figure this out.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;So what do we do about it?&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;Well, we can use subqueries instead, like this:&lt;/font&gt;&lt;/p&gt;&lt;font color="#0000ff"&gt;
&lt;p&gt;&lt;font face="Courier New" size="2"&gt;SELECT&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Id &lt;font color="#0000ff"&gt;AS&lt;/font&gt; ProductId&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;&amp;nbsp; (&lt;/font&gt;&lt;font color="#0000ff"&gt;SELECT&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; Id&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff"&gt;&amp;nbsp;&amp;nbsp; FROM&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; AmountRange&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;&amp;nbsp;&amp;nbsp; WHERE&lt;/font&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Cost &lt;font color="#808080"&gt;&amp;gt;=&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt; MinAmount&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND&lt;/font&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Cost &lt;font color="#808080"&gt;&amp;lt;&lt;/font&gt; MaxAmount&lt;/font&gt;&lt;font color="#808080" size="2"&gt;),&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;&amp;nbsp; (&lt;/font&gt;&lt;font color="#0000ff"&gt;SELECT&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; Id&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff"&gt;&amp;nbsp;&amp;nbsp; FROM&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt; AmountRange&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;WHERE&lt;/font&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Price &lt;font color="#808080"&gt;&amp;gt;=&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt; MinAmount&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND&lt;/font&gt; p&lt;font color="#808080"&gt;.&lt;/font&gt;Price &lt;font color="#808080"&gt;&amp;lt;&lt;/font&gt; MaxAmount&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New"&gt;FROM&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt; Products p&lt;font color="#808080"&gt;;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#808080"&gt;&lt;font face="Tahoma" color="#000000" size="2"&gt;The query plan for the updated query looks like this (again edited for readability):&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;&lt;font color="#808080"&gt;&lt;font color="#000000"&gt;
&lt;p&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;|--Compute Scalar(DEFINE:([Expr1013]=[Expr1013], [Expr1015]=[Expr1015]))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; |--Nested Loops(Left Outer Join, OUTER REFERENCES:([p].[Price]))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; |--Nested Loops(Left Outer Join, OUTER REFERENCES:([p].[Cost]))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; | |--Clustered Index Scan(OBJECT:([p].[PK_Products]))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; | |--Assert(WHERE:(CASE WHEN [Expr1012]&amp;gt;(1) THEN (0) ELSE NULL END))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; | |--Stream Aggregate(DEFINE:([Expr1012]=Count(*), [Expr1013]=ANY([cr].[Id])))&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;| |--Clustered Index Scan(OBJECT:([cr].[PK_AmountRange]), WHERE:([p].[Cost]&amp;gt;=[cr].[MinAmount] AND [p].[Cost]&amp;lt;[cr].[MaxAmount]))&lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;&amp;nbsp;&amp;nbsp; |--Assert(WHERE:(CASE WHEN [Expr1014]&amp;gt;(1) THEN (0) ELSE NULL END))&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New" color="#0000ff"&gt;&amp;nbsp;&amp;nbsp; |--Stream Aggregate(DEFINE:([Expr1014]=Count(*), [Expr1015]=ANY([pr].[Id])))&lt;br /&gt;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([pr].[PK_AmountRange]), WHERE:([p].[Price]&amp;gt;=[pr].[MinAmount] AND [p].[Price]&amp;lt;[pr].[MaxAmount]))&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#000000"&gt;&lt;font face="Tahoma" size="2"&gt;The total cost for this plan is 516.937, significantly lower than the previous version.&amp;nbsp; The updated query plan is also notable for its accurate cardinality estimates.&amp;nbsp; At every step of the process SQL Server estimates 19,000,000 rows of input, 19,000,000 rows of output, which matches up with our expectations.&amp;nbsp; The better estimates that come with the second version help SQL Server make better decisions along the way and help it come up with a much better query plan.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#000000"&gt;&lt;font face="Tahoma" size="2"&gt;If you have to perform a &amp;quot;banding&amp;quot; function like this, I highly recommend considering using subqueries instead of outer joins.&lt;/font&gt;&lt;font color="#008000"&gt;&lt;/p&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=3039" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2005/default.aspx">SQL Server 2005</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/optimization/default.aspx">optimization</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/joins/default.aspx">joins</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/query+plans/default.aspx">query plans</category></item><item><title>SSIS Custom Logging the Easy Way</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/09/ssis-custom-logging-the-easy-way.aspx</link><pubDate>Wed, 10 Oct 2007 02:44:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:3012</guid><dc:creator>Michael Coles</dc:creator><slash:comments>2</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=3012</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=3012</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/09/ssis-custom-logging-the-easy-way.aspx#comments</comments><description>&lt;p&gt;&lt;font face="Arial" size="2"&gt;SSIS provides several log providers, including the Log Provider for SQL Server (my personal favorite).&amp;nbsp; I decided pretty quickly that I needed to customize my logging capabilities.&amp;nbsp; There are two main methods for custom logging in SSIS:&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Create a custom log provider in managed code, as detailed on MSDN.&lt;/font&gt; 
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Use event handlers in SSIS to implement custom logging.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;The custom log provider option requires you to implement some .NET interfaces, compile, sign your assembly with a strong-name key, register the assembly with the GAC, etc.&amp;nbsp; Based on the deadlines I&amp;#39;m up against, and the extensive testing required before I could move something like this into production, this was not really option for me.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;Using event handlers is an option described in detail by Jamie Thomson at&amp;nbsp;&lt;/font&gt;&lt;a href="http://blogs.conchango.com/jamiethomson/archive/2005/06/11/SSIS_3A00_-Custom-Logging-Using-Event-Handlers.aspx"&gt;&lt;font face="Arial" size="2"&gt;http://blogs.conchango.com/jamiethomson/archive/2005/06/11/SSIS_3A00_-Custom-Logging-Using-Event-Handlers.aspx&lt;/font&gt;&lt;/a&gt;&lt;font face="Arial" size="2"&gt;.&amp;nbsp; This method has some limitations, however.&amp;nbsp; No fault of Jamie&amp;#39;s, but SSIS makes this a little more complex than it needs to be.&amp;nbsp; Some of the items involved are downright counterintuitive (variable scope issues with the &lt;font face="Courier New"&gt;OnVariableValueChanged&lt;/font&gt; event, for instance).&amp;nbsp; The custom log providers, and even stock log providers, provide more information than is accessible through event handlers.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;I toyed with both options before coming up with option number 3 for the stock Log Provider for SQL Server.&amp;nbsp; The SSIS Log Provider for SQL Server writes entries to the &lt;font face="Courier New"&gt;sysdtslog90&lt;/font&gt; table of the target database.&amp;nbsp; The log provider creates a stored procedure named &lt;font face="Courier New"&gt;sp_dts_addlogentry&lt;/font&gt;, which it calls to insert each row into the log table.&amp;nbsp; Option 3 is just to simply modify the &lt;font face="Courier New"&gt;sp_dts_addlogentry&lt;/font&gt; procedure to perform custom logging/auditing to your specifications.&amp;nbsp; In the example below I&amp;#39;m still writing the original log entry to the &lt;font face="Courier New"&gt;sysdtslog90&lt;/font&gt;&amp;nbsp;table, but I&amp;#39;ve extended the stored procedure to write entries to a second&amp;nbsp;table called &lt;font face="Courier New"&gt;AuditPackage&lt;/font&gt;.&amp;nbsp; The &lt;font face="Courier New"&gt;AuditPackage&lt;/font&gt; table stores a summary of each package as it executes.&lt;/font&gt;&lt;/p&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New" size="2"&gt;&lt;font color="#0000ff" size="2"&gt;
&lt;p&gt;CREATE&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;TABLE&lt;/font&gt;&lt;font color="#000000" size="2"&gt; AuditPackage &lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#000000" size="2"&gt;Id &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;INT&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;IDENTITY&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font color="#000000" size="2"&gt;1&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;/font&gt;&lt;font color="#000000" size="2"&gt; 1&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#808080" size="2"&gt;NOT&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#808080" size="2"&gt;NULL&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;PRIMARY&lt;/font&gt;&lt;font color="#000000" size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;KEY&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; PackageName &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;)&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#808080" size="2"&gt;NOT&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#808080" size="2"&gt;NULL,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; PackageGuid &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;UNIQUEIDENTIFIER&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; ExecutionGuid &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;UNIQUEIDENTIFIER&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; StartTime &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;DATETIME&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; EndTime &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;DATETIME&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font size="2"&gt;&amp;nbsp; ElapsedTime &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;INT&lt;/font&gt;&lt;font color="#808080" size="2"&gt;,&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp; Status&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;VARCHAR&lt;/font&gt;&lt;font color="#808080" size="2"&gt;(&lt;/font&gt;&lt;font size="2"&gt;100&lt;/font&gt;&lt;font color="#808080" size="2"&gt;));&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;CREATE&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt; &lt;font color="#0000ff"&gt;PROCEDURE&lt;/font&gt; dbo&lt;font color="#808080"&gt;.&lt;/font&gt;sp_dts_addlogentry @event &lt;font color="#0000ff"&gt;sysname&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@computer &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;&lt;font color="#808080"&gt;(&lt;/font&gt;128&lt;font color="#808080"&gt;),&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@operator &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;&lt;font color="#808080"&gt;(&lt;/font&gt;128&lt;font color="#808080"&gt;),&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@source &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;&lt;font color="#808080"&gt;(&lt;/font&gt;1024&lt;font color="#808080"&gt;),&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@sourceid &lt;font color="#0000ff"&gt;uniqueidentifier&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@executionid &lt;font color="#0000ff"&gt;uniqueidentifier&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@starttime &lt;font color="#0000ff"&gt;datetime&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@endtime &lt;font color="#0000ff"&gt;datetime&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@datacode &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@databytes &lt;font color="#0000ff"&gt;image&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;br /&gt;&amp;nbsp; &lt;/font&gt;@message &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;&lt;font color="#808080"&gt;(&lt;/font&gt;2048&lt;font color="#808080"&gt;)&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;&lt;font face="Courier New" size="2"&gt;AS&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="2"&gt; &lt;br /&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;BEGIN&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;INSERT&lt;/font&gt; &lt;font color="#0000ff"&gt;INTO&lt;/font&gt; sysdtslog90 &lt;font color="#808080"&gt;(&lt;/font&gt;&lt;font color="#0000ff"&gt;event&lt;/font&gt;&lt;font color="#808080"&gt;,&lt;/font&gt; computer&lt;font color="#808080"&gt;,&lt;/font&gt; operator&lt;font color="#808080"&gt;,&lt;/font&gt; source&lt;font color="#808080"&gt;,&lt;/font&gt; sourceid&lt;font color="#808080"&gt;,&lt;/font&gt; executionid&lt;font color="#808080"&gt;,&lt;/font&gt; starttime&lt;font color="#808080"&gt;,&lt;/font&gt; endtime&lt;font color="#808080"&gt;,&lt;/font&gt; datacode&lt;font color="#808080"&gt;,&lt;/font&gt; databytes&lt;font color="#808080"&gt;,&lt;/font&gt; &lt;font color="#0000ff"&gt;message&lt;/font&gt;&lt;font color="#808080"&gt;)&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;VALUES&lt;/font&gt; &lt;font color="#808080"&gt;(&lt;/font&gt;@event&lt;font color="#808080"&gt;,&lt;/font&gt; @computer&lt;font color="#808080"&gt;,&lt;/font&gt; @operator&lt;font color="#808080"&gt;,&lt;/font&gt; @source&lt;font color="#808080"&gt;,&lt;/font&gt; @sourceid&lt;font color="#808080"&gt;,&lt;/font&gt; @executionid&lt;font color="#808080"&gt;,&lt;/font&gt; @starttime&lt;font color="#808080"&gt;,&lt;/font&gt; @endtime&lt;font color="#808080"&gt;,&lt;/font&gt; @datacode&lt;font color="#808080"&gt;,&lt;/font&gt; @databytes&lt;font color="#808080"&gt;,&lt;/font&gt; @message&lt;font color="#808080"&gt;);&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;&amp;nbsp; INSERT&lt;/font&gt; &lt;font color="#0000ff"&gt;INTO&lt;/font&gt; AuditPackage &lt;font color="#808080"&gt;(&lt;/font&gt;PackageName&lt;font color="#808080"&gt;,&lt;/font&gt; PackageGuid&lt;font color="#808080"&gt;,&lt;/font&gt; ExecutionGuid&lt;font color="#808080"&gt;,&lt;/font&gt; StartTime&lt;font color="#808080"&gt;,&lt;/font&gt; ElapsedTime&lt;font color="#808080"&gt;)&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;SELECT&lt;/font&gt; @source&lt;font color="#808080"&gt;,&lt;/font&gt; @sourceid&lt;font color="#808080"&gt;,&lt;/font&gt; @executionid&lt;font color="#808080"&gt;,&lt;/font&gt; &lt;font color="#ff00ff"&gt;GETDATE&lt;/font&gt;&lt;font color="#808080"&gt;(),&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt; 0&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;WHERE&lt;/font&gt; &lt;font color="#808080"&gt;(&lt;/font&gt;@event &lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff0000"&gt;&amp;#39;PackageStart&amp;#39;&lt;/font&gt;&lt;font color="#808080"&gt;);&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New" color="#0000ff"&gt;&lt;br /&gt;&amp;nbsp; UPDATE&lt;/font&gt;&lt;font face="Courier New"&gt; AuditPackage&lt;br /&gt;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;SET&lt;/font&gt; EndTime &lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff00ff"&gt;GETDATE&lt;/font&gt;&lt;font color="#808080"&gt;(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;ElapsedTime &lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff00ff"&gt;DATEDIFF&lt;/font&gt;&lt;font color="#808080"&gt;(&lt;/font&gt;ms&lt;font color="#808080"&gt;,&lt;/font&gt; StartTime&lt;font color="#808080"&gt;,&lt;/font&gt; &lt;font color="#ff00ff"&gt;GETDATE&lt;/font&gt;&lt;font color="#808080"&gt;()),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;Status&lt;/font&gt; &lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff0000"&gt;&amp;#39;Complete&amp;#39;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;&amp;nbsp; WHERE&lt;/font&gt; &lt;font color="#808080"&gt;(&lt;/font&gt;@event &lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff0000"&gt;&amp;#39;PackageEnd&amp;#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080"&gt;AND&lt;/font&gt; PackageGuid &lt;font color="#808080"&gt;=&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt; @sourceid&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;AND&lt;/font&gt; ExecutionGuid &lt;font color="#808080"&gt;=&lt;/font&gt; @executionid&lt;font color="#808080"&gt;);&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New" color="#0000ff"&gt;&amp;nbsp; UPDATE&lt;/font&gt;&lt;font face="Courier New"&gt; AuditPackage&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;&amp;nbsp; SET&lt;/font&gt; &lt;font color="#0000ff"&gt;Status&lt;/font&gt; &lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff0000"&gt;&amp;#39;Error&amp;#39;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#0000ff"&gt;&amp;nbsp; WHERE&lt;/font&gt; &lt;font color="#808080"&gt;(&lt;/font&gt;@event &lt;font color="#808080"&gt;=&lt;/font&gt; &lt;font color="#ff0000"&gt;&amp;#39;OnError&amp;#39;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt;&lt;font face="Courier New"&gt;&lt;font color="#808080"&gt;AND&lt;/font&gt; PackageGuid &lt;font color="#808080"&gt;=&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt; @sourceid&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="2"&gt;&lt;font color="#808080"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; AND&lt;/font&gt; ExecutionGuid &lt;font color="#808080"&gt;=&lt;/font&gt; @executionid&lt;font color="#808080"&gt;);&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" color="#0000ff" size="2"&gt;END&lt;/p&gt;&lt;/font&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;&lt;font face="Arial"&gt;&lt;font size="2"&gt;On the SSIS side you need to enable the Log Provider for SQL Server on your packages, and select the events you want to capture.&amp;nbsp; Some possible extensions to this method include grabbing the &lt;font face="Courier New"&gt;OnPipelineRowsSent&lt;/font&gt; event and parsing the message to get more information for each data flow component, or logging other events like &lt;font face="Courier New"&gt;OnWarning&lt;/font&gt;, etc.&amp;nbsp; You could also add additional code to fire off a call to send email via database mail for errors or to write certain events to the Windows Event Log.&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;&lt;font face="Arial"&gt;&lt;strong&gt;NOTE:&amp;nbsp; &lt;/strong&gt;&lt;em&gt;Just for clarification, the &lt;font face="Courier New"&gt;sp_dts_addlogentry&lt;/font&gt; procedure is *not* a &amp;quot;system&amp;quot; stored procedure.&amp;nbsp; It&amp;#39;s simply a stored procedure that is created by the SSIS Log Provider for SQL Server if it does not currently exist in the database you are writing to.&amp;nbsp; If the procedure does exist, it is not overwritten by the Log Provier for SQL Server.&amp;nbsp; Appropriate care should be taken not to mess up your procedure by introducing logic or other errors into the procedure.&amp;nbsp; On the other hand, if you do mess something up beyond repair the Log Provider will recreate the procedure if you drop it.&lt;/em&gt;&lt;/p&gt;&lt;/font&gt;&lt;/font&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=3012" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2005/default.aspx">SQL Server 2005</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/logging/default.aspx">logging</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/stored+procedures/default.aspx">stored procedures</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SSIS/default.aspx">SSIS</category></item><item><title>The Great SQL 2005 SP:CacheMiss-tery</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/08/09/the-great-sql-2005-sp-cachemiss-tery.aspx</link><pubDate>Wed, 09 Aug 2006 02:41:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:695</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=695</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=695</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/08/09/the-great-sql-2005-sp-cachemiss-tery.aspx#comments</comments><description>&lt;p&gt;OK, so I&amp;#39;m using SQL Server Profiler on SQL 2005 to determine for myself if the rules for stored procedure name resolution are the same as for SQL 2000.&amp;nbsp; So like any curious SQL programmer I jump in with a simple test.&amp;nbsp; I ran the following script in SQL 2000:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;CREATE PROCEDURE&lt;/font&gt; dbo.Test&lt;br /&gt;&lt;font color="#0000ff"&gt;AS&lt;/font&gt;&lt;br /&gt;&lt;font color="#0000ff"&gt;SELECT&lt;/font&gt; &amp;#39;Test&amp;#39;;&lt;br /&gt;GO&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;EXEC&lt;/font&gt; dbo.Test;&lt;br /&gt;GO&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;I then ran the &lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;EXEC&lt;/font&gt; dbo.Test&lt;/font&gt; line several times in a row.&amp;nbsp; The results were pretty much as I expected in SQL Profiler (abbreviated for brevity):&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;SP:CacheMiss&lt;br /&gt;SP:CacheInsert&lt;br /&gt;...&lt;br /&gt;SP:ExecContextHit&lt;br /&gt;...&lt;br /&gt;SP:ExecContextHit&lt;br /&gt;...&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;I then ran the exact same script in SQL 2005 with SQL Server Profiler running.&amp;nbsp; Like before I ran &lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;EXEC &lt;/font&gt;dbo.Test&lt;/font&gt; several times in a row.&amp;nbsp; The results were a little surprising:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;SP:CacheMiss&lt;br /&gt;SP:CacheInsert&lt;br /&gt;...&lt;br /&gt;SP:CacheMiss&lt;br /&gt;SP:CacheHit&lt;br /&gt;...&lt;br /&gt;SP:CacheMiss&lt;br /&gt;SP:CacheHit&lt;br /&gt;...&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;So why all the &lt;font face="Courier New"&gt;SP:CacheMiss&lt;/font&gt; events when we know that &lt;font face="Courier New"&gt;dbo.Test&lt;/font&gt; is in the cache?&amp;nbsp; It turns out that &lt;font face="Courier New"&gt;dbo.Test&lt;/font&gt; is in the cache, but the cost of an ad hoc query (a single &lt;font face="Courier New" color="#0000ff"&gt;SELECT&lt;/font&gt;, &lt;font face="Courier New" color="#0000ff"&gt;INSERT&lt;/font&gt;, &lt;font face="Courier New" color="#0000ff"&gt;UPDATE&lt;/font&gt; or &lt;font face="Courier New" color="#0000ff"&gt;DELETE&lt;/font&gt; statement - or apparently in this case an &lt;font face="Courier New" color="#0000ff"&gt;EXEC&lt;/font&gt; statement) is zero, so the &lt;font face="Courier New" color="#0000ff"&gt;EXEC&lt;/font&gt; by itself is not cached.&amp;nbsp; A more complex batch eliminates all of the &lt;font face="Courier New"&gt;SP:CacheMiss&lt;/font&gt; events:&lt;/p&gt;&lt;font size="2"&gt;&lt;font color="#0000ff" size="2"&gt;
&lt;p&gt;&lt;font face="Courier New" size="3"&gt;EXEC&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New" size="3"&gt; dbo&lt;font color="#808080"&gt;.&lt;/font&gt;Test&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="3"&gt;&lt;font color="#808080"&gt;;&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;SELECT&lt;/font&gt; &lt;font color="#0000ff"&gt;TOP&lt;/font&gt; 10 &lt;font color="#808080"&gt;*&lt;/font&gt; &lt;font color="#0000ff"&gt;FROM&lt;/font&gt; Person&lt;font color="#808080"&gt;.&lt;/font&gt;Contact&lt;/font&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;font size="3"&gt;&lt;font color="#808080"&gt;;&lt;br /&gt;&lt;/font&gt;GO&lt;/font&gt;&lt;/p&gt;&lt;/font&gt;&lt;/font&gt;&lt;font color="#808080" size="2"&gt;&lt;/font&gt;
&lt;p&gt;Putting a &lt;font face="Courier New" color="#0000ff"&gt;SELECT&lt;/font&gt; statement in the same batch as the &lt;font face="Courier New" color="#0000ff"&gt;EXEC&lt;/font&gt; eliminates the &lt;font face="Courier New"&gt;SP:CacheMiss&lt;/font&gt; events.&amp;nbsp;&amp;nbsp;Miss-tery solved!&amp;nbsp; Obviously this isn&amp;#39;t a cure-all or an option for every case, but it made me aware that the cost of an ad hoc query is zero which prevents it from being cached.&amp;nbsp; A batch with multiple statements, however, has a cost associated which causes SQL Server 2005 to cache the query plan.&lt;/p&gt;
&lt;p&gt;A couple of other things to know about the cache:&lt;/p&gt;
&lt;p&gt;- The cache is case-sensitive (regardless of your database or server collation).&amp;nbsp;&amp;nbsp;Using the name &lt;font face="Courier New"&gt;DBO.TEST&lt;/font&gt; will cause an &lt;font face="Courier New"&gt;SP:CacheInsert&lt;/font&gt; since we previously called it with &lt;font face="Courier New"&gt;dbo.Test&lt;/font&gt;.&lt;/p&gt;
&lt;p&gt;- The cache requires an exact match.&amp;nbsp; Even extra white-space in your queries will affect your cache performance and cause &lt;font face="Courier New" color="#000000"&gt;SP:CacheInsert&lt;font face="Times New Roman"&gt;s&lt;/font&gt;&lt;/font&gt;.&lt;/p&gt;
&lt;p&gt;- SQL 2005 has several improvements in caching including differences in &amp;quot;sufficient difference&amp;quot; and recompilation thresholds from SQL 2000, forced parameterization, parameter sniffing, etc.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=695" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/optimization/default.aspx">optimization</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/query+plans/default.aspx">query plans</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/cached/default.aspx">cached</category></item><item><title>DUDE!?  Where's my Upsert?</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/05/22/dude-where-s-my-upsert.aspx</link><pubDate>Mon, 22 May 2006 13:56:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:617</guid><dc:creator>Michael Coles</dc:creator><slash:comments>1</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=617</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=617</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/05/22/dude-where-s-my-upsert.aspx#comments</comments><description>&lt;p&gt;&lt;font size="2"&gt;&lt;font face="Tahoma"&gt;In the build-up to the release of SQL 2005, some great new features were introduced.&amp;nbsp; One of these was the implementation of the mythical &amp;quot;Upsert&amp;quot; (&amp;quot;Update or Insert&amp;quot;) statement.&amp;nbsp; ANSI SQL:2003 defines this as the&lt;/font&gt;&amp;nbsp;&lt;font face="Courier New"&gt;MERGE&lt;/font&gt;&lt;font face="Times New Roman"&gt;&lt;font face="Tahoma"&gt; statement.&amp;nbsp; When I saw this I&amp;nbsp;envisioned the end to convoluted upsert stored procedures that 1)&lt;/font&gt; &lt;font face="Courier New"&gt;SELECT&lt;/font&gt;&lt;font face="Tahoma"&gt; to verify if a row already exists in a table, 2) Uses an&lt;/font&gt; &lt;font face="Courier New"&gt;IF&lt;/font&gt;&lt;font face="Tahoma"&gt; statement to decide whether to&lt;/font&gt;&amp;nbsp;&lt;font face="Courier New"&gt;INSERT&lt;/font&gt;&lt;font face="Tahoma"&gt; or&lt;/font&gt; &lt;font face="Courier New"&gt;UPDATE&lt;/font&gt;&lt;/font&gt;&lt;font face="Tahoma"&gt; the row&amp;nbsp;depending on the&amp;nbsp;result of step 1.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Tahoma" size="2"&gt;Microsoft introduced a &lt;font face="Courier New"&gt;MERGE&lt;/font&gt; statement with the following syntax:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New" size="2"&gt;&lt;font color="#0000ff"&gt;MERGE INTO&lt;/font&gt;&amp;nbsp;DestinationTable&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;USING&amp;nbsp;&lt;/font&gt;SourceTable&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;ON &lt;/font&gt;SourceTable.ColumnX = DestinationTable.ColumnX&lt;br /&gt;&lt;font color="#0000ff"&gt;WHEN MATCHED THEN&lt;/font&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;UPDATE&amp;nbsp;&lt;/font&gt;ColumnY = SourceTable.ColumnY&lt;br /&gt;&lt;font color="#0000ff"&gt;WHEN NOT MATCHED THEN&lt;/font&gt;&lt;br /&gt;&lt;font color="#0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; INSERT VALUES&lt;/font&gt;(SourceTable.ColumnX, SourceTable.ColumnY)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font size="2"&gt;&lt;font face="Tahoma"&gt;Touted in several books and blogs prior to the RTM release, the&lt;/font&gt; &lt;font face="Courier New"&gt;MERGE&lt;/font&gt;&lt;font face="Tahoma"&gt; statement was unceremoniously dropped sometime prior to RTM.&amp;nbsp; It&amp;#39;s a shame to drop such a useful piece of functionality, already implemented in competing products (Oracle, DB2).&amp;nbsp; I wonder why it was dropped...&amp;nbsp; there doesn&amp;#39;t appear to be anything on Microsoft&amp;#39;s website explaining it.&amp;nbsp; Until they bring it back, it looks like it&amp;#39;s back to the&lt;/font&gt; &lt;font face="Courier New"&gt;SELECT...IF...UPDATE...ELSE...INSERT&lt;/font&gt;&lt;font face="Tahoma"&gt; method.&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=617" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL+Server+2005/default.aspx">SQL Server 2005</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/features/default.aspx">features</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/DML/default.aspx">DML</category></item><item><title>SQL Basics:  Part 2,  Nonclustered Indexes</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/05/15/sql-basics-part-2-nonclustered-indexes.aspx</link><pubDate>Mon, 15 May 2006 04:56:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:611</guid><dc:creator>Michael Coles</dc:creator><slash:comments>1</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=611</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=611</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/05/15/sql-basics-part-2-nonclustered-indexes.aspx#comments</comments><description>&lt;p&gt;&lt;font face="Arial"&gt;&lt;strong&gt;&lt;u&gt;Nonclustered Indexes&lt;/u&gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;If you caught the first article in the series, you know that clustered indexes are pretty handy.&amp;nbsp; But sometimes you need to rapidly retrieve data without physically re-ordering your table.&amp;nbsp; Going back to the clustered index analogy, some books (phone books, dictionaries, etc.) benefit from having their content ordered alphabetically -- but most do not.&amp;nbsp; To demonstrate, I&amp;#39;ve reorganized the words in Act I of Shakespeare&amp;#39;s &lt;em&gt;Hamlet&lt;/em&gt; in alphabetical order -- here are the first few words:&lt;/font&gt;&lt;/p&gt;
&lt;p align="center"&gt;&lt;img height="493" alt="Fig. 4. Act I of Shakespeare&amp;#39;s Hamlet in alphabetical order" src="http://www.thesqlfaq.com/SQLBasics1/Fig4.gif" width="331" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;As you can see it has the same words as the original, but it&amp;#39;s even harder to read (some of us might consider making Shakespeare harder to read a real feat).&amp;nbsp; Fortunately for us, some really smart guys came up with another method of indexing a long time ago.&amp;nbsp; The &amp;quot;book index&amp;quot; is an alphabetically ordered listing of words at the back of the book.&amp;nbsp; With a book index, you first locate the word you&amp;#39;re searching for in the list.&amp;nbsp; Each word has page numbers listed next to it indicating the pages on which it can be found.&amp;nbsp; You then turn the book to the referenced page(s) to read the sentences that contain the word in question.&amp;nbsp; SQL Server&amp;#39;s analogous indexing mechanism is called the &lt;strong&gt;nonclustered index&lt;/strong&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;Unlike a clustered index, nonclustered indexes are stored separately from the main table.&amp;nbsp; These indexes are not dependent on the physical ordering of the table.&amp;nbsp; Nonclustered indexes require a trade-off between storage and data retrieval efficiency.&amp;nbsp; Here are some of the factors to take into consideration:&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;font face="Arial"&gt;Clustered indexes are automatically added to all non-clustered indexes, creating what is called a &lt;strong&gt;covering index&lt;/strong&gt;.&amp;nbsp; A covering index helps reduce bookmark lookups as we’ll see below.&lt;/font&gt; 
&lt;li&gt;&lt;font face="Arial"&gt;Nonclustered indexes can take up considerably more space than a clustered index, since they represent copies of the specified columns from the table, plus the nonclustered index.&lt;/font&gt; 
&lt;li&gt;&lt;font face="Arial"&gt;You can also expect to encounter &amp;quot;&lt;strong&gt;bookmark lookups&lt;/strong&gt;&amp;quot; when using nonclustered indexes.&amp;nbsp; Considering the book index analogy, after you look up a word or phrase in the index you flip back in the book to the referenced page number to get the associated data.&amp;nbsp; A bookmark lookup is essentially SQL Server’s way of flipping back to the referenced row to get required data that was not included in the index itself.&amp;nbsp; Bookmark lookups can degrade performance, but sometimes this performance hit is better than the alternative of expanding your index.&amp;nbsp; This will be discussed further below. &lt;/font&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;font face="Arial"&gt;&lt;strong&gt;&lt;u&gt;The Sample&lt;/u&gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;We&amp;#39;ll create a small table to demonstrate indexes.&amp;nbsp; This will be a small product table for a retail store.&amp;nbsp; We will assume that 80% of the searches performed will be by product description and 20% will be by price.&amp;nbsp; Here&amp;#39;s the script to create the table with our indexes:&lt;/font&gt;&lt;/p&gt;
&lt;blockquote dir="ltr"&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;CREATE TABLE&lt;/font&gt; Products_Test&lt;br /&gt;(ean_upc&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;CHAR&lt;/font&gt;(13)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;NOT NULL PRIMARY KEY NONCLUSTERED&lt;/font&gt;,&lt;br /&gt;&amp;nbsp;prod_desc&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;VARCHAR&lt;/font&gt;(32)&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color="#0000ff"&gt;NOT NULL&lt;/font&gt;,&lt;br /&gt;&amp;nbsp;sell_price&amp;nbsp; &lt;font color="#0000ff"&gt;NUMERIC&lt;/font&gt;(10, 2) &lt;font color="#0000ff"&gt;NOT NULL&lt;/font&gt;,&lt;br /&gt;&amp;nbsp;qty_on_hand &lt;font color="#0000ff"&gt;INT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NOT NULL&lt;/font&gt;)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;CREATE CLUSTERED INDEX&lt;/font&gt; IX1 &lt;font color="#0000ff"&gt;ON&lt;/font&gt; Products_Test (prod_desc)&lt;br /&gt;&lt;font color="#0000ff"&gt;CREATE NONCLUSTERED INDEX&lt;/font&gt; IX2 &lt;font color="#0000ff"&gt;ON&lt;/font&gt; Products_Test(sell_price)&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;INSERT INTO&lt;/font&gt; Products_Test (ean_upc, prod_desc, sell_price, qty_on_hand)&lt;br /&gt;&lt;font color="#0000ff"&gt;SELECT&lt;/font&gt; &amp;#39;0884312039275&amp;#39;, &amp;#39;Shoes&amp;#39;, 16.99, 12&lt;br /&gt;&lt;font color="#0000ff"&gt;UNION SELECT&lt;/font&gt; &amp;#39;0024543223818&amp;#39;, &amp;#39;Shirt&amp;#39;, 14.99, 9&lt;br /&gt;&lt;font color="#0000ff"&gt;UNION SELECT&lt;/font&gt; &amp;#39;0883339064505&amp;#39;, &amp;#39;Pants&amp;#39;, 25.99, 24&lt;br /&gt;&lt;font color="#0000ff"&gt;UNION SELECT&lt;/font&gt; &amp;#39;0829845980625&amp;#39;, &amp;#39;Underwear&amp;#39;, 4.99, 11&lt;br /&gt;&lt;font color="#0000ff"&gt;UNION SELECT&lt;/font&gt; &amp;#39;0780978723230&amp;#39;, &amp;#39;Socks&amp;#39;, 5.99, 3&lt;br /&gt;&lt;font color="#0000ff"&gt;UNION SELECT&lt;/font&gt; &amp;#39;0623451012479&amp;#39;, &amp;#39;Tie&amp;#39;, 6.99, 13&lt;br /&gt;&lt;font color="#0000ff"&gt;UNION SELECT&lt;/font&gt; &amp;#39;0822342190603&amp;#39;, &amp;#39;Jacket&amp;#39;, 17.99, 6&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;font face="Arial"&gt;First notice that we used the &lt;font face="Courier New"&gt;ean_upc&lt;/font&gt; column (EAN/UPC product code) as the primary key.&amp;nbsp; Next we created a clustered index on the &lt;font face="Courier New" color="#000000"&gt;prod_desc&lt;/font&gt; column.&amp;nbsp; Finally we built a nonclustered index on &lt;font face="Courier New"&gt;sell_price&lt;/font&gt;.&amp;nbsp; &lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;We can test the performance of our queries against this table by looking at the &lt;strong&gt;query execution plans&lt;/strong&gt; SQL Server generates.&amp;nbsp; For a simple product search we get a query plan like the following:&lt;/font&gt;&lt;/p&gt;
&lt;p align="center"&gt;&lt;img height="445" alt="Fig. 5. SELECT on a clustered index" src="http://www.thesqlfaq.com/SQLBasics1/Fig5.gif" width="595" /&gt;&lt;/p&gt;&lt;font face="Arial"&gt;
&lt;p&gt;&lt;br /&gt;A &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; using the clustered index column in the &lt;font face="Courier New"&gt;WHERE&lt;/font&gt; clause generates a nice efficient clustered index seek.&amp;nbsp; So what happens when we &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; on the nonclustered index?&lt;/p&gt;
&lt;p align="center"&gt;&lt;/font&gt;&lt;img height="556" alt="Fig. 6. SELECT on a nonclustered index" src="http://www.thesqlfaq.com/SQLBasics1/Fig6.gif" width="595" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;Notice when we &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; on the nonclustered index, we get a Bookmark Lookup in the query plan.&amp;nbsp; The reason for this is simple:&amp;nbsp; we included included columns that are not part of our index in the &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; clause.&amp;nbsp; Specifically we included the ean_upc and qty_on_hand columns.&amp;nbsp; We can eliminate bookmark lookups a couple of ways: 1) extend the nonclustered index to include more columns, or 2) eliminate the columns that are not part of the index from the &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; clause.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;Consider the following revised &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; statement:&lt;/font&gt;&lt;/p&gt;
&lt;p align="center"&gt;&lt;img height="443" alt="Fig. 7. Revised SELECT on nonclustered index" src="http://www.thesqlfaq.com/SQLBasics1/Fig7.gif" width="603" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;The clustered index is automatically included in every nonclustered index.&amp;nbsp; This is a major reason for keeping your nonclustered indexes as narrow as possible.&amp;nbsp; This combination of clustered index with a nonclustered index creates a &lt;strong&gt;covering index&lt;/strong&gt;.&amp;nbsp; The revised &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; statement includes only columns that are part of the covering index, eliminating the Bookmark Lookups.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;As mentioned, nonclustered indexes represent a trade-off between storage and data retrieval efficiency.&amp;nbsp; For queries that are executed rarely and/or return few rows it might not be worth expanding your index to get rid of bookmark lookups.&amp;nbsp; For queries that are executed often and/or return large result sets, bookmark lookups can be performance killers.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial"&gt;Here are some considerations for nonclustered indexes:&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;font face="Arial"&gt;Nonclustered indexes are best for queries that return few rows and small ranges of data.&amp;nbsp; One of the reasons for this is the cost of bookmark lookups when columns that are not part of&amp;nbsp;the index are included in the &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; clause.&amp;nbsp; This can cause a considerable performance hit when returning large numbers of rows.&lt;/font&gt; 
&lt;li&gt;&lt;font face="Arial"&gt;Nonclustered indexes are best used in queries with very high &lt;strong&gt;selectivity&lt;/strong&gt;.&amp;nbsp; Selectivity is a&amp;nbsp;reference to the percentage of rows from a table that are returned by a query.&amp;nbsp; A low selectivity can result in SQL Server ignoring a nonclustered index, rendering it useless.&amp;nbsp; There are no hard and fast rules concerning what constitutes high selectivity, but a selectivity of 95% or higher will usually get you in the ball park.&lt;/font&gt;
&lt;li&gt;&lt;font face="Arial"&gt;Nonclustered indexes are good candidates when the indexed columns are included in both the &lt;font face="Courier New"&gt;WHERE&lt;/font&gt; clause and the &lt;font face="Courier New"&gt;ORDER BY&lt;/font&gt; clause.&amp;nbsp; The reason is that often a sort operation can be avoided since the index returns the items in indexed order.&lt;/font&gt; 
&lt;li&gt;&lt;font face="Arial"&gt;Although you should avoid &amp;quot;&lt;strong&gt;wide&lt;/strong&gt;&amp;quot; indexes, or indexes that include a large number of columns, sometimes it is unavoidable.&amp;nbsp; Nonclustered indexes are preferable to clustered indexes when a wide index is needed.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;font face="Arial"&gt;Don&amp;#39;t over-index a table.&amp;nbsp; An index is like a summary of the data in your table.&amp;nbsp; Indexing too many columns of a table defeats the purpose and can cause performance problems.&amp;nbsp; Indexes also have an associated cost for updates and inserts, since SQL Server has to update all associated indexes when it updates the table.&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=611" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/optimization/default.aspx">optimization</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/tutorial/default.aspx">tutorial</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/indexes/default.aspx">indexes</category></item><item><title>SQL Basics:  Part 1, Clustered Indexes</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/05/08/sql-basics-part-1-clustered-indexes.aspx</link><pubDate>Mon, 08 May 2006 04:56:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:599</guid><dc:creator>Michael Coles</dc:creator><slash:comments>1</slash:comments><description>&lt;p&gt;&lt;font face="Arial" size="2"&gt;I&amp;#39;ve found that many article writers (myself included) make a lot of assumptions about basic concepts.&amp;nbsp; I&amp;#39;ve also discovered that many times these assumptions leave some readers (especially newly minted DBA&amp;#39;s, developers and others just learning the trade) with a lot of unanswered questions.&amp;nbsp; This series will describe basic&amp;nbsp;concepts of SQL Server development and administration, with a focus on new&amp;nbsp;developers and DBA&amp;#39;s who often get overlooked in the rush to publish highly technical, cutting edge articles.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;&lt;font face="Arial" size="2"&gt;SQL Server Indexes&lt;/font&gt;&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;SQL Server provides indexing mechanisms to improve data retrieval efficiency.&amp;nbsp; Most SQL Server developers and admins are familiar with the term &amp;quot;&lt;em&gt;index&lt;/em&gt;&amp;quot;, but what is it really?&amp;nbsp; I&lt;/font&gt;&lt;font face="Arial" size="2"&gt;f you&amp;#39;ve ever been on a technical interview for a SQL Server job, you&amp;#39;re probably aware that SQL Server provides two types of indexes:&amp;nbsp; &lt;em&gt;clustered&lt;/em&gt; and &lt;em&gt;non-clustered&lt;/em&gt;.&amp;nbsp; You&amp;#39;ve probably been asked the difference between these two types of indexes.&amp;nbsp; So let&amp;#39;s begin by talking about clustered indexes.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;&lt;font face="Arial" size="2"&gt;Clustered Indexes&lt;/font&gt;&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;A clustered index defines the physical ordering of rows in your table.&amp;nbsp; The classic analogy of a clustered index is the layout of the phone book (white pages, of course).&amp;nbsp; The data in the phone book is listed in alphabetical order by name, so the names could be considered the phone book&amp;#39;s &amp;quot;clustered index.&amp;quot;&lt;/font&gt;&lt;/p&gt;
&lt;p align="center"&gt;&lt;font face="Arial" size="2"&gt;&lt;img height="265" alt="Fig. 1. White Pages as a Clustered Index" src="http://www.thesqlfaq.com/SQLBasics1/Fig1.png" width="367" /&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;The alphabetical ordering of the contents of the phone book helps users to rapidly locate specific entries.&amp;nbsp; Like the phone book, explicitly ordering a table by clustered index helps SQL Server rapidly locate relevant data when queried.&amp;nbsp; Because a table can only be physically ordered one way, only one clustered index can exist on a table.&amp;nbsp; In addition to increased search efficiency, properly defined clustered indexes can also eliminate some of the sort operations that might be generated internally by &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; statements with &lt;font face="Courier New"&gt;ORDER BY&lt;/font&gt; clauses, &lt;font face="Courier New"&gt;JOIN&lt;/font&gt;s and aggregate functions.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;The downside of clustered indexes is that SQL Server must physically re-order a table when a new row is inserted in the middle.&amp;nbsp; To expand on the example above, if the name &amp;quot;&lt;font face="Courier New"&gt;Aestra, W&lt;/font&gt;&amp;quot; were inserted, every item below it in the phone book would need to be physically shifted down one line in order to maintain the correct physical ordering.&amp;nbsp; This can cause performance issues with very large tables that require a lot of inserts.&lt;/font&gt;&lt;/p&gt;
&lt;p align="center"&gt;&lt;font face="Arial" size="2"&gt;&lt;img height="258" alt="Fig. 2. Inserting a New Row in the Phone Book" src="http://www.thesqlfaq.com/SQLBasics1/Fig2.png" width="365" /&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;One method of dealing with these performance issues is to adjust the &amp;quot;fill factor&amp;quot; for a clustered index.&amp;nbsp; SQL Server stores indexes in a B+ Tree structure, with the lowest level being the &amp;quot;leaf level.&amp;quot;&amp;nbsp; The fill factor determines the amount of free space to be left on the leaf level for future inserts.&amp;nbsp; By default this value is 0 (treated similarly to 100) indicating that the leaf level should be filled completely.&amp;nbsp; For tables with a few small regular updates, a fill factor of 90 might be more appropriate.&amp;nbsp; Tables with lots of larger updates might require lower fill factors like 80 or 70.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;There aren&amp;#39;t any hard and fast rules for setting fill factor, but a lower fill factor means SQL Server must traverse more pages to locate data, slowing down your &lt;font face="Courier New"&gt;SELECT&lt;/font&gt; queries.&amp;nbsp; I highly recommend keeping fill factors above 60.&amp;nbsp; The key is to find the right balance, which is best done through experimentation with your particular tables.&amp;nbsp; The fill factor can be changed via the &lt;strong&gt;Indexes/Keys&lt;/strong&gt; tab of the &lt;strong&gt;Table Properties&lt;/strong&gt; window in Enterprise Manager, or via the &lt;font face="Courier New"&gt;ALTER TABLE&lt;/font&gt; statement.&lt;br /&gt;&amp;nbsp;&lt;/font&gt;&lt;/p&gt;
&lt;p align="center"&gt;&lt;font face="Arial" size="2"&gt;&lt;img height="569" alt="Fig 3. FILL FACTOR Setting in Enterprise Manager" src="http://www.thesqlfaq.com/SQLBasics1/Fig3.png" width="412" /&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;Though not technically required on a table, I personally feel the benefits of clustered indexes far outweigh the drawbacks and I use them on all tables I design.&amp;nbsp; Some key factors to consider when deciding which columns to include in your clustered index are:&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Columns which are included in &lt;font face="Courier New"&gt;WHERE&lt;/font&gt; clauses the most often when querying a table often make a good candidate for clustered indexing. &lt;/font&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Columns that are part of &lt;font face="Courier New"&gt;ORDER BY&lt;/font&gt; or &lt;font face="Courier New"&gt;GROUP BY&lt;/font&gt; clauses in large queries (or queries on large tables) can make good clustered indexes. &lt;/font&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Queries that use &lt;font face="Courier New"&gt;JOIN&lt;/font&gt;s, and those that return large numbers of rows in the result set, can often benefit from clustered indexes on the underlying tables. &lt;/font&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Columns that are searched for a large range of values using &lt;font face="Courier New"&gt;WHERE&lt;/font&gt; clause operators such as &lt;font face="Courier New"&gt;&amp;gt;&lt;/font&gt;, &lt;font face="Courier New"&gt;&amp;lt;&lt;/font&gt;, &lt;font face="Courier New"&gt;&amp;lt;=&lt;/font&gt;, &lt;font face="Courier New"&gt;&amp;gt;=&lt;/font&gt;, &lt;font face="Courier New"&gt;BETWEEN&lt;/font&gt;, &lt;font face="Courier New"&gt;LIKE &amp;#39;&lt;em&gt;x&lt;/em&gt;%&amp;#39;&lt;/font&gt;, etc. tend to make excellent clustered indexes. &lt;/font&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Columns on which aggregate functions that require sorting such as &lt;font face="Courier New"&gt;MIN()&lt;/font&gt; and &lt;font face="Courier New"&gt;MAX()&lt;/font&gt; are used are good candidates for clustered indexes. &lt;/font&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Columns with high &amp;#39;cardinality&amp;#39; (a large number of distinct values) make the best clustered indexes.&amp;nbsp; Clustered indexes are wasted on columns with low cardinality, such as a single column restricted to &amp;quot;&lt;font face="Courier New"&gt;YES&lt;/font&gt;&amp;quot; and &amp;quot;&lt;font face="Courier New"&gt;NO&lt;/font&gt;&amp;quot; values. &lt;/font&gt;
&lt;li&gt;&lt;font face="Arial" size="2"&gt;Clustered indexes should be kept as lean as possible. Ideally a clustered index will be a single column, although the clustered index can contain up to 16 columns&amp;nbsp;with a total maximum length of 900 bytes.&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;A popular misconception is that the primary key of a table must also be a clustered index.&amp;nbsp; &lt;em&gt;(This also makes for a very good technical interview question.)&amp;nbsp;&lt;/em&gt; The fact of the matter is that this is simply not true.&amp;nbsp; A lot of confusion comes from one simple fact:&amp;nbsp; if no clustered index currently exists on a table and you create a primary key on that table, SQL Server makes the primary key a clustered index by default.&amp;nbsp; &lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;Often the primary key is the best candidate for the clustered index; however, a primary key can exist separately from a clustered index and it can be useful to separate the two.&amp;nbsp; For instance, in a table of products for a store where 99% of the queries are performed on a product description column, that column might make the most effective clustered index.&amp;nbsp; On the other hand, the primary key in such a table might be the UPC/EAN code or some other unique product identifier which is not queried as often.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;That&amp;#39;s the end of &lt;strong&gt;Part 1 - Clustered Indexes&lt;/strong&gt;. In the next part we&amp;#39;ll talk about non-clustered indexes.&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=599" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/SQL/default.aspx">SQL</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/optimization/default.aspx">optimization</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/tutorial/default.aspx">tutorial</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/indexes/default.aspx">indexes</category></item><item><title>SOX Relief?</title><link>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/04/28/sox-relief.aspx</link><pubDate>Fri, 28 Apr 2006 16:47:00 GMT</pubDate><guid isPermaLink="false">70975365-724d-4ce8-8d1c-45c963ab81ff:581</guid><dc:creator>Michael Coles</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.sqlservercentral.com/blogs/michael_coles/rsscomments.aspx?PostID=581</wfw:commentRss><wfw:comment>http://www.sqlservercentral.com/blogs/michael_coles/commentapi.aspx?PostID=581</wfw:comment><comments>http://www.sqlservercentral.com/blogs/michael_coles/archive/2006/04/28/sox-relief.aspx#comments</comments><description>&lt;p&gt;&lt;font face="Arial" size="2"&gt;Is SOX relief at hand?&amp;nbsp; An advisory panel to the SEC formally recommended that small companies be exempt from portions of Sarbanes-Oxley (SOX).&amp;nbsp; In particular they&amp;#39;re looking to provide an exemption to Section 404 of SOX, which requires an independent auditor to verify internal controls (&lt;/font&gt;&lt;a href="http://www.insidesarbanesoxley.com/2006/04/loosening-reporting-chains-on-business.asp"&gt;&lt;font face="Arial" size="2"&gt;http://www.insidesarbanesoxley.com/2006/04/loosening-reporting-chains-on-business.asp&lt;/font&gt;&lt;/a&gt;&lt;font face="Arial" size="2"&gt;).&amp;nbsp; According to the recommendation, companies that have a market capitalization of less than $128.2 million would not be required to hire an independent auditor.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;There&amp;#39;s been a lot of debate over Section 404 compliance, with anecdoctal evidence of companies going private rather than deal with compliance issues.&amp;nbsp; One report even suggests that companies are moving overseas to avoid compliance (&lt;/font&gt;&lt;a href="http://www.aeanet.org/governmentaffairs/AeASOXPaperFinal021005.asp"&gt;&lt;font face="Arial" size="2"&gt;http://www.aeanet.org/governmentaffairs/AeASOXPaperFinal021005.asp&lt;/font&gt;&lt;/a&gt;&lt;font face="Arial" size="2"&gt;).&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font face="Arial" size="2"&gt;Supporters of the&amp;nbsp;exemption suggest that the cost of external audits are prohibitive.&amp;nbsp; They also point out that companies with market capitalizations less than $128.2 million make up less than 10% of the total value of stocks listed on American Stock Markets.&amp;nbsp; So will small companies catch a break?&amp;nbsp; We&amp;#39;ll find out...&lt;/font&gt;&lt;/p&gt;&lt;img src="http://www.sqlservercentral.com/blogs/aggbug.aspx?PostID=581" width="1" height="1"&gt;</description><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/IT/default.aspx">IT</category><category domain="http://www.sqlservercentral.com/blogs/michael_coles/archive/tags/regulations/default.aspx">regulations</category></item></channel></rss>
