<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-3912586870549609682</atom:id><lastBuildDate>Fri, 29 May 2009 22:08:23 +0000</lastBuildDate><title>All Fired Up...</title><description>Advanced SQL Server discussions, articles and tools.</description><link>http://www.fotia.co.uk/fotia/Blog/AllFiredUp...html</link><managingEditor>noreply@blogger.com (Stefan Delmarco)</managingEditor><generator>Blogger</generator><openSearch:totalResults>31</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-336958889629647540</guid><pubDate>Fri, 29 May 2009 21:40:00 +0000</pubDate><atom:updated>2009-05-29T23:08:23.666+01:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sqlclr</category><title>StringFormat* and VARCHAR(MAX)</title><description>&lt;p&gt;The StringFormat* functions in the &lt;a href="http://fotiasqlclr.codeplex.com/" target="_blank"&gt;Fotia SQLCLR Library&lt;/a&gt; demonstrate some very interesting SQLCLR capabilities. In order to make the StringFormat* functions useful, they need to work with all data types being passed as arguments to the format. The natural C# solution is to use the object data type for the argument parameters:&lt;/p&gt;  &lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;   &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 21&lt;/span&gt; [&lt;span style="color: #2b91af"&gt;SqlFunction&lt;/span&gt;(DataAccess = &lt;span style="color: #2b91af"&gt;DataAccessKind&lt;/span&gt;.None, IsDeterministic = &lt;span style="color: blue"&gt;true&lt;/span&gt;)]&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 22&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;public&lt;/span&gt; &lt;span style="color: blue"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; FormatString2(&lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; format, &lt;span style="color: blue"&gt;object&lt;/span&gt; arg0, &lt;span style="color: blue"&gt;object&lt;/span&gt; arg1, &lt;span style="color: blue"&gt;object&lt;/span&gt; arg2)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 23&lt;/span&gt; {&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The .NET object maps to sql_variant, making the TSQL function prototype:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 1&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;create &lt;/span&gt;function dbo.FormatString2(@format nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;), @arg0 sql_variant, @arg1 sql_variant&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 2&lt;/span&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ,@arg2 sql_variant)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 3&lt;/span&gt; returns nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;However, sql_variant comes with its own limitations. It isn’t compatible will all data types, specifically those that can exceed 8,o16 bytes. The StringFormat* functions therefore do not work with the varchar(max)-family of data types. Given that StingFormat*’s primary purpose is to make string manipulation easier I’ve added the StringFormatMax* family of functions that take all their arguments as SqlString and use nvarchar(max) as their prototype:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 21&lt;/span&gt; [&lt;span style="color: #2b91af"&gt;SqlFunction&lt;/span&gt;(DataAccess = &lt;span style="color: #2b91af"&gt;DataAccessKind&lt;/span&gt;.None, IsDeterministic = &lt;span style="color: blue"&gt;true&lt;/span&gt;)]&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 22&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;public&lt;/span&gt; &lt;span style="color: blue"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; FormatStringMax2(&lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; format, &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; arg0, &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; arg1,&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 23&lt;/span&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; arg2)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 24&lt;/span&gt; {&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;TSQL prototype:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 5&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;create &lt;/span&gt;function dbo.FormatStringMax2(@format nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;), @arg0 nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;), @arg1 nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 6&lt;/span&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ,@arg2 nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;))&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 7&lt;/span&gt; returns nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;StringFormatMax* can handle all the varchar(max)-like data types. However, it loses the ability to perform any data type specific formatting (like float precision, padding, etc.) as the arguments are converted to strings by SQL before being passed to the StringFormatMax* functions.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-336958889629647540?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2009/05/stringformat-and-varcharmax.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-1275358477480695313</guid><pubDate>Sat, 23 May 2009 23:54:00 +0000</pubDate><atom:updated>2009-05-24T00:54:41.876+01:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sqlclr</category><title>Indispensable SQL CLR functions</title><description>&lt;p&gt;Since the first CTP of SQL Server 2005 I have been building up a library of SQL CLR utilities. Some were just experiments to find the limits of the technology. Others have had tremendous longevity and are part of my daily TSQL repertoire.&lt;/p&gt;  &lt;p&gt;The first SQLCLR function I’d like to share deals with a frustration all TSQL programmers have to face on a regular basis. In fact we’re so used to having to work around this obvious TSQL limitation we don’t even &lt;a href="http://en.wikipedia.org/wiki/Elephant_in_the_room" target="_blank"&gt;notice it&lt;/a&gt; anymore. I’m talking about string manipulation. &lt;/p&gt;  &lt;p&gt;Everything from simple formatting for reporting to building dynamic SQL (evil but sometimes necessary) highlights how clunky TSQL is when it comes to string manipulation. For example, given a float how difficult is it to output it with a thousands separator to 2 decimal places:&lt;/p&gt;  &lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;   &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 2&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;declare &lt;/span&gt;@some &lt;span style="color: blue"&gt;float&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 3&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;@some = &lt;span style="color: blue"&gt;rand&lt;/span&gt;() * &lt;span style="color: maroon"&gt;1E9&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 4&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select convert&lt;/span&gt;(&lt;span style="color: blue"&gt;varchar&lt;/span&gt;(&lt;span style="color: maroon"&gt;20&lt;/span&gt;), &lt;span style="color: blue"&gt;convert&lt;/span&gt;(&lt;span style="color: blue"&gt;money&lt;/span&gt;, @some), &lt;span style="color: maroon"&gt;1&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Building up a trivial dynamic SQL statement can be pretty difficult to debug with all the VB-style concatenation:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 7&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;declare &lt;/span&gt;@database nvarchar(&lt;span style="color: maroon"&gt;30&lt;/span&gt;), @columnFilter nvarchar(&lt;span style="color: maroon"&gt;30&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 8&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;@database = &lt;span style="background: #ffff80; color: fuchsia"&gt;'AdventureWorks'&lt;/span&gt;, @columnFilter = &lt;span style="background: #ffff80; color: fuchsia"&gt;'FirstName'&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160;&amp;#160; 9&lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 10&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;declare &lt;/span&gt;@sql nvarchar(&lt;span style="color: blue"&gt;max&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 11&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;@sql = &lt;span style="background: #ffff80; color: fuchsia"&gt;'select * from ' &lt;/span&gt;+ @database + &lt;span style="background: #ffff80; color: fuchsia"&gt;'.Person.Contact where ' &lt;/span&gt;+ @columnFilter + &lt;span style="background: #ffff80; color: fuchsia"&gt;' like @Value'&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 12&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;exec &lt;/span&gt;sp_executesql @sql, N&lt;span style="background: #ffff80; color: fuchsia"&gt;'@Value nvarchar(50)'&lt;/span&gt;, N&lt;span style="background: #ffff80; color: fuchsia"&gt;'Stef%'&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;There is a better way. With a very simple CLR function we can bring all the power of C#-style string manipulation inline with TSQL. For example:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 15&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;@sql = dbo.FormatString1(&lt;span style="background: #ffff80; color: fuchsia"&gt;'select * from {0}.Person.Contact where {1} like @Value'&lt;/span&gt;, @database, @columnFilter);&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 16&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;exec &lt;/span&gt;sp_executesql @sql, N&lt;span style="background: #ffff80; color: fuchsia"&gt;'@Value nvarchar(50)'&lt;/span&gt;, N&lt;span style="background: #ffff80; color: fuchsia"&gt;'Stef%'&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;A bunch more examples where TSQL string handling is much improved:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 19&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;-- Date formatting is easy...&lt;/span&gt;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 20&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;dbo.FormatString0(&lt;span style="background: #ffff80; color: fuchsia"&gt;'{0:dd MMM yyyy}'&lt;/span&gt;, &lt;span style="color: blue"&gt;getdate&lt;/span&gt;());&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 21&lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 22&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;-- Can create templates and keep reusing them. Format and args&lt;/span&gt;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 23&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;-- can be separated (not possible with TSQL).&lt;/span&gt;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 24&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;declare &lt;/span&gt;@template &lt;span style="color: blue"&gt;varchar&lt;/span&gt;(&lt;span style="color: maroon"&gt;500&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 25&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;set &lt;/span&gt;@template = &lt;span style="background: #ffff80; color: fuchsia"&gt;'Order {0} shipped at {1:dd MMM yyyy} for Account {2}, Total Due {3:N2}'&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 26&lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 27&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;dbo.FormatString3(@template, SalesOrderID, ShipDate, AccountNumber, TotalDue)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 28&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;from &lt;/span&gt;Sales.SalesOrderHeader&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 29&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;where &lt;/span&gt;SalesOrderId = &lt;span style="color: maroon"&gt;44314&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 30&lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 31&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;-- Custom formatting is simple, try do this as succinctly in TSQL:&lt;/span&gt;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 32&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;dbo.FormatString0(&lt;span style="background: #ffff80; color: fuchsia"&gt;'{0:0.00%;(0.00)%}'&lt;/span&gt;,-&lt;span style="color: maroon"&gt;0.1567&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 33&lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 34&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;-- Padding is all in the format string....&lt;/span&gt;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 35&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;dbo.FormatString0(&lt;span style="background: #ffff80; color: fuchsia"&gt;'{0,20}'&lt;/span&gt;, LastName)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 36&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;from &lt;/span&gt;Person.Contact&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 37&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;where &lt;/span&gt;FirstName &lt;span style="color: blue"&gt;like &lt;/span&gt;&lt;span style="background: #ffff80; color: fuchsia"&gt;'ste%'&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 38&lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 39&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;-- The thousands separator example from earlier...&lt;/span&gt;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 40&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;declare &lt;/span&gt;@some &lt;span style="color: blue"&gt;float&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 41&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;@some = &lt;span style="color: blue"&gt;rand&lt;/span&gt;() * &lt;span style="color: maroon"&gt;1E9&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 42&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;select &lt;/span&gt;dbo.FormatString0(&lt;span style="background: #ffff80; color: fuchsia"&gt;'{0:N2}'&lt;/span&gt;, @some);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The source code is pretty trivial:&lt;/p&gt;

&lt;div style="margin-top: 1em; font-family: courier new; margin-bottom: 1em; background: white; color: black; font-size: 8pt"&gt;
  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 15&lt;/span&gt; [&lt;span style="color: #2b91af"&gt;SqlFunction&lt;/span&gt;(DataAccess = &lt;span style="color: #2b91af"&gt;DataAccessKind&lt;/span&gt;.None)]&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 16&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;public&lt;/span&gt; &lt;span style="color: blue"&gt;static&lt;/span&gt; &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; FormatString1(&lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt; format, &lt;span style="color: blue"&gt;object&lt;/span&gt; arg0, &lt;span style="color: blue"&gt;object&lt;/span&gt; arg1)&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 17&lt;/span&gt; {&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 18&lt;/span&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; format.IsNull ? &lt;span style="color: #2b91af"&gt;SqlString&lt;/span&gt;.Null : &lt;span style="color: blue"&gt;string&lt;/span&gt;.Format(format.Value, SqlTypeToNetType(arg0, arg1));&lt;/pre&gt;

  &lt;pre style="line-height: 130%; margin: 0px"&gt;&lt;span style="background: silver"&gt;&amp;#160;&amp;#160; 19&lt;/span&gt; }&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Some points to note about the source code: &lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The SqlTypes don’t have any overloads in their ToString implementations which makes custom formatting impossible. To work around this limitation I convert the SqlType to a .NET underlying type.&amp;#160; This is the purpose of the SqlTypeToNetType function. It extracts the .Value property from each of the SqlTypes. &lt;/li&gt;

  &lt;li&gt;Unlike stored procedures, UDF parameters are not optional. Instead of having a single StringFormat function that takes the most number of args I’ll ever need (which will be NULL in most places) I have 4 UDFs: StringFormat0/1/2/3. &lt;/li&gt;

  &lt;li&gt;I’ve created a project on &lt;a href="http://www.codeplex.com/" target="_blank"&gt;CodePlex&lt;/a&gt; called &lt;a href="http://fotiasqlclr.codeplex.com/" target="_blank"&gt;Fotia SQLCLR Library&lt;/a&gt;. The full source code is available as well as a SQL script that will deploy the entire library (binaries not required). In the coming weeks I’ll be adding many more very useful SQL CLR functions and user-defined aggregates. &lt;/li&gt;
&lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-1275358477480695313?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2009/05/indispensable-sql-clr-functions.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-7919049480691442739</guid><pubDate>Fri, 01 Feb 2008 01:10:00 +0000</pubDate><atom:updated>2008-02-04T23:15:21.982Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>partitioned views</category><title>Partitioned Views on a Budget</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;&lt;a title="Partitioned Views" href="http://www.fotia.co.uk/fotia/FA.02.Sql2KPartitionedViews.01.aspx"&gt;Partitioned views&lt;/a&gt; are a great feature of SQL Server. By injecting an additional layer of abstraction between your logical view and physical implementation they give you fine grained control over data partitioning. However, the cover charge required for entry to this exclusive club is pretty &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190019.aspx"&gt;high&lt;/a&gt;. The checklist required to implement a partitioned view can often interfere with your design to such a degree that they become more of a hindrance than a benefit. However, by decomposing the features SQL Server uses to implement the complete partitioned views specification we can get most of the benefits without meeting all of the criteria.&lt;/p&gt; &lt;/font&gt;  &lt;p&gt;One of the benefits of partitioned views is the query optimizer's ability to determine which subset of the member tables need to be queried for each access to the partitioned view. The SQL Server query optimizer has clever features built into it that are able to understand variables and constants in TSQL and relate those constants to search predicates. The optimizer is able to use that insight to short-circuit query plan execution and not bother with branches that will never execute. For example:&lt;/p&gt; &lt;/font&gt;&lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @constant &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @constant = 0;       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#696969"&gt;*&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; 1 &lt;font color="#696969"&gt;= &lt;/font&gt;@constant; &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;The query plan for this bit of TSQL looks like this:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.fotia.co.uk/fotia/Blog/Images/200802.StartupExpression.png" /&gt;&lt;/p&gt;  &lt;p&gt;The SQL Server query optimizer has introduced a filter into the query plan that has a special property called a Startup Expression Predicate. This property is the short-circuit logic that determines whether or not the clustered index scan on the Person.Contact table executes or not. If the startup expression evaluates to FALSE then the optimizer doesn't bother executing any portion of that query. In this example, SQL Server will do nothing when @constant = 1 evaluates to FALSE. It is the exact same technology that partitioned views use to determine which underlying member tables to access based on the member tables' check constraints / partitioning columns.&lt;/p&gt;  &lt;p&gt;For example, consider a situation where I would like to query a table that exists in a number of databases. I would like to have a partitioned view that aggregates these member tables in a single view. However, I don't want to pay the penalty of having to query every member table for every query. If I specify the database name in the SELECT query, I want the optimizer to use its short-circuit feature to go directly to the one table that will satisfy the search arguments. The solution is easier than you think!&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create database&lt;/font&gt; Database01;       &lt;br /&gt;&lt;font color="#0000ff"&gt;create database&lt;/font&gt; Database02;       &lt;br /&gt;&lt;font color="#0000ff"&gt;create database&lt;/font&gt; Database10;       &lt;br /&gt;&lt;font color="#0000ff"&gt;create database&lt;/font&gt; Database11;       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; Database01       &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; MemberTable (       &lt;br /&gt;&amp;#160;&amp;#160; Col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(10));       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; Database02       &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; MemberTable (       &lt;br /&gt;&amp;#160;&amp;#160; Col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(10));       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; Database10       &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; MemberTable (       &lt;br /&gt;&amp;#160;&amp;#160; Col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(10));       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; Database11       &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; MemberTable (       &lt;br /&gt;&amp;#160;&amp;#160; Col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(10));       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; Database01.dbo.MemberTable &lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'Data01'&lt;/font&gt;);       &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; Database02.dbo.MemberTable &lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'Data02'&lt;/font&gt;);       &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; Database10.dbo.MemberTable &lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'Data10'&lt;/font&gt;);       &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; Database11.dbo.MemberTable &lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'Data11'&lt;/font&gt;);       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; tempdb;       &lt;br /&gt;go       &lt;br /&gt;&lt;font color="#0000ff"&gt;create view&lt;/font&gt; BudgetView &lt;font color="#0000ff"&gt;as&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff0000"&gt;'Database01'&lt;/font&gt; &lt;font color="#0000ff"&gt;as&lt;/font&gt; SourceDatabase, Col1       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Database01.dbo.MemberTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;union all &lt;/font&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff0000"&gt;'Database02'&lt;/font&gt;, Col1       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Database02.dbo.MemberTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;union all&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff0000"&gt;'Database10'&lt;/font&gt;, Col1       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Database10.dbo.MemberTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;union all        &lt;br /&gt;select&lt;/font&gt; &lt;font color="#ff0000"&gt;'Database11'&lt;/font&gt;, Col1       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Database11.dbo.MemberTable       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; BudgetView;       &lt;br /&gt;      &lt;br /&gt;      &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;SourceDatabase&lt;/td&gt;            &lt;td&gt;Col1&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;--------------&lt;/td&gt;            &lt;td&gt;----------&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database01&lt;/td&gt;            &lt;td&gt;Data01&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database02&lt;/td&gt;            &lt;td&gt;Data02&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database10&lt;/td&gt;            &lt;td&gt;Data10&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database11&lt;/td&gt;            &lt;td&gt;Data11&lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;      &lt;p&gt;&lt;b&gt;(4 rows(s) affected)&lt;/b&gt;&lt;/p&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;The query plan for a SELECT of all of the rows shows SQL accessing each member table in turn:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.fotia.co.uk/fotia/Blog/Images/200802.TableScans.PNG" /&gt;&lt;/p&gt;  &lt;p&gt;This is the clever bit. We've put the partitioning criteria that separates each member table into the view's definition. We've hardcoded the database name of each member table into the first column of the view. The really clever bit is that SQL Server understands this! Try the following:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; BudgetView       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; SourceDatabase =&lt;font color="#ff0000"&gt; 'Database01'&lt;/font&gt;;       &lt;br /&gt;      &lt;br /&gt;      &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;SourceDatabase&lt;/td&gt;            &lt;td&gt;Col1&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;--------------&lt;/td&gt;            &lt;td&gt;----------&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database01&lt;/td&gt;            &lt;td&gt;Data01&lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;      &lt;p&gt;&lt;b&gt;(1 row(s) affected)&lt;/b&gt;&lt;/p&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;SQL Server has looked at the search argument we specified for SourceDatabase and short-circuited the query plan. It &lt;b&gt;only&lt;/b&gt; queried Database01's MemberTable and didn't bother accessing any of the others!&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.fotia.co.uk/fotia/Blog/Images/200802.SingleTable.PNG" /&gt;&lt;/p&gt;  &lt;p&gt;It gets even better, SQL Server understands all manner of comparisons, including LIKEs, in this example it only access databases where the name starts with &lt;strong&gt;Database0&lt;/strong&gt;.&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; BudgetView       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; SourceDatabase like &lt;font color="#ff0000"&gt;'Database0%'&lt;/font&gt;;       &lt;br /&gt;      &lt;br /&gt;      &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;SourceDatabase&lt;/td&gt;            &lt;td&gt;Col1&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;--------------&lt;/td&gt;            &lt;td&gt;----------&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database01&lt;/td&gt;            &lt;td&gt;Data01&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database02&lt;/td&gt;            &lt;td&gt;Data02&lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;      &lt;p&gt;&lt;b&gt;(2 row(s) affected)&lt;/b&gt;&lt;/p&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;&lt;img src="http://www.fotia.co.uk/fotia/Blog/Images/200802.DualTable.PNG" /&gt;&lt;/p&gt;  &lt;p&gt;The story with parameters is also good! The startup filters we saw earlier make an appearance: &lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @sourceDatabase &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(20);       &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @sourceDatabase = &lt;font color="#ff0000"&gt;'Database10'&lt;/font&gt;;       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; dbo.BudgetView       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; SourceDatabase = @SourceDatabase;       &lt;br /&gt;      &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;SourceDatabase&lt;/td&gt;            &lt;td&gt;Col1&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;--------------&lt;/td&gt;            &lt;td&gt;----------&lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;Database10&lt;/td&gt;            &lt;td&gt;Data10&lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;      &lt;p&gt;&lt;b&gt;(1 row(s) affected)&lt;/b&gt;&lt;/p&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;&lt;img src="http://www.fotia.co.uk/fotia/Blog/Images/200802.VariableFilter.PNG" /&gt;&lt;/p&gt;  &lt;p&gt;At execution time SQL Server will dynamically determine which branches of the query plan to short-circuit. This is great as it allows a single query plan to be re-used for any value of @sourceDatabase and still deliver the short-circuits benefits.&lt;/p&gt;  &lt;p&gt;We've managed to create a view that has the same short-circuit benefits as a pukka partitioned view without having to adhere to all the partitioned view requirements. Our base tables don't have primary keys defined and there are no check constraints. On the downside our view is not updateable, it cannot be inserted into, updated or deleted. If you need all of these features you can either implement INSTEAD OF triggers or go with fully-fledged partitioned views. In this example we used the partitioning column to identify the source database name. Constant partitioning columns are also useful for soft-delete / archive tables, coarse categories, or chunky time periods.&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-7919049480691442739?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2008/02/partitioned-views-on-budget.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-8985261877128987891</guid><pubDate>Mon, 23 Apr 2007 07:13:00 +0000</pubDate><atom:updated>2008-02-04T23:13:49.697Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The NEWSEQUENTIALID Function</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;The NEWSEQUENTIALID system function is an addition to SQL Server 2005. It seeks to bring together, what used to be, conflicting requirements in SQL Server 2000; namely identity-level insert performance and globally unique values. &lt;/p&gt;    &lt;h2&gt;Insert Performance and Uniqueness&lt;/h2&gt;    &lt;p&gt;The best insert performance will always be achieved by using an identity column as the clustering key. With a monotonically increasing identity column as the clustered index, data is always inserted at the end of the table. At a basic level this is equivalent to the performance of a table that has no clustered index (i.e. a heap) where the insert position for the next row is the page where the last row was inserted. Secondly, there will be no page splits. With a normal clustered index, SQL Server will move half the rows on a full page to a newly allocated page in order to make space for the new row. With an identity column on a clustered index SQL Server will not 'split' the page in this manner. It will just allocate another page and continue inserting rows. It has enough smarts to know not to copy half the rows to the newly allocated page as all new rows will be inserted into the new page. &lt;/p&gt;    &lt;p&gt;The limitations of identity columns come to the fore when you need have unique keys that span either tables, databases or SQL Servers. For example, say you have multiple branches that can each generate orders. Each branch has a replicated copy of the Orders table, however you don't want duplicate order numbers generated across branches. Common solutions to this problem include creating a composite order number that includes the branch id, reserving ranges of the identity column or making use of a central 'number fountain'. Each of these approaches introduce a level of indirection that must be adhered to before a row can be created. If the order number is not required to be human-readable friendly, GUIDs are often used instead. &lt;/p&gt;    &lt;h2&gt;GUIDs&lt;/h2&gt;    &lt;p&gt;GUIDs (Globally Unique Identifiers) are pseudo-random 16-byte (128-bit) numbers whose generation algorithm provides a sufficiently high degree of probability that the same GUID will never be generated twice on any computer at any time. Although a GUID is a binary array, it is often represented in its hexadecimal form using the following format, for example: &lt;font style="font-weight: bold"&gt;dbbc2827-edf8-4a2d-92ad-c1e0059304d7&lt;/font&gt;. This makes them suitable for the 'distributed uniqueness requirement' of our Orders table. &lt;/p&gt;    &lt;p&gt;The problem would be pretty much solved there unless we have some non-trivial performance requirements, especially around insert volumes and speed. As GUIDs are effectively pseudo-random (the degree of randomness / predictability was changed by Microsoft in response to privacy concerns) they do not possess the same orderly insert benefits as identity columns when used as the clustering key. The insert IO pattern changes from sequential to random-access when using GUIDs instead of an IDENTITY column. Not only do GUIDs require far more IO to locate the insert location in the clustered index (the index has to be traversed from the root for every insert), fragmentation at the leaf level leads to less dense pages, which in turn requires defragmentation / reindexing. &lt;/p&gt;    &lt;h2&gt;NEWSEQUENTIALID()&lt;/h2&gt;    &lt;p&gt;Enter NEWSEQUENTIALID(), stage left. This is a new system function included in SQL Server 2005 that combines the best of both worlds. NEWSEQUENTIALID() will generate a GUID that will be greater in value than the previously generated one. &lt;/p&gt;    &lt;p&gt;It is easy to see how this function works by looking at the output the following TSQL script generates: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; TestTable (         &lt;br /&gt;&amp;#160;&amp;#160; id &lt;font color="#0000ff"&gt;uniqueidentifier default&lt;/font&gt; &lt;font color="#ff00ff"&gt;newsequentialid&lt;/font&gt;() &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; sequence &lt;font color="#0000ff"&gt;int&lt;/font&gt;);         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Insert 100 rows.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @count &lt;font color="#0000ff"&gt;int&lt;/font&gt;;         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @count = 0;         &lt;br /&gt;&lt;font color="#0000ff"&gt;while&lt;/font&gt; @count &amp;lt; 100 &lt;font color="#0000ff"&gt;begin&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; TestTable (sequence)         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;values&lt;/font&gt; (@count);         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @count = @count + 1;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; TestTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; id;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;id&lt;/td&gt;              &lt;td&gt;sequence&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------------------------------&lt;/td&gt;              &lt;td&gt;-----------&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;FA780E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;0&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;FB780E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;1&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;FC780E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;2&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;FD780E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;3&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;FE780E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;4&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;...&lt;/td&gt;              &lt;td&gt;&amp;#160;&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;5C790E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;98&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;5D790E3B-03C2-DB11-BD9F-0011D82F3F23&lt;/td&gt;              &lt;td&gt;99&lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;You'll notice that there is a clear bit-shifting pattern that SQL Server is employing to keep the GUIDs increasing in relative value. These are the restrictions in NEWSEQUENTIALID's use: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;NEWSEQUENTIALID cannot be used in arbitrary TSQL statements. It can only be specified as the DEFAULT value for an uniqueidentifier column &lt;/li&gt;      &lt;li&gt;More than one column in a table can use NEWSEQUENTIALID &lt;/li&gt;      &lt;li&gt;It cannot be combined with other scalar functions e.g. REVERSE(NEWSEQUENTIALID()) is not allowed &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;The network card's MAC address is easily identifiable in the generated GUID. My laptop's MAC address is&lt;/p&gt;    &lt;div class="tsql"&gt;&amp;#160;&amp;#160; 00-01-4A-28-64-8B&lt;/div&gt;    &lt;p&gt;This is one of the NEWSEQUENTIALID GUIDs that was generated:&lt;/p&gt;    &lt;div class="tsql"&gt;&amp;#160;&amp;#160; 4EAC7708-30C3-DB11-B902-&lt;font style="font-weight: bold; text-decoration: underline"&gt;00014A28648B&lt;/font&gt;&lt;/div&gt;    &lt;p&gt;Hence the privacy warnings in BOL:&lt;/p&gt;   &lt;img class="pix" alt="NEWSEQUENTIALID() Privacy Warning" src="/fotia/Blog/Images/200704.NewSequentialIdPrivacy.PNG" /&gt;     &lt;p&gt;Correlating the position of the MAC address in the GUID with the bits that get incremented / shifted for each successive GUID sheds light on why the GUIDS remain globally unique. The MAC address portion of the GUID does not change. &lt;/p&gt;    &lt;h2&gt;Performance Comparison&lt;/h2&gt;    &lt;p&gt;The following test demonstrates the benefits of NEWSEQUENTIALID. The insert performance of the various clustering key variations was measured and compared. For each test the same TestTable was recreated with a different Id column definition: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;IDENTITY(,) &lt;/li&gt;      &lt;li&gt;NEWID() &lt;/li&gt;      &lt;li&gt;NEWSEQUENTIALID() &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;The following TSQL contains the DDL used to construct the tables for the 3 clustered keys: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- IDENTITY&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; TestTable (         &lt;br /&gt;&amp;#160;&amp;#160; id &lt;font color="#0000ff"&gt;int identity&lt;/font&gt;(1,1) &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; sequence &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; data &lt;font color="#0000ff"&gt;char&lt;/font&gt;(250) &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;default&lt;/font&gt; &lt;font color="#ff0000"&gt;''&lt;/font&gt;);         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- NEWID&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; TestTable (         &lt;br /&gt;&amp;#160;&amp;#160; id &lt;font color="#0000ff"&gt;uniqueidentifier default&lt;/font&gt; &lt;font color="#ff00ff"&gt;newid&lt;/font&gt;() not null &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; sequence &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; data &lt;font color="#0000ff"&gt;char&lt;/font&gt;(250) &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;default&lt;/font&gt; &lt;font color="#ff0000"&gt;''&lt;/font&gt;);         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- NEWSEQUENTIALID&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; TestTable (         &lt;br /&gt;&amp;#160;&amp;#160; id &lt;font color="#0000ff"&gt;uniqueidentifier default&lt;/font&gt; &lt;font color="#ff00ff"&gt;newsequentialid&lt;/font&gt;() not null &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; sequence &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; data &lt;font color="#0000ff"&gt;char&lt;/font&gt;(250) &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;default&lt;/font&gt; &lt;font color="#ff0000"&gt;''&lt;/font&gt;);         &lt;br /&gt;go         &lt;br /&gt;&lt;/div&gt;      &lt;p&gt;For each test 50,000 rows were inserted into TestTable table using the following TSQL script: &lt;/p&gt;      &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Insert 50,000 rows.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @count &lt;font color="#0000ff"&gt;int&lt;/font&gt;;         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @count = 0;         &lt;br /&gt;&lt;font color="#0000ff"&gt;while&lt;/font&gt; @count &amp;lt; 50000 &lt;font color="#0000ff"&gt;begin&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; TestTable (sequence)         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;values&lt;/font&gt; (@count);         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @count = @count + 1;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;;         &lt;br /&gt;go         &lt;br /&gt;&lt;/div&gt;      &lt;p&gt;The following metrics were gathered after each run: &lt;/p&gt;      &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Get the number of read / writes for this session...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; reads, writes         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#0000ff"&gt;sys.dm_exec_sessions&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; session_id = &lt;font color="#ff00ff"&gt;@@spid&lt;/font&gt;;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Get the page fragmentation and density at the leaf level.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; index_type_desc, index_depth, page_count, avg_page_space_used_in_percent, avg_fragmentation_in_percent, record_count         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; sys.dm_db_index_physical_stats(&lt;font color="#ff00ff"&gt;db_id&lt;/font&gt;(), &lt;font color="#ff00ff"&gt;object_id&lt;/font&gt;(&lt;font color="#ff0000"&gt;'TestTable'&lt;/font&gt;), &lt;font color="#696969"&gt;null&lt;/font&gt;, &lt;font color="#696969"&gt;null&lt;/font&gt;, &lt;font color="#ff0000"&gt;'detailed'&lt;/font&gt;)         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; index_level = 0;         &lt;br /&gt;go         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The results are quite compelling:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;       &lt;table class="data"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;th&gt;&amp;#160;&lt;/th&gt;              &lt;th&gt;Reads&lt;/th&gt;              &lt;th&gt;Writes&lt;/th&gt;              &lt;th&gt;Leaf Pages&lt;/th&gt;              &lt;th&gt;Avg Page Used&lt;/th&gt;              &lt;th&gt;Avg Fragmentation&lt;/th&gt;              &lt;th&gt;Record Count&lt;/th&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;th&gt;IDENTITY(,)&lt;/th&gt;              &lt;td&gt;0&lt;/td&gt;              &lt;td&gt;1,683&lt;/td&gt;              &lt;td&gt;1,667&lt;/td&gt;              &lt;td&gt;98.9%&lt;/td&gt;              &lt;td&gt;0.7%&lt;/td&gt;              &lt;td&gt;50,000&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;th&gt;NEWID()&lt;/th&gt;              &lt;td&gt;0&lt;/td&gt;              &lt;td&gt;5,386&lt;/td&gt;              &lt;td&gt;2,486&lt;/td&gt;              &lt;td&gt;69.3%&lt;/td&gt;              &lt;td&gt;99.2%&lt;/td&gt;              &lt;td&gt;50,000&lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;th&gt;NEWSEQUENTIALID()&lt;/th&gt;              &lt;td&gt;0&lt;/td&gt;              &lt;td&gt;1,746&lt;/td&gt;              &lt;td&gt;1,725&lt;/td&gt;              &lt;td&gt;99.9%&lt;/td&gt;              &lt;td&gt;1.0%&lt;/td&gt;              &lt;td&gt;50,000&lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Most striking is the number of writes required by the NEWID system function. This, coupled with the average page density of 69%, is evidence of the page splitting caused by the random distribution of inserts at the leaf level. As soon as a page fills up, it needs to be split into 2 pages of 50% each for the insert to complete. Not only has page splitting resulted in poor page density, it has fragmented the data pages quite badly (there is a 99% probability that the next data page is not next to the current one). In our tests the most likely place for a free page required for the page split is at the end of the table irrespective of where the row is being inserted. Therefore to read the rows in order the scan needs to keep jumping back and forth between widely distributed split pages, hence the appalling fragmentation. &lt;/p&gt;    &lt;p&gt;The minor difference in page count between IDENTITY and NEWSEQUENTIALID metrics is due to the difference in size between IDENTITY's INT (4 bytes) and NEWSEQUENTIALID's UNQUEIDENTIFIER (16 bytes). However, note that any non-clustered index on the UNIQUEIDENTIFIER table will be 4 times larger than the same index on the IDENTITY table. Therefore IDENTITY is still preferred over NEWSEQUENTIALID when choosing between the two. &lt;/p&gt;    &lt;p&gt;The NEWSEQUENTIALID system function has clearly lived up to its claim of providing GUID-like uniqueness coupled with identity-level insert performance. The number of writes, fragmentation and page density are all inline with identity-level metrics. These benefits make NEWSEQUENTIALID a compelling feature. &lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-8985261877128987891?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2007/04/newsequentialid-function.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-6549536543849322791</guid><pubDate>Mon, 12 Mar 2007 23:51:00 +0000</pubDate><atom:updated>2008-02-04T23:12:19.707Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>.net</category><title>When a String Is Not a String</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;This article was inspired by a seemingly innocent bit of code that caused a performance problem in a very unexpected way. It is the kind of code that you'd find in many samples. Unfortunately, it has a potential flaw that is only exposed when large data volumes are present. &lt;/p&gt;    &lt;h2&gt;The Problem Code&lt;/h2&gt;    &lt;p&gt;The Data Access Layer (DAL) code in question is as follows: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;public static class&lt;/font&gt; &lt;font color="#008b8b"&gt;AccountDal&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public static string&lt;/font&gt; GetTerritoryForAccount(string accountNumber)         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;using&lt;/font&gt;(&lt;font color="#008b8b"&gt;SqlConnection&lt;/font&gt; con = &lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;SqlConnection&lt;/font&gt;(&lt;font color="#008b8b"&gt;@&amp;quot;Server=localhost;Database=AdventureWorks;          &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Integrated Security=SSPI&amp;quot;&lt;/font&gt;))         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;using&lt;/font&gt;(&lt;font color="#008b8b"&gt;SqlCommand&lt;/font&gt; cmd = con.CreateCommand())         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cmd.CommandText = &lt;font color="#008b8b"&gt;@&amp;quot;          &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; select t.Name           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; from Sales.Customer c           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; inner join Sales.SalesTerritory t           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; on c.TerritoryID = t.TerritoryId           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; where c.AccountNumber = @AccountNumber&amp;quot;&lt;/font&gt;;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cmd.CommandType = &lt;font color="#008b8b"&gt;CommandType&lt;/font&gt;.Text;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cmd.Parameters.AddWithValue(&lt;font color="#008b8b"&gt;&amp;quot;@AccountNumber&amp;quot;&lt;/font&gt;, accountNumber);         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; con.Open();         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; cmd.ExecuteScalar() &lt;font color="#0000ff"&gt;as string&lt;/font&gt;;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Very simple stuff. Open a connection to SQL, select a specific row from the Sales.Customer table, join the resultant row to the Sales.SalesTerritory table and return the name of the territory. The code is making diligent use of parameters for reusable query plans, strong typing and to prevent SQL injection attacks. The following test exercises the code: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;[&lt;font color="#008b8b"&gt;TestClass&lt;/font&gt;]         &lt;br /&gt;&lt;font color="#0000ff"&gt;public class&lt;/font&gt; &lt;font color="#008b8b"&gt;AccountDalTests&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; [&lt;font color="#008b8b"&gt;TestMethod&lt;/font&gt;]         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public void&lt;/font&gt; CanGetTerritory()         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;string&lt;/font&gt; territory = &lt;font color="#008b8b"&gt;AccountDal&lt;/font&gt;.GetTerritoryForAccount(&lt;font color="#008b8b"&gt;&amp;quot;AW00000010&amp;quot;&lt;/font&gt;);         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008b8b"&gt;Assert&lt;/font&gt;.AreEqual(territory, &lt;font color="#008b8b"&gt;&amp;quot;Canada&amp;quot;&lt;/font&gt;);         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This test passes, so functionally the code works as expected. Running this bit of code on a decently spec'd developer workstation takes about 800 ms. Running it repeatedly doesn't improve on this figure. The Sales.Customer table contains 19,185 rows. Given that we're looking up a single row in the Sales.Customer row the alarm bells should be ringing, especially as the Sales.Customer has a nonclustered, unique index on AccountNumber: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; &lt;font color="#800000"&gt;sp_helpindex&lt;/font&gt; &lt;font color="#ff0000"&gt;'Sales.Customer'&lt;/font&gt;;         &lt;br /&gt;        &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;index_name &lt;/td&gt;              &lt;td&gt;index_description &lt;/td&gt;              &lt;td&gt;index_key &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;-------------------------- &lt;/td&gt;              &lt;td&gt;----------------------------------------------------- &lt;/td&gt;              &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr style="font-weight: bold"&gt;             &lt;td&gt;AK_Customer_AccountNumber &lt;/td&gt;              &lt;td&gt;nonclustered, unique located on PRIMARY &lt;/td&gt;              &lt;td&gt;AccountNumber &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;AK_Customer_rowguid &lt;/td&gt;              &lt;td&gt;nonclustered, unique located on PRIMARY &lt;/td&gt;              &lt;td&gt;rowguid &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;IX_Customer_TerritoryID &lt;/td&gt;              &lt;td&gt;nonclustered located on PRIMARY &lt;/td&gt;              &lt;td&gt;TerritoryID &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;PK_Customer_CustomerID &lt;/td&gt;              &lt;td&gt;clustered, unique, primary key located on PRIMARY &lt;/td&gt;              &lt;td&gt;CustomerID &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;h2&gt;Diagnosing the Issue&lt;/h2&gt;    &lt;p&gt;Capturing the TSQL being executed in SQL Profiler and running it through Query Analyzer shows the following query plan. The query plan shows exactly where all the time is being spent: &lt;img class="pix" alt="Original Query Plan" src="/fotia/Blog/Images/200703.QueryPlanBefore.png" /&gt; The IX_Customer_TerritoryID index is being used. However, we're performing an index scan of all the AccountNumbers instead of just an index seek to the specific row we're returning. The compute scalar step following the index scan shows precisely why. Expr1004 is the output of an implicit conversion of every AccountNumber to NVARCHAR(10). Looking at the definition of the Sales.Customer table shows that AccountNumber is, in fact, a VARCHAR(10): &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; column_name, data_type, character_maximum_length         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;information_schema.columns&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; table_catalog = &lt;font color="#ff0000"&gt;'AdventureWorks'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&lt;font color="#696969"&gt;and&lt;/font&gt; table_schema = &lt;font color="#ff0000"&gt;'Sales'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&lt;font color="#696969"&gt;and&lt;/font&gt; table_name = &lt;font color="#ff0000"&gt;'Customer'&lt;/font&gt;;         &lt;br /&gt;        &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;column_name &lt;/td&gt;              &lt;td&gt;data_type &lt;/td&gt;              &lt;td&gt;character_maximum_length &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;-------------- &lt;/td&gt;              &lt;td&gt;------------------- &lt;/td&gt;              &lt;td&gt;------------------------ &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;CustomerID &lt;/td&gt;              &lt;td&gt;int &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;TerritoryID &lt;/td&gt;              &lt;td&gt;int &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr style="font-weight: bold"&gt;             &lt;td&gt;AccountNumber &lt;/td&gt;              &lt;td&gt;varchar &lt;/td&gt;              &lt;td&gt;10 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;CustomerType &lt;/td&gt;              &lt;td&gt;nchar &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;rowguid &lt;/td&gt;              &lt;td&gt;uniqueidentifier &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;ModifiedDate &lt;/td&gt;              &lt;td&gt;datetime &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Looking at the TSQL the application sends to SQL Server, we can see that it is binding @AccountNumber as an NVARCHAR(10): &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; &lt;font color="#800000"&gt;sp_executesql&lt;/font&gt; N&lt;font color="#ff0000"&gt;'          &lt;br /&gt;&amp;#160;&amp;#160; select t.Name           &lt;br /&gt;&amp;#160;&amp;#160; from Sales.Customer c           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; inner join Sales.SalesTerritory t           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; on c.TerritoryID = t.TerritoryId           &lt;br /&gt;&amp;#160;&amp;#160; where c.AccountNumber = @AccountNumber'&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; N&lt;font color="#ff0000"&gt;'@AccountNumber &lt;font style="font-weight: bold; text-decoration: underline"&gt;nvarchar(10)&lt;/font&gt;'&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160; @AccountNumber=N&lt;font color="#ff0000"&gt;'AW00000010'&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;So the application is binding an NVARCHAR(10) parameter that is then being compared to a VARCHAR(10) column. SQL Server then converts every row of the VARCHAR(10) column to an NVARCHAR(10) data type. Clearly this is where the performance problem is. The scalability of this query is therefore linearly dependant on the number of rows in the table. The more rows there are, the more values need to be converted. This not a good situation to be in. &lt;/p&gt;    &lt;h2&gt;Data Type Precedence&lt;/h2&gt;    &lt;p&gt;Whenever SQL Server is asked to compare two differing data types, it uses its data type precedence rules to determine which data type needs to be converted to perform the comparison. Assuming an implicit conversion exists, the data type that appears lower in the list is converted to the data type that appears higher in the list. SQL Server's &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190309.aspx"&gt;Data Type Precedence&lt;/a&gt; order has NVARCHAR appearing higher than VARCHAR. Therefore, all VARCHAR data types need to be converted to NVARCHAR in order to evaluate the AccountNumber values.&lt;/p&gt;    &lt;p&gt;Unfortunately, in our case, we have ~20,000 VARCHARs to convert to NVARCHAR and 1 NVARCHAR @AccountNumber! This is the reason for the index scan in the query plan. Note that with SQL Server 7.0, the query optimiser would always convert the literal and never the column. SQL Server 2000/5 strictly adheres to the Data Type Precedence rules, even if it means having to convert 19,185 rows. This change in behaviour is documented in KB &lt;a href="http://support.microsoft.com/?id=271566"&gt;271566&lt;/a&gt;. &lt;/p&gt;    &lt;p&gt;Looking back at the original C# code, we didn't specify a SqlDbType when adding the @AccountNumber parameter. The SqlParameter class will infer the SqlDbType from the object value specified if it is not specified explicitly. A System.String is always interpreted as SqlDbType.NVarChar as all .NET strings are Unicode. &lt;/p&gt;    &lt;p&gt;There are a number of possible solutions to this problem: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;Change the C# code to have knowledge of the SqlDbTypes the parameters are binding to &lt;/li&gt;      &lt;li&gt;Standardise by only allowing inferred SqlDbType types to be used in your database &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;Personally, I prefer the first option by a country mile. The second option prevents you from designing a data model that fully exploits all of the advantages of the SQL Server domain. For example, disallowing VARCHARs will double the amount of storage required to store your VARCHARs. I prefer to use a data type dictionary. &lt;/p&gt;    &lt;h2&gt;Data Type Dictionary&lt;/h2&gt;    &lt;p&gt;Data dictionaries are normally only used within databases. However, introducing a data type dictionary in the DAL allows you to have fine-grained control over your data types as well as other data type facets such as length and precision. In addition, it provides for better type safety as users of the data dictionary can only pass a .NET string to get an AccountNumber SqlParameter object. Any other .NET would obviously be a compile time error. Lastly, should you ever have to change data types (e.g. accountNumber becomes a System.Int32) it would be easy to make the change to the GetAccountNumber method and get compile time errors where string accountNumbers are still being used. &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;public static class&lt;/font&gt; &lt;font color="#008b8b"&gt;SqlDictionary&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public static&lt;/font&gt; &lt;font color="#008b8b"&gt;SqlParameter&lt;/font&gt; GetAccountNumber(&lt;font color="#0000ff"&gt;string&lt;/font&gt; accountNumber)         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008b8b"&gt;SqlParameter&lt;/font&gt; parameter = &lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;SqlParameter&lt;/font&gt;(&lt;font color="#008b8b"&gt;&amp;quot;@AccountNumber&amp;quot;&lt;/font&gt;, &lt;font color="#008b8b"&gt;SqlDbType&lt;/font&gt;.VarChar, 10);         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; parameter.Value = accountNumber;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; parameter;         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;With this change, the query plan now looks far more respectable: &lt;img class="pix" alt="Updated Query Plan" src="/fotia/Blog/Images/200703.QueryPlanAfter.png" /&gt; &lt;/p&gt;    &lt;p&gt;Relying on inferred Object / Relational data mappings can lead to unexpected issues. This is a classic example of the ORM-impedance mismatch that so many tools try to abstract away. However, by utilising a simple data dictionary, many of these problems can be easily solved by writing disciplined code. &lt;/p&gt;    &lt;h2&gt;Default Implicit Conversion&lt;/h2&gt;    &lt;p&gt;SQL Server 2005 has mediated the data type precedence trap somewhat. For example, in SQL Server 2000, comparing a BIT column to a literal 1 always resulted in the BIT column being upcast to a TINYINT. Whilst this may sound a bit bizarre, the problem was that the literal number 1 was always implicitly assigned the TINYINT data type and TINYINT appears above BIT in the data type precedence rules. SQL Server 2005 now applies a bit more intelligence. It assigns the literal the same data type as the column so no conversion is required. &lt;/p&gt;    &lt;p&gt;So, if you're still using SQL Server 2000, be sure to specify your BIT literals as 0x1. Whilst this doesn't equate to a BIT data type, its BINARY datatype is lower on the data type precedence rules and is therefore implicitly upcast to a BIT data type. Using this standard you'll never fall into the trap of upcasting all your BITS to TINYINTs. Note that if you run a SQL Server 2005 database in 80 compatability level you'll get the old SQL Server 2000 behaviour. &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- SQL Server 2000 implicit conversion issue.          &lt;br /&gt;-- SQL Server 2005 OK.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; FinishedGoodsFlag = 1;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- SQL Server 2000 OK.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; FinishedGoodsFlag = 0x1;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The implicit conversion is quite easy to spot in the query plan. &lt;/p&gt;   &lt;img class="pix" alt="Convert SQL 200 vs 2005" src="/fotia/Blog/Images/200703.Convert2Kvs2K5.png" /&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-6549536543849322791?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2007/03/when-string-is-not-string.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-4199268131752539985</guid><pubDate>Thu, 01 Mar 2007 22:35:00 +0000</pubDate><atom:updated>2008-02-04T23:10:51.658Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><title>KB Article Hidden Gems</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;One habit that has paid off repeatedly has been to loyally scan through PSS's SQL Server 2005 KB Article &lt;a href="http://support.microsoft.com/common/rss.aspx?rssid=2855&amp;amp;ln=en-us"&gt;RSS Feed&lt;/a&gt; on a daily basis. There isn't a huge amount of volume; some days as many as 5 new articles, most days none. It works very well as my SQL Server gossip column. The KB articles tell me what the latest issues are and what information is considered critical enough by PSS to warrant a KB article.&lt;/p&gt;    &lt;p&gt;For example, yesterday the following KB article was published:&lt;/p&gt;    &lt;p&gt;&lt;a href="http://support.microsoft.com/default.aspx?id=929240"&gt;FIX: I/O requests that are generated by the checkpoint process may cause I/O bottlenecks if the I/O subsystem is not fast enough to sustain the IO requests in SQL Server 2005&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;If you take the time to scroll to the very end of the article, you'll find the &lt;b&gt;More Information&lt;/b&gt; section. This is where the really good stuff is kept. This specific hotfix is not what you would expect. The hotfix adds an additional SQL Server command line option to throttle the I/O requests to the disk subsystem. I/O subsystem issues are common when dealing with SQL Server so knowing about this specific issue and the existence of the hotfix will give you some options when investigating I/O subsystem issues.&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-4199268131752539985?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2007/03/kb-article-hidden-gems.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-1457585753553891492</guid><pubDate>Thu, 18 Jan 2007 19:27:00 +0000</pubDate><atom:updated>2008-02-04T23:10:01.120Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>.net</category><title>Covariant Generic List</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;The introduction of generics in .NET 2.0 has opened up a whole new approach to class design in C#. The first, and most obvious, benefit generics provide is strongly typed containers. We no longer have to keep casting, boxing / unboxing, to / from object for ArrayList items anymore. &lt;/p&gt;    &lt;p&gt;However, with the power of generics come a few limitations. This article explores some subtle restrictions in covariance with generics. &lt;/p&gt;    &lt;h2&gt;What is Covariance?&lt;/h2&gt;    &lt;p&gt;In mathematical terms covariance is the measure of the tendency of two things to move or vary together. In the world of object-oriented programming, covariance describes the situation in which a derived type is used where a base class was expected. For example, in .NET arrays are covariant. That is: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008b8b"&gt;DerivedClass&lt;/font&gt;[] derivedClasses = &lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;DerivedClass&lt;/font&gt;[3];         &lt;br /&gt;&lt;font color="#008b8b"&gt;BaseClass&lt;/font&gt;[] baseClasses = derivedClasses; &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As a worked example, let's say that we have a People class. An instance of the People class is created from a data source that requires read / write access to the class's properties. A good example of such a data source is XML serialisation. The XmlSerializer class requires read / write properties and a default constructor in order to create an object from XML. However, once this object has been created, I want the object to be immutable. I don't want any properties of the object to be changed once it has been created. The natural implementation for this requirement is to create the IPerson interface that only exposes the property getters: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;// Immutable Person.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;public interface&lt;/font&gt; &lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;string&lt;/font&gt; Name { &lt;font color="#0000ff"&gt;get&lt;/font&gt;; }         &lt;br /&gt;}         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;public class&lt;/font&gt; &lt;font color="#008b8b"&gt;Person&lt;/font&gt; : &lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;private string&lt;/font&gt; name;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public&lt;/font&gt; Person()         &lt;br /&gt;&amp;#160;&amp;#160; {}         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public string&lt;/font&gt; Name         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;get&lt;/font&gt; { &lt;font color="#0000ff"&gt;return this&lt;/font&gt;.name; }         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; { &lt;font color="#0000ff"&gt;this&lt;/font&gt;.name = &lt;font color="#0000ff"&gt;value&lt;/font&gt;; }         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;public static class&lt;/font&gt; &lt;font color="#008b8b"&gt;PersonFactory&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// Factory method to create an IPerson instance.&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public static&lt;/font&gt; &lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt; CreatePerson()         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// Simulate a data source.&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008b8b"&gt;Person&lt;/font&gt; person = &lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;Person&lt;/font&gt;();         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; person.Name = &lt;font color="#ff8c00"&gt;&amp;quot;Trogdor&amp;quot;&lt;/font&gt;;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; person;         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;h2&gt;Generic Covariance Limitations&lt;/h2&gt;    &lt;p&gt;Now, let's say that we aren't creating single Person objects. Instead the data source returns an IList of Person objects. We would naturally try the following code first: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;public static class&lt;/font&gt; &lt;font color="#008b8b"&gt;PeopleFactory&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public static&lt;/font&gt; &lt;font color="#ff00ff"&gt;IList&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;IPerson&lt;/font&gt;&amp;gt; CreatePeople()         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// Simulate a data source.&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008b8b"&gt;List&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;Person&lt;/font&gt;&amp;gt; people = &lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;List&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;Person&lt;/font&gt;&amp;gt;();         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// Person objects added to list.&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// ...&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; people;         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The code above does not compile. The C# compiler fails with error:      &lt;br /&gt;&lt;/p&gt;    &lt;div class="tsql"&gt;&lt;font color="#ff0000"&gt;CS0030: Cannot convert type 'System.Collections.Generic.List&amp;lt;TestScratch.Person&amp;gt;' to 'System.Collections.Generic.List&amp;lt;TestScratch.IPerson&amp;gt;' &lt;/font&gt;&lt;/div&gt;    &lt;p&gt;The problem we've run into here has to do with the generic type compatibility, specifically, generics are not covariant. &lt;/p&gt;    &lt;p&gt;This is nicely summarised over on MSDN: &lt;a href="http://msdn2.microsoft.com/en-gb/library/aa479859.aspx#fundamentals_topic12"&gt;Are Generics Covariant, Contra-Variant, or Invariant?&lt;/a&gt;. In a nutshell, generics cannot be covariant as it would allow illegal constructs. For example: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;public class&lt;/font&gt; &lt;font color="#008b8b"&gt;Adult&lt;/font&gt; : &lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;//&lt;/font&gt;         &lt;br /&gt;}         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;public class&lt;/font&gt; &lt;font color="#008b8b"&gt;Child&lt;/font&gt; : &lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;//&lt;/font&gt;         &lt;br /&gt;}         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;public void&lt;/font&gt; WhyCoVarianceIsNotAllowed()         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008b8b"&gt;List&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;Adult&lt;/font&gt;&amp;gt; adults = &lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;List&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;Adult&lt;/font&gt;&amp;gt;();         &lt;br /&gt;&amp;#160;&amp;#160; adults.Add(&lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;Adult&lt;/font&gt;());         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// If the following line would be allowed...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008b8b"&gt;List&lt;/font&gt;&amp;lt;&lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;&amp;gt; people = adults;         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// This line would have to be legal, i.e. adding a Child to an Adult List.&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160; people.Add(&lt;font color="#0000ff"&gt;new&lt;/font&gt; &lt;font color="#008b8b"&gt;Child&lt;/font&gt;());         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;h2&gt;A Generic Covariant List Implementation&lt;/h2&gt;    &lt;p&gt;As dire as this seems, not all is lost. A natural extension to the immutable object requirement would be to constrain the list of immutable objects to be itself immutable. This additional restriction allows us to circumvent the generic covariance issue as the immutable list would not allow the 'illegal' situation to arise. The most natural interface to return for an immutable list is IEnumerable&amp;lt;T&amp;gt;. We therefore need to find a way of converting / casting IList&amp;lt;Person&amp;gt; to IEnumerable&amp;lt;IPerson&amp;gt;. As the C# compiler does not make any generic covariance exceptions, it will still not allow us to directly cast IList&amp;lt;Person&amp;gt; to IEnumerable&amp;lt;IPerson&amp;gt;. Instead we need to find a way to express the readonly nature of IEnumerable in a way that gets the C# compiler to accept that what we're trying to do is legal. That is what the following class accomplishes: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;public class&lt;/font&gt; &lt;font color="#008b8b"&gt;EnumerableGeneric&lt;/font&gt;&amp;lt;TClass, TInterface&amp;gt; : &lt;font color="#ff00ff"&gt;IEnumerable&lt;/font&gt;&amp;lt;TInterface&amp;gt;         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; TClass : TInterface         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;private&lt;/font&gt; &lt;font color="#ff00ff"&gt;IList&lt;/font&gt;&amp;lt;TClass&amp;gt; list;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public&lt;/font&gt; EnumerableGeneric(&lt;font color="#ff00ff"&gt;IList&lt;/font&gt;&amp;lt;TClass&amp;gt; list)         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;this&lt;/font&gt;.list = list;         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public&lt;/font&gt; &lt;font color="#ff00ff"&gt;IEnumerator&lt;/font&gt;&amp;lt;TInterface&amp;gt; GetEnumerator()         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;foreach&lt;/font&gt;(TClass item &lt;font color="#0000ff"&gt;in&lt;/font&gt; list)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;yield return&lt;/font&gt; item;         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;IEnumerator&lt;/font&gt; &lt;font color="#ff00ff"&gt;IEnumerable&lt;/font&gt;.GetEnumerator()         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; &lt;font color="#0000ff"&gt;this&lt;/font&gt;.GetEnumerator();         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This class pulls together pretty much all of the .NET 2.0 features. Firstly, it uses generic constraints to ensure that TClass implements TInterface (or TInterface could be a base class of TClass). Secondly, the compiler understands constraint as it allows us to use yield return to implicitly cast TClass to TInterface. This class then becomes a simple generic wrapper that can be used whenever a covariant list is required: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;public static class&lt;/font&gt; &lt;font color="#008b8b"&gt;PeopleFactory&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;public static&lt;/font&gt; &lt;font color="#ff00ff"&gt;IEnumerable&lt;/font&gt;&amp;lt;&lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;&amp;gt; CreatePeople()         &lt;br /&gt;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// Simulate a data source.&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;IList&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;Person&lt;/font&gt;&amp;gt; people = &lt;font color="#0000ff"&gt;new&lt;/font&gt; List&amp;lt;Person&amp;gt;();         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// Person objects added to list.&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;// ...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return new&lt;/font&gt; &lt;font color="#008b8b"&gt;EnumerableGeneric&lt;/font&gt;&amp;lt;&lt;font color="#008b8b"&gt;Person&lt;/font&gt;, &lt;font color="#ff00ff"&gt;IPerson&lt;/font&gt;&amp;gt;(people);         &lt;br /&gt;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-1457585753553891492?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2007/01/covariant-generic-list.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-2758620945276793951</guid><pubDate>Mon, 20 Nov 2006 19:41:00 +0000</pubDate><atom:updated>2008-02-04T23:08:50.958Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The TABLESAMPLE Clause</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;When I first same across TABLESAMPLE I had visions of some advanced index hints that allowed fine grained control over table statistics (distribution, ranges, density, etc.). Unfortunately not. As per &lt;a href="http://en.wikipedia.org/wiki/Occam's_Razor"&gt;Occam's Razor&lt;/a&gt; the simplest explanation is the correct one. It just... returns a &lt;b&gt;sample&lt;/b&gt; from the specified &lt;b&gt;table&lt;/b&gt;...?&lt;/p&gt;    &lt;p&gt;The complete syntax for TABLESAMPLE is as follows (from BOL):&lt;/p&gt;   &lt;img class="pix" alt="TABLESAMPLE" src="/fotia/Blog/Images/200611.TableSample.jpg" /&gt;     &lt;p&gt;Given this syntax, the most obvious query to test drive TABLESAMPLE would be to return, say, 10 rows from the Sales.SalesOrderHeader table. After all, this syntax looks a lot like TOP:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; SalesOrderId, OrderDate, TotalDue         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Sales.SalesOrderHeader &lt;font color="#0000ff"&gt;tablesample&lt;/font&gt; system (10 rows)&lt;font color="#696969"&gt;;          &lt;br /&gt;&lt;/font&gt;        &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(0 rows(s) affected)&lt;/b&gt;&lt;/p&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;How unexpected is that! Zero rows returned?! Huh? For the sake of adding to the confusion, change the query to return 1,000 rows as follows:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; SalesOrderId, OrderDate, TotalDue         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Sales.SalesOrderHeader &lt;font color="#0000ff"&gt;tablesample&lt;/font&gt; system (1000 rows)&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Then run the query a few times and observe how many rows get returned:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;       &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;44137 &lt;/td&gt;              &lt;td&gt;2001-09-01 00:00:00.000 &lt;/td&gt;              &lt;td&gt;3953.9884 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(938 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;44407 &lt;/td&gt;              &lt;td&gt;2001-10-16 00:00:00.000 &lt;/td&gt;              &lt;td&gt;3729.364 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(1263 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;44757 &lt;/td&gt;              &lt;td&gt;2001-12-01 00:00:00.000 &lt;/td&gt;              &lt;td&gt;49953.7086 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(1085 rows(s) affected)&lt;/b&gt;&lt;/p&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Each execution returns a &lt;b&gt;different&lt;/b&gt; number of rows! Some of them just below 1,000 and others just above. Clearly there is a non-deterministic factor at play here. Without any data changing, re-running the identical query keeps giving different results. Digging into the BOL documentation shows that the TABLESAMPLE algorithm has a very strong statistical slant to it. It works as follows:&lt;/p&gt;    &lt;p&gt;For the table affected by the TABLESAMPLE, SQL Server needs to determine a probability that a &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190969.aspx"&gt;page&lt;/a&gt; of that table's data will be sampled. This probability is equal to the PERCENTAGE specified in the TABLESAMPLE. Alternatively, if a number of ROWS was specified, that number is divided by the total number of rows in the table to calculate the probability. In our case, the Sales.SalesOrderHeader table has 31,465 rows. We asked for 1,000 rows in the second query. Therefore the probability is calculated as 3.2%.&lt;/p&gt;    &lt;p&gt;Once this probability has been calculated, SQL Server will evaluate it for every page of the table. The details of the algorithm aren't documented but it is along the lines of:&lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;For each page, a random number between 0 and 1 is generated (e.g. RAND()). &lt;/li&gt;      &lt;li&gt;If the random number is greater than the probability, SQL Server will pretend that the page for the table does not exist (i.e. it is not sampled / 'out-of-scope'). &lt;/li&gt;      &lt;li&gt;Otherwise, SQL Server considers &lt;b&gt;all&lt;/b&gt; of the rows on the page sampled / 'in scope'. &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;This is very revealing as it explains the behaviour we saw with the first query. We asked for a sample of 10 rows. SQL Server therefore calculated the probability of a page being returned as:      &lt;br /&gt;10 / 31,465 = 0.032 %, a very small number. If we execute the query once, there is a chance SQL Server will not generate a random number that falls within this small probability. However, we can increase our chances of getting at least one page returned by executing the select statement a number of times:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @loopCount &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @loopCount = 0&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @rowcount &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @rowCount = 0&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Keep looping till we manage to sample at least one page.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;while&lt;/font&gt; @rowCount = 0 &lt;font color="#0000ff"&gt;begin&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; SalesOrderId, OrderDate, TotalDue         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Sales.SalesOrderHeader &lt;font color="#0000ff"&gt;tablesample&lt;/font&gt; system (10 rows)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @rowcount = &lt;font color="#ff00ff"&gt;@@rowcount&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @loopCount = @loopCount + 1&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;raiserror&lt;/font&gt;(&lt;font color="#ff0000"&gt;'Required %d loops to return %d rows'&lt;/font&gt;, 0, 1, @loopCount, @rowCount)&lt;font color="#696969"&gt;;          &lt;br /&gt;&lt;/font&gt;        &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(0 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(0 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(0 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(0 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;SalesOrderId &lt;/td&gt;              &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;TotalDue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------ &lt;/td&gt;              &lt;td&gt;----------------------- &lt;/td&gt;              &lt;td&gt;--------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;69998 &lt;/td&gt;              &lt;td&gt;2004-05-06 00:00:00.000 &lt;/td&gt;              &lt;td&gt;2649.8453 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;p&gt;&lt;b&gt;(46 rows(s) affected)&lt;/b&gt;&lt;/p&gt;        &lt;p&gt;&lt;b&gt;Required 5 loops to return 46 rows&lt;/b&gt;&lt;/p&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Interestingly enough, when we get back some rows (i.e. we get a probability hit) we get 91 rows returned instead of 10. This reveals another facet of the TABLESAMPLE algorithm. It only goes as far as the probability calculation of a page for sampling data. Once it has decided that a page of the table is to be included in the sample, it will not limit the number of rows sampled from that page. Therefore you are always liable to get back the number of rows equivalent to the page size divided by the row width (assuming, of course, that the pages are densely packed). Running the above query repeatedly returns 40-45 rows, indicating that a page of the Sales.SalesOrderHeader table typically holds 40-45 rows per page.&lt;/p&gt;    &lt;p&gt;A couple of additional points to note: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;The TABLESAMPLE is applied before any WHERE clauses are evaluated. &lt;/li&gt;      &lt;li&gt;This includes any JOIN conditions as well. Some misleading results can be obtained if two tables that are JOIN'd have a TABLESAMPLE applied to them. As the sampling of the tables are independent and uncorrelated, there is no guarantee that related rows from both tables will be included. &lt;/li&gt;      &lt;li&gt;The TOP clause can be combined with TABLESAMPLE to cap the number of rows returned. We saw earlier how the TABLESAMPLE algorithm can return more rows than requested. However, there is still every chance that less rows will be returned than was specified in the TABLESAMPLE. &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;The REPEATABLE option of the TABLESAMPLE clause basically causes SQL Server to use the same random numbers when evaluating each of the table's pages again (SQL Server is probably using a &lt;a href="http://en.wikipedia.org/wiki/Linear_congruential_generator"&gt;linear congruential generator&lt;/a&gt; to obtain the random numbers with the same seed). The net effect is that the same pages will be sampled assuming no underlying data has been changed.&lt;/p&gt;    &lt;p&gt;In terms of practical applications of the TABLESAMPLE algorithm, I really struggled to think of a situation where it would have been useful. At face value, all that TABLESAMPLE does is return a certain number or percentage of 'random-ish' rows from the specified table. Unless you have some highly-specialised requirement to get a 'flavour' for the data in the table, this new clause isn't that useful. It doesn't augment the TOP / ROWCOUNT clause at all and doesn't expose any customisable or extensible behaviour.&lt;/p&gt;    &lt;p&gt;I strongly suspect that the TABLESAMPLE clause was added specifically for the optimiser's gathering of statistics. Anyone who has seen STATMAN scrolling past in a SQL Profiler trace will know that the update of statistics uses a number of undocumented TSQL operators and index hints. For example:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; SalesOrderId, OrderDate, TotalDue         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select top&lt;/font&gt; 100 &lt;font color="#0000ff"&gt;percent&lt;/font&gt; SalesOrderId, OrderDate, TotalDue         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Sales.SalesOrderHeader &lt;font color="#0000ff"&gt;with&lt;/font&gt; (readcommitted, sample 10e-2 &lt;font color="#0000ff"&gt;percent&lt;/font&gt;)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;order by&lt;/font&gt; 1, 2, 3)         &lt;br /&gt;&lt;font color="#0000ff"&gt;option&lt;/font&gt; (bypass optimizer_queue, maxdop 1)&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;I count at least 2 undocumented operators and 1 view ordering workaround being used in this query. TABLESAMPLE is definitely a much cleaner implementation for a query that appears to be performing the same function.&lt;/p&gt;    &lt;p&gt;Who knows, maybe one day your boss will ask your team: &lt;i&gt;&amp;quot;You know what, it would be really cool if we could return random pages from this table, according to a probability we specify, get back different results every time we execute it, and even return vastly different numbers of rows than what we asked for&amp;quot;&lt;/i&gt;. You'll be the first one to raise your hand...&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-2758620945276793951?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/11/tablesample-clause.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-7459003878340261328</guid><pubDate>Fri, 13 Oct 2006 14:22:00 +0000</pubDate><atom:updated>2008-02-04T23:06:27.244Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The UNPIVOT Operator</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;Completing the discussion on the &lt;a href="DY.16.ThePivotOperator.aspx"&gt;PIVOT&lt;/a&gt; operator is its antonym, the UNPIVOT. However, I would argue that the UNPIVOT operator has been misnamed by Microsoft as it creates the mistaken impression that PIVOT -&amp;gt; UNPIVOT -&amp;gt; PIVOT gets you back to where you started. From BOL: &lt;/p&gt;    &lt;p&gt;&lt;i&gt;UNPIVOT performs the opposite operation to PIVOT by rotating columns of a table-valued expression into column values.&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;It fails to highlight that fact that PIVOT has aggregated the raw data in order to rotate the underlying rows into columns - you have to read to the very end of the &lt;b&gt;Using PIVOT and UNPIVOT&lt;/b&gt; sample for the fine print in BOL. In contrast, the UNPIVOT operator does not perform any aggregation whatsoever. It just turns columns into rows. For each column in the table being &lt;i&gt;UNPIVOT'd&lt;/i&gt; you get a row in the result set where the row values are ColumnName and ColumnValue. It therefore helps to think of &lt;a href="http://en.wikipedia.org/wiki/Matrix_transpose"&gt;transpose&lt;/a&gt; instead of UNPIVOT as this is more accurate description of the UNPIVOT operator's behaviour.&lt;/p&gt;    &lt;p&gt;Nevertheless, let's have a look at the syntax to try and get a better feel for what UNPIVOT brings to the table. As a worked example, consider the following requirement: Write a query to produce a result set that contains a list of products (Production.Product) and their SellStartDate and SellEndDate. The SellStartDate and SellEndDate need to be listed in a single column with a second column specifying whether it is the SellStartDate or SellEndDate value being specified. Lastly, the result set needs to be sorted by this consolidated date column to produce a linear timeline of 'product events'.&lt;/p&gt;    &lt;p&gt;In SQL Server 2000, you'd probably end up with a query that looks very much like the following:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductId, [Name], &lt;font color="#ff0000"&gt;'SellStartDate'&lt;/font&gt; &lt;font color="#0000ff"&gt;as&lt;/font&gt; DateType, SellStartDate &lt;font color="#0000ff"&gt;as&lt;/font&gt; DateValue         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product         &lt;br /&gt;&lt;font color="#0000ff"&gt;union&lt;/font&gt; &lt;font color="#696969"&gt;all&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductId, [Name], &lt;font color="#ff0000"&gt;'SellEndDate'&lt;/font&gt; &lt;font color="#0000ff"&gt;as&lt;/font&gt; DateType, SellEndDate &lt;font color="#0000ff"&gt;as&lt;/font&gt; DateValue         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; SellEndDate &lt;font color="#696969"&gt;is not null&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; DateValue&lt;font color="#696969"&gt;;         &lt;br /&gt;          &lt;br /&gt;&lt;/font&gt;         &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;ProductId &lt;/td&gt;              &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;DateType &lt;/td&gt;              &lt;td&gt;DateValue &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;320 &lt;/td&gt;              &lt;td&gt;Chainring Bolts &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;321 &lt;/td&gt;              &lt;td&gt;Chainring Nut &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;322 &lt;/td&gt;              &lt;td&gt;Chainring &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;323 &lt;/td&gt;              &lt;td&gt;Crown Race &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;324 &lt;/td&gt;              &lt;td&gt;Chain Stays &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;325 &lt;/td&gt;              &lt;td&gt;Decal 1 &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;326 &lt;/td&gt;              &lt;td&gt;Decal 2 &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;327 &lt;/td&gt;              &lt;td&gt;Down Tube &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;328 &lt;/td&gt;              &lt;td&gt;Mountain End Caps &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;329 &lt;/td&gt;              &lt;td&gt;Road End Caps &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;330 &lt;/td&gt;              &lt;td&gt;Touring End Caps &lt;/td&gt;              &lt;td&gt;SellStartDate &lt;/td&gt;              &lt;td&gt;1998-06-01 00:00:00.000 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Using the UNPIVOT operator, in SQL Server 2005, the equivalent is as follows:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductId, [Name], DateType, DateValue         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;(&lt;/font&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID, [Name], SellStartDate, SellEndDate         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product&lt;font color="#696969"&gt;)&lt;/font&gt; &lt;font color="#0000ff"&gt;as&lt;/font&gt; rawData         &lt;br /&gt;&lt;font color="#696969"&gt;unpivot&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;(&lt;/font&gt;DateValue &lt;font color="#0000ff"&gt;for&lt;/font&gt; DateType &lt;font color="#696969"&gt;in&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;(&lt;/font&gt;SellStartDate, SellEndDate&lt;font color="#696969"&gt;))&lt;/font&gt; &lt;font color="#0000ff"&gt;as&lt;/font&gt; transposed         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; DateValue&lt;font color="#696969"&gt;;&lt;/font&gt; &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The key to understanding the UNPIVOT syntax is to see how the DateValue and DateType columns are specified. When transposing the columns into rows, they effectively become name / value pairs. The name is the name of the column and value is the value of the column for the relative row. BOL calls it &lt;i&gt;value_column&lt;/i&gt; and &lt;i&gt;pivot_column&lt;/i&gt; but just think of them as name / value pairs.&lt;/p&gt;    &lt;p&gt;The first portion of the query selects the raw data which we will be transposing. The UNPIVOT portion of the query requires the name of the columns to be used for the name and value pair columns (Value FOR Name) followed by the columns that will be transposed. Lastly, the outermost select allows you specify which transposed columns you want to return, so you can choose not to return the Name column if you don't want it.&lt;/p&gt;    &lt;p&gt;As the SELECT...CASE statements were able to duplicate all the functionality of the &lt;a href="DY.16.ThePivotOperator.aspx"&gt;PIVOT&lt;/a&gt; operator, SELECT...UNION ALL can perform all the same tricks as UNPIVOT. However, the advantage again is a more compact and expressive syntax.&lt;/p&gt;    &lt;p&gt;One behaviour of UNPIVOT to watch out for is that it will not transpose column values that are NULL. NULL values will not appear as name / value pairs in the results. In the sample above I've compensated for this behaviour by including &lt;i&gt;where SellEndDate is not null&lt;/i&gt; in the SQL Server 2000 equivalent query. Note that there is way to change this behaviour.&lt;/p&gt;    &lt;p&gt;In terms of performance, the query optimiser highlights a significant difference between the SELECT...UNION ALL and UNPIVOT queries. The former requires a pass over the underlying table for each select (2 passes in our sample) whereas the UNPIVOT requires only a single pass. SQL Server is able to transpose the columns into rows for each column in a single pass over the underlying data. This makes the UNPIVOT query less expensive than the equivalent UNION ALL. The size of this penalty is proportional to the number of rows being processed. This fact along makes UNPIVOT a very useful tool to keep note of in your TSQL arsenal.&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-7459003878340261328?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/10/unpivot-operator.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-4621243415141018660</guid><pubDate>Sun, 01 Oct 2006 20:54:00 +0000</pubDate><atom:updated>2008-02-04T23:04:47.457Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><title>SPID Blocks Itself...?</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;Came across this useful KB the other day: &lt;a href="http://support.microsoft.com/?id=906344"&gt;906344&lt;/a&gt;, &lt;b&gt;The blocked column in the sysprocesses table is populated for latch waits after you install SQL Server 2000 SP4&lt;/b&gt;. The technical detail may be a bit confusing. Essentially the SPID is doing a convoluted &amp;quot;WaitForSingleObject&amp;quot;-type block, waiting for a data page to be fetched into memory from disk. If you see a lot of this type of blocking you may want to check your other performance counters as you may have poor performing IO or buffer pool memory pressure.&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-4621243415141018660?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/10/spid-blocks-itself.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-4263470973485378611</guid><pubDate>Sat, 23 Sep 2006 00:24:00 +0000</pubDate><atom:updated>2008-02-04T23:03:02.711Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><title>Microsoft SQL Server Development Team Blogs</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;The best source for SQL Server internals and advanced TSQL are the Microsot SQL Server development teams' blogs. Some are more active than others, but they're all worth subscribing to for that gem you don't want to miss. For example, the SCHEMABINDING &lt;a href="http://blogs.msdn.com/sqlprogrammability/archive/2006/05/12/596424.aspx"&gt;tip&lt;/a&gt; is a real nugget.&lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;&lt;a href="http://blogs.msdn.com/sqlprogrammability/"&gt;SQL Programmability &amp;amp; API Development Team Blog&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;&lt;a href="http://blogs.msdn.com/sqltips/"&gt;SQL Server Engine Tips&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;&lt;a href="http://blogs.msdn.com/sqlserverstorageengine//"&gt;SQL Server Storage Engine&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;&lt;a href="http://blogs.msdn.com/sqlqueryprocessing/"&gt;Tips, Tricks, and Advice from the SQL Server Query Processing Team&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;&lt;a href="http://blogs.msdn.com/sqlclr/"&gt;SQL Server 2005: CLR Integration&lt;/a&gt;&lt;/li&gt;   &lt;/ul&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-4263470973485378611?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/09/microsoft-sql-server-development-team.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-7311548577161661814</guid><pubDate>Fri, 22 Sep 2006 20:49:00 +0000</pubDate><atom:updated>2008-02-04T23:01:14.253Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>visual studio</category><category domain='http://www.blogger.com/atom/ns#'>sql</category><title>For Database Professionals</title><description>&lt;font style="font-family: Georgia; font-size: small"&gt; &lt;p&gt;Visual Studio 2005 Team Edition for Database Professionals CTP 5 - Beta, download &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=4014554e-903a-4a62-b429-2b027321c32d&amp;amp;DisplayLang=en"&gt;here&lt;/a&gt;. An update patch for CTP 5 already &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=9f7d1be0-79c0-4d26-81e2-4ccd07d2352f&amp;amp;DisplayLang=en"&gt;available&lt;/a&gt;&lt;/p&gt;&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-7311548577161661814?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/09/for-database-professionals.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-8296940258164873951</guid><pubDate>Thu, 21 Sep 2006 23:12:00 +0000</pubDate><atom:updated>2008-02-04T22:59:51.207Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The PIVOT Operator</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;SQL Server 2005 introduces the PIVOT operator to the TSQL stable. The intention of this operator is to make it easier to transform unique values in rows into columns and aggregate the resulting data. Anyone familiar with Excel pivot tables or Access cross-tab queries will immediately be able to visualise what pivot tables look like. Prior to SQL Server 2005, the only way to transform data, in the manner provided by PIVOT, was to use a number of SELECT...CASE statements. As we'll see, the PIVOT operator has not given us functionality that was not possible before. However, it has made pivoting data much easier to perform and far more readable.&lt;/p&gt;    &lt;p&gt;In order to understand how to form a PIVOT query, it is important to be able to describe, in plain English, what the query is trying to accomplish. Personally I have found that thinking of the PIVOT result set in terms of a graph (i.e. a X- and Y- axis) makes it easier to formulate into TSQL.&lt;/p&gt;    &lt;p&gt;As a worked example, consider the following requirement. You need to generate a report that will correlate the number of addresses that were changed per city for each year. In other words, formulate a result set that will have each city listed as a column (X-axis). For each city, count the number of times an address in that city was modified in a given year (Y-axis). In SQL Server 2000, you'd probably create some TSQL similar to the following:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;datepart&lt;/font&gt;(yyyy, modifiedDate) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [WhenUpdates],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(&lt;font color="#0000ff"&gt;case&lt;/font&gt; city &lt;font color="#0000ff"&gt;when&lt;/font&gt; &lt;font color="#ff0000"&gt;'Monroe'&lt;/font&gt; &lt;font color="#0000ff"&gt;then&lt;/font&gt; 1 &lt;font color="#0000ff"&gt;else&lt;/font&gt; 0 &lt;font color="#0000ff"&gt;end&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Monroe],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(&lt;font color="#0000ff"&gt;case&lt;/font&gt; city &lt;font color="#0000ff"&gt;when&lt;/font&gt; &lt;font color="#ff0000"&gt;'Redmond'&lt;/font&gt; &lt;font color="#0000ff"&gt;then&lt;/font&gt; 1 &lt;font color="#0000ff"&gt;else&lt;/font&gt; 0 &lt;font color="#0000ff"&gt;end&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Redmond],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(&lt;font color="#0000ff"&gt;case&lt;/font&gt; city &lt;font color="#0000ff"&gt;when&lt;/font&gt; &lt;font color="#ff0000"&gt;'Snohomish'&lt;/font&gt; &lt;font color="#0000ff"&gt;then&lt;/font&gt; 1 &lt;font color="#0000ff"&gt;else&lt;/font&gt; 0&lt;font color="#0000ff"&gt; end&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Snohomish],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(&lt;font color="#0000ff"&gt;case&lt;/font&gt; city &lt;font color="#0000ff"&gt;when&lt;/font&gt; &lt;font color="#ff0000"&gt;'Issaquah'&lt;/font&gt; &lt;font color="#0000ff"&gt;then&lt;/font&gt; 1 &lt;font color="#0000ff"&gt;else&lt;/font&gt; 0 &lt;font color="#0000ff"&gt;end&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Issaquah]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Address         &lt;br /&gt;&lt;font color="#0000ff"&gt;group by&lt;/font&gt; &lt;font color="#ff00ff"&gt;datepart&lt;/font&gt;(yyyy, modifiedDate)         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; 1&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;WhenUpdates &lt;/td&gt;              &lt;td&gt;Monroe &lt;/td&gt;              &lt;td&gt;Redmond &lt;/td&gt;              &lt;td&gt;Snohomish &lt;/td&gt;              &lt;td&gt;Issaquah &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1996 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1997 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1998 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1999 &lt;/td&gt;              &lt;td&gt;11 &lt;/td&gt;              &lt;td&gt;12 &lt;/td&gt;              &lt;td&gt;7 &lt;/td&gt;              &lt;td&gt;13 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2000 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2001 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;5 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;6 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2002 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;9 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;13 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2003 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;45 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;42 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2004 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;44 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;45 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;A couple of things to note: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;Even though there are many more cities in the Person.Address table than the ones specified, we must 'hardcode' the city names into the query. There is no way to dynamically create additional columns if a new city is encountered. That fact alone makes this query difficult to maintain. If another city is added to Person.Address, this query would have to be modified. &lt;/li&gt;      &lt;li&gt;The Y-axis (WhenUpdated) column is dynamic, we do not require prior knowledge of the modifiedDate's content when formulating the query. When an address is modified in a new year (e.g. 2005), another row will automatically be added to this result set without any changes. &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;Now, for the PIVOT operator. There is good news and bad news. The good news is that the syntax is far more compact and readable. The bad news is that it overcomes none of the limitations mentioned above! However, as we'll see, the good news is still worthwhile...&lt;/p&gt;    &lt;p&gt;A PIVOT query is split into a number of sections. First, you need to write the SELECT statement that provides you with the raw, non-pivoted data. In our example, the raw data would be the following query:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;datepart&lt;/font&gt;(yyyy, modifiedDate) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [WhenUpdated], AddressID, City         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Address&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;WhenUpdated &lt;/td&gt;              &lt;td&gt;AddressID &lt;/td&gt;              &lt;td&gt;City &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;------------------------------ &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2004 &lt;/td&gt;              &lt;td&gt;19 &lt;/td&gt;              &lt;td&gt;Bothell &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1999 &lt;/td&gt;              &lt;td&gt;20 &lt;/td&gt;              &lt;td&gt;Bothell &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1999 &lt;/td&gt;              &lt;td&gt;21 &lt;/td&gt;              &lt;td&gt;Bothell &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2001 &lt;/td&gt;              &lt;td&gt;22 &lt;/td&gt;              &lt;td&gt;Portland &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1999 &lt;/td&gt;              &lt;td&gt;23 &lt;/td&gt;              &lt;td&gt;Seattle &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2001 &lt;/td&gt;              &lt;td&gt;24 &lt;/td&gt;              &lt;td&gt;Duluth &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2002 &lt;/td&gt;              &lt;td&gt;25 &lt;/td&gt;              &lt;td&gt;Dallas &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2001 &lt;/td&gt;              &lt;td&gt;26 &lt;/td&gt;              &lt;td&gt;San Francisco &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;WhenUpdated is the Y-axis, AddressID is a data point - we're going to count AddressIDs for a given (X,Y) / (City,WhenUpdated) combination - and City is the X-axis. Once we have the raw data, it is simple case of articulating which column is used for what function in the pivot:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; Pvt.*         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;-- The raw data as a derived table...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; datepart(yyyy, modifiedDate) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [WhenUpdated], AddressID, City         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Address) &lt;font color="#0000ff"&gt;as&lt;/font&gt; RawData         &lt;br /&gt;&lt;font color="#696969"&gt;pivot&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(AddressID)&amp;#160; &lt;font color="#008000"&gt;-- How each X,Y point is calculated...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;for&lt;/font&gt; City&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;-- The X-axis, stated explicitly...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;in&lt;/font&gt; ([Monroe], [Redmond], [Snohomish], [Issaquah])) &lt;font color="#0000ff"&gt;as&lt;/font&gt; Pvt         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; 1&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;WhenUpdated &lt;/td&gt;              &lt;td&gt;Monroe &lt;/td&gt;              &lt;td&gt;Redmond &lt;/td&gt;              &lt;td&gt;Snohomish &lt;/td&gt;              &lt;td&gt;Issaquah &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1996 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1997 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1998 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1999 &lt;/td&gt;              &lt;td&gt;11 &lt;/td&gt;              &lt;td&gt;12 &lt;/td&gt;              &lt;td&gt;7 &lt;/td&gt;              &lt;td&gt;13 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2000 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2001 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;5 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;6 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2002 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;9 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;13 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2003 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;45 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;42 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2004 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;44 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;45 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;At first glance the PIVOT section seems a bit unintuitive. However, if you work through the syntax it will start to make sense. The PIVOT operator needs to know how to calculate each (X,Y) data point's value, specified as an aggregate function. The FOR column contains the X-axis values and the IN contains the hard-coded list of X-axis columns. As you can see, there is no getting around the SELECT...CASE limitation of having to know the transformed columns before-hand. In case you are wondering: no, IN cannot take a wildcard, e.g. IN(*). I believe that the limitation exists due to the optimizer's requirement to have fixed metadata when the execution plan is generated. This would not be possible if the column metadata is dynamically generated from the data itself.&lt;/p&gt;    &lt;p&gt;The Y-axis is inferred as the distinct set of all columns that have not been used by the aggregate function or by the X-Axis. In our case, that would be the WhenUpdated computed column.&lt;/p&gt;    &lt;p&gt;In terms of execution plans, the SELECT...CASE and PIVOT produce almost identical plans. However, as we're throwing away more of the rows in the Person.Address table (only 267 of the 19,614 rows are for the cities in our X-axis), both queries would be far more efficient if they included the set of X-axis column values (i.e. the names of the cities) in their WHERE clauses.&lt;/p&gt;    &lt;p&gt;Instead of trying to dream up dynamic-SQL ways of working around this problem (non-dynamic columns and rows falling outside the predefined X-axis values) I would argue that you're better off aggregating the data in TSQL (using 'traditional' SELECT...GROUP BY statements) and transforming it in your middle tier / client application where there are better data structures that can be used to represent extremely wide or sparse result sets.&lt;/p&gt;    &lt;p&gt;A better pattern to follow is to realise that the X-axis can be used to specify fixed ranges instead of raw values. Using this technique you can select ranges that will guarantee that you have covered off all possible X-axis values. For example, instead of allowing the raw unique values of a column to destabilise the PIVOT, the following query maps the values in the TotalDue column in a number of well-known buckets and then uses those values as the X-axis. Using this technique there is no possibility of having rows fall outside the X-axis range:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; Pvt.*         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;datepart&lt;/font&gt;(yyyy, OrderDate) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [OrderDate],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;case&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;when&lt;/font&gt; TotalDue &amp;lt; 100 &lt;font color="#0000ff"&gt;then&lt;/font&gt; &lt;font color="#ff0000"&gt;'NoMargin'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;when&lt;/font&gt; TotalDue &amp;gt;= 100 and TotalDue &amp;gt; 1000 &lt;font color="#0000ff"&gt;then&lt;/font&gt; &lt;font color="#ff0000"&gt;'OnTarget'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;else&lt;/font&gt; &lt;font color="#ff0000"&gt;'ExtraBonus'&lt;/font&gt; &lt;font color="#0000ff"&gt;end as&lt;/font&gt; [Profit],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; PurchaseOrderId         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Purchasing.PurchaseOrderHeader) &lt;font color="#0000ff"&gt;as&lt;/font&gt; RawData         &lt;br /&gt;&lt;font color="#696969"&gt;pivot&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(PurchaseOrderId)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;for&lt;/font&gt; Profit         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;in&lt;/font&gt;([NoMargin], [OnTarget], [ExtraBonus])) &lt;font color="#0000ff"&gt;as&lt;/font&gt; Pvt         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; 1&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;OrderDate &lt;/td&gt;              &lt;td&gt;NoMargin &lt;/td&gt;              &lt;td&gt;OnTarget &lt;/td&gt;              &lt;td&gt;ExtraBonus &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2001 &lt;/td&gt;              &lt;td&gt;0 &lt;/td&gt;              &lt;td&gt;4 &lt;/td&gt;              &lt;td&gt;4 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2002 &lt;/td&gt;              &lt;td&gt;7 &lt;/td&gt;              &lt;td&gt;105 &lt;/td&gt;              &lt;td&gt;160 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2003 &lt;/td&gt;              &lt;td&gt;26 &lt;/td&gt;              &lt;td&gt;399 &lt;/td&gt;              &lt;td&gt;607 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2004 &lt;/td&gt;              &lt;td&gt;68 &lt;/td&gt;              &lt;td&gt;1070 &lt;/td&gt;              &lt;td&gt;1550 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As we've seen, the PIVOT operator does not provide any transformational capabilities that weren't possible in SQL Server 2000 (with liberal use of SELECT...CASE). However, as any experienced developer will know, the easier it is to express a reasonably complex SET-based problem, the better chance you have of getting it right, first time. Even more importantly, the poor developer required to maintain it (after you've moved on to greater conquests) will have a better chance of getting it right for the second time!&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-8296940258164873951?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/09/pivot-operator.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-3366047632390012872</guid><pubDate>Sat, 26 Aug 2006 15:47:00 +0000</pubDate><atom:updated>2008-02-04T22:57:08.204Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The APPLY Operator</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;We've come across the APPLY operator before when we were exploring &lt;a href="DY.11.DynamicManagementViewsPart2.aspx"&gt;DMVs&lt;/a&gt;. The APPLY operator has been added to the TSQL repertoire in SQL Server 2005. It fills an important gap with User-Defined Functions (UDFs).&lt;/p&gt;    &lt;p&gt;UDFs are less well-known than stored procedures (SPs) and are often overlooked by inexperienced SQL developers. &lt;a href="http://msdn2.microsoft.com/en-us/library/ms130214(SQL.90).aspx"&gt;BOL&lt;/a&gt; has a good &lt;a href="http://msdn2.microsoft.com/en-us/library/ms187650.aspx"&gt;write-up&lt;/a&gt; on how to choose between SPs and UDFs. For the context of this article, a brief overview of UDFs:&lt;/p&gt;    &lt;p&gt;UDFs come in two flavours: those that returns scalars and those that return tables. Scalar-valued UDFs are very straightforward. They take in a bunch of parameters and return a single scalar value. These are functions in the traditional VB / C# / C++ sense. Table-valued functions are a little different. They also can take in a bunch of parameters. However, they are able to return a table instead of a scalar value. This returned table is referenced in TSQL in the same manner as a table or a view. This makes them look like a cross between functions and views. In fact, there are two types of table-valued UDFs. Those that contain a single SELECT statement (they look very much like a derived table) called 'Inline Table-Valued Functions' and those that build-up the return table using procedural logic (they look like stored procedures) called 'Multistatement Table-Values Functions'. It is the latter-type that we are interested in.&lt;/p&gt;    &lt;p&gt;Multistatement table-valued functions are very powerful as they allow us to encapsulate complex procedural logic in a function when that logic cannot be implemented in a database view (i.e. you cannot express that desired output using a single set-based TSQL expression). You are free to construct the output rows in what ever manner you wish! The only restriction is that you need to declare the schema of the table that the UDF returns upfront as part of the UDF's declaration.&lt;/p&gt;    &lt;p&gt;As an example, consider the following table-valued UDF:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create function&lt;/font&gt; dbo.udf_DecomposeDns (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; @webServiceUrl &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;(1024))         &lt;br /&gt;&lt;font color="#0000ff"&gt;returns&lt;/font&gt; @dnsDomains &lt;font color="#0000ff"&gt;table&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; domain &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;(1024))         &lt;br /&gt;&lt;font color="#0000ff"&gt;as begin&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;-- Return no rows if NULL...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;if&lt;/font&gt; @webServiceUrl &lt;font color="#696969"&gt;is NULL&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; return&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;declare&lt;/font&gt; @dns &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;(1024)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;-- Remove the contents after the DNS...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;declare&lt;/font&gt; @slashIndex &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @slashIndex = &lt;font color="#ff00ff"&gt;charindex&lt;/font&gt;(N&lt;font color="#ff0000"&gt;'/'&lt;/font&gt;, @webServiceUrl)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;if&lt;/font&gt; @slashIndex &amp;gt; 0         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @dns = &lt;font color="#696969"&gt;left&lt;/font&gt;(@webServiceUrl, @slashIndex - 1)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;else&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @dns = @webServiceUrl&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;-- Decompose the DNS into its hierarchical parts...&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;declare&lt;/font&gt; @dotIndex int&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @dotIndex = &lt;font color="#ff00ff"&gt;charindex&lt;/font&gt;(N&lt;font color="#ff0000"&gt;'.'&lt;/font&gt;, @dns)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; @dnsDomains (domain) &lt;font color="#0000ff"&gt;values&lt;/font&gt; (@dns)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;while&lt;/font&gt; @dotIndex &amp;gt; 0 &lt;font color="#0000ff"&gt;begin&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @dns = &lt;font color="#696969"&gt;right&lt;/font&gt;(@dns, len(@dns) - @dotIndex)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; @dnsDomains (domain) values (@dns)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @dotIndex = &lt;font color="#ff00ff"&gt;charindex&lt;/font&gt;(N&lt;font color="#ff0000"&gt;'.'&lt;/font&gt;, @dns)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;end&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This UDF will decompose a URL into its hierarchical DNS domain constituents. For the sake of brevity the URL is assumed to consist of a DNS domain name followed by a URL-path (as in AdventureWorks' Purchasing.Vendor PurchasingWebServiceURL column). For example: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; dbo.udf_DecomposeDns(N&lt;font color="#ff0000"&gt;'www.extranet.fotia.com/order.asmx'&lt;/font&gt;)        &lt;br /&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;domain &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;www.extranet.fotia.co.uk &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;extranet.fotia.co.uk &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;fotia.co.uk &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;co.uk &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;uk &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;So far, all this is well within the capabilities of SQL Server 2000. Now, consider the following requirement: Generate a report that will contain the DNS composition of each Vendor's PurchasingWebServiceURL in AdventureWorks' Purchasing.Vendor table.&lt;/p&gt;    &lt;p&gt;To achieve this we want to decompose each URL into its domain hierarchy. For each vendor we would expect multiple rows to be returned, equivalent to the number of parts in that Vendor's URL. This would not be possible to do in a single SELECT statement in SQL Server 2000. The SELECT syntax did not have an operator / syntax that would allow a table-valued UDF to be re-evaluated repeatedly for every row it was being joined to. We could only execute a table-valued UDF once in a SELECT statement. We could pass a parameter to the UDF but the parameter value could not be from any column in the resulting set. It could only be a constant value or a declared variable.&lt;/p&gt;    &lt;p&gt;This is the gap that the APPLY operator fills in SQL Server 2005. It allows us to get the table-valued UDF to be executed for each row it is being joined to. In this manner the parameters to a table-valued UDF should be seen as the join criteria.&lt;/p&gt;    &lt;p&gt;The APPLY operator can be used in two forms: CROSS APPLY and OUTER APPLY. To understand the difference think of the table-valued UDF as a normal table you join to. You'd either use an INNER or an OUTER join. CROSS APPLY is an INNER JOIN, and OUTER APPLY is an OUTER JOIN. If the UDF returns an empty result set for a specific row, that row will be excluded if CROSS APPLY is used. However, if OUTER APPLY is used that row will still be included. NULL values will be substituted for the missing UDF row (identical to a traditional OUTER JOIN). It's that simple.&lt;/p&gt;    &lt;p&gt;CROSS APPLY over the Purchasing.Vendor table produces the following result:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; [Name], PurchasingWebServiceURL, domain         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Purchasing.Vendor         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;cross apply&lt;/font&gt; dbo.udf_DecomposeDns(PurchasingWebServiceURL)&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;PurchasingWebServiceURL &lt;/td&gt;              &lt;td&gt;domain &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------------- &lt;/td&gt;              &lt;td&gt;------------------------------ &lt;/td&gt;              &lt;td&gt;-------------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;www.adatum.com/ &lt;/td&gt;              &lt;td&gt;www.adatum.com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;www.adatum.com/ &lt;/td&gt;              &lt;td&gt;adatum.com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;www.adatum.com/ &lt;/td&gt;              &lt;td&gt;com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Trey Research &lt;/td&gt;              &lt;td&gt;www.treyresearch.net/ &lt;/td&gt;              &lt;td&gt;www.treyresearch.net &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Trey Research &lt;/td&gt;              &lt;td&gt;www.treyresearch.net/ &lt;/td&gt;              &lt;td&gt;treyresearch.net &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Trey Research &lt;/td&gt;              &lt;td&gt;www.treyresearch.net/ &lt;/td&gt;              &lt;td&gt;net &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Wide World Importers &lt;/td&gt;              &lt;td&gt;www.wideworldimporters.com/ &lt;/td&gt;              &lt;td&gt;www.wideworldimporters.com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Compare the output with OUTER APPLY. Note the NULL rows substituted for the missing rows from the UDF:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; [Name], PurchasingWebServiceURL, domain         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Purchasing.Vendor         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;outer apply&lt;/font&gt; dbo.udf_DecomposeDns(PurchasingWebServiceURL)&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;PurchasingWebServiceURL &lt;/td&gt;              &lt;td&gt;domain &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;---------------------------------- &lt;/td&gt;              &lt;td&gt;------------------------ &lt;/td&gt;              &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;International &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Electronic Bike Repair &amp;amp; Supplies &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Premier Sport, Inc. &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Comfort Road Bicycles &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Metro Sport Equipment &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Green Lake Bike Company &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Mountain Works &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Continental Pro Cycles &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;www.adatum.com/ &lt;/td&gt;              &lt;td&gt;www.adatum.com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;www.adatum.com/ &lt;/td&gt;              &lt;td&gt;adatum.com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;www.adatum.com/ &lt;/td&gt;              &lt;td&gt;com &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Trey Research &lt;/td&gt;              &lt;td&gt;www.treyresearch.net/ &lt;/td&gt;              &lt;td&gt;www.treyresearch.net &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Trey Research &lt;/td&gt;              &lt;td&gt;www.treyresearch.net/ &lt;/td&gt;              &lt;td&gt;treyresearch.net &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Trey Research &lt;/td&gt;              &lt;td&gt;www.treyresearch.net/ &lt;/td&gt;              &lt;td&gt;net &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Anderson's Custom Bikes &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Compete, Inc. &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;NULL &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;We are able to bring the full might of TSQL to bear on table-valued UDFs. For example, aggregations can be run on the result set:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; domain, &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) as [CountOf]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Purchasing.Vendor         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;outer apply&lt;/font&gt; dbo.udf_DecomposeDns(PurchasingWebServiceURL)         &lt;br /&gt;&lt;font color="#0000ff"&gt;group by&lt;/font&gt; domain         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; 2 &lt;font color="#0000ff"&gt;desc&lt;/font&gt;&lt;font color="#696969"&gt;;         &lt;br /&gt;          &lt;br /&gt;&lt;/font&gt;         &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;domain &lt;/td&gt;              &lt;td&gt;CountOf &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------------------------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;NULL &lt;/td&gt;              &lt;td&gt;98 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;com &lt;/td&gt;              &lt;td&gt;5 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;litwareinc.com &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;net &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;northwindtraders.com &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;proseware.com &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;treyresearch.net &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The APPLY operator is indeed a valuable addition to the TSQL. It allows a whole new modular and procedural approach to composing result sets. &lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-3366047632390012872?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/08/apply-operator.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-8755037212883707977</guid><pubDate>Mon, 07 Aug 2006 07:04:00 +0000</pubDate><atom:updated>2008-02-04T22:54:29.155Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>Introducing Common Table Expressions (CTEs)</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;The concept of CTEs has been around in SQL Server for a long time. Anyone who's used derived tables before will immediately recognise and understand CTEs. Think of CTEs as formalised sub-queries or temporary views that can be defined and referenced in a single TSQL statement.&lt;/p&gt;    &lt;p&gt;I've labelled this article as an introduction as CTEs bring a lot to the TSQL table, which we'll explore in much greater detail in future articles.&lt;/p&gt;    &lt;p&gt;Consider the following requirement: you need a result-set that returns the sum of all products sold per product. For each product, you'll need to return a bunch of metadata about that product. In addition, your boss (for reasons only known to him) want these results only for products that are either red in colour or have more than 10 transactions! In SQL Server 2000 you might start writing something similar to the following:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; [Name], Color, &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [SumQuantity]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Production.TransactionHistory T         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId = T.ProductId         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; P.Color = &lt;font color="#ff0000"&gt;'red'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;or&lt;/font&gt; (&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory THinner         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; THinner.ProductId = P.ProductID) &amp;gt;= 10         &lt;br /&gt;&lt;font color="#0000ff"&gt;group by&lt;/font&gt; [Name], Color&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Whilst this query gets the job done, it does look a bit messy as we're having to perform a correlated sub-query for each ProductID, evaluate it as a scalar and then determine whether or not we're going to calculate the sum of all quantities of all transactions for the associated ProductID. In addition, this TSQL is misrepresenting its purpose in the GROUP BY clause. The requirement stated that the summation needs to be performed per ProductID. We probably got lucky here as Name is unique per Product. If it wasn't we could get some unpredictable results (e.g. if there were two Pink FooBar's that differed only by some other metadata). Any column in the SELECT list that is not an aggregate must be included in the GROUP BY. It is the mix of aggregate and non-aggregate data that has lead to this mistake. A correct way to write this query would be to use a derived table:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name], P.Color, PA.SumQuantity         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfTransactions],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [SumQuantity]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;&amp;#160;&amp;#160;&amp;#160; group by&lt;/font&gt; ProductID) &lt;font color="#0000ff"&gt;as&lt;/font&gt; PA         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId = PA.ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; PA.[CountOfTransactions] &amp;gt;= 10         &lt;br /&gt;&amp;#160;&lt;font color="#696969"&gt;or&lt;/font&gt; P.Color = &lt;font color="#ff0000"&gt;'red'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This is a much better as we have separated out the aggregate from the additional metadata portion of the query. The inner select statement is a derived table that is uncorrelated with the outer select so that it can execute independently. We have given this derived table the PA alias. Whilst this has given the TSQL much better structure, it does have some limitations. If we wanted to refer to the derived table again we would have to copy-paste it again into the select. For example, if we now wanted to include number of transactions the modulo 10 of the ProductID contained (simulating an arbitrary Product-to-Product relationship), we'd have to copy-and-paste the whole derived table again:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name], P.Color, PA1.SumQuantity, PA2.ProductID, PA2.SumQuantity         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfTransactions],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [SumQuantity]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;group by&lt;/font&gt; ProductID) &lt;font color="#0000ff"&gt;as&lt;/font&gt; PA1         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId = PA1.ProductID         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfTransactions],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [SumQuantity]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;group by&lt;/font&gt; ProductID) &lt;font color="#0000ff"&gt;as&lt;/font&gt; PA2         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId % 10 = PA2.ProductId         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; PA1.[CountOfTransactions] &amp;gt;= 10         &lt;br /&gt;&amp;#160;&lt;font color="#696969"&gt;or&lt;/font&gt; P.Color = &lt;font color="#ff0000"&gt;'red'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Now to introduce CTEs: Rewriting the above query to use a CTE will look as follows,&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;with&lt;/font&gt; ProductAggregate(ProductId, CountOfTransactions, SumQuantity)         &lt;br /&gt;&lt;font color="#0000ff"&gt;as&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID, &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*), &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(Quantity)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;group by&lt;/font&gt; ProductID)         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name], P.Color, PA1.SumQuantity, PA2.ProductID, PA2.SumQuantity         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; ProductAggregate PA1         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId = PA1.ProductID         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; ProductAggregate PA2         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId % 10 = PA2.ProductId         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; PA1.CountOfTransactions &amp;gt;= 10         &lt;br /&gt;or P.Color = &lt;font color="#ff0000"&gt;'red'&lt;/font&gt;;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As a developer, think of a CTE as a local variable declaration, having scope of a single TSQL statement. The WITH statement is the declaration that defines the columns (schema) of the CTE. The AS portion defines the implementation of the CTE. Once declared, the CTE exists as any normal table would in the TSQL statement. As demonstrated above, the CTE can be reused as many times are required in the TSQL statement. In the example above we're using it twice (with different aliases) yet we don't have to re-declare it (as we were required to with derived tables). CTE re-use extends to UNIONs as well. For example:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;with&lt;/font&gt; ProductAggregate(ProductId, CountOfTransactions, SumQuantity)         &lt;br /&gt;&lt;font color="#0000ff"&gt;as&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select&lt;/font&gt; ProductID, &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*), sum&lt;font color="#ff00ff"&gt;(&lt;/font&gt;Quantity)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;group by&lt;/font&gt; ProductID)         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name], P.Color, PA.SumQuantity         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; inner join ProductAggregate PA         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId = PA.ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; PA.CountOfTransactions &amp;gt;= 10         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;union&lt;/font&gt; all         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name], P.Color, PA.SumQuantity         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; inner join ProductAggregate PA         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; P.ProductId = PA.ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; P.Color = &lt;font color="#ff0000"&gt;'red'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;A couple of other points to note about CTEs: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;A CTE can only be used in the TSQL statement it is declared in. If you find yourself repeating a CTE a number of times then you should consider 'upgrading' it to a database view in its own right (assuming you're not using a CTE-only feature - mentioned below). &lt;/li&gt;      &lt;li&gt;CTE's can be used in SELECT / UPDATE / DELETE / INSERT statements, anywhere a table can be used. &lt;/li&gt;      &lt;li&gt;The previous statement of any TSQL statement that uses a CTE must be terminated with a semi-colon. This can be a bit infuriating if you're not used to using semi-colons in your TSQL but try to get into the habit of terminating all your TSQL statements with semi-colons (this works even in SQL Server 2000). &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;Anyone that's looked at CTEs before will have noted that I have not yet mentioned the one feature that everyone immediately highlights when discussing CTE's, i.e. CTE's can be used recursively to join on themselves. In my opinion, the recursive properties of CTEs will only be used in the very few queries that need to unroll / traverse hierarchies. The ability to write modular set-based queries is the primary reason we should all start using CTEs. They make queries easier to maintain and allow complex queries to be broken down into their logical components without sacrificing the power of set-based queries (as opposed to poorly performing but easier to read procedural TSQL).&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-8755037212883707977?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/08/introducing-common-table-expressions.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-4767917546423174704</guid><pubDate>Mon, 24 Jul 2006 20:52:00 +0000</pubDate><atom:updated>2008-02-04T22:52:46.841Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>data types</category><title>VARCHAR(MAX)</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;VARCHAR(MAX), NVARCHAR(MAX) and VARBINARY(MAX) are the SQL Server 2005 replacements for TEXT, NTEXT and IMAGE large. Not only are the replacements far better named than their SQL Server 2000 counterparts, they are also far easier to use. However, before we start rejoicing in their virtues, a word of caution. Just because you can now assign a 2GB string to a local variable doesn't mean you should! The ease with which large-value data types can be accessed and manipulated means that we have a greater need to treat the data responsibly and be aware that we could be handling a large amount of data in a local variable. It is now more important to be aware that you could be moving mountains of data in some very innocent looking TSQL. We must make sure we don't use these powerful capabilities inappropriately. &lt;i&gt;&amp;quot;With great power comes great responsibility&amp;quot;&lt;/i&gt; - Spiderman!&lt;/p&gt;    &lt;p&gt;In BOL you'll notice that a distinction is drawn between large &lt;font style="text-decoration: underline"&gt;value&lt;/font&gt; types and large &lt;font style="text-decoration: underline"&gt;object&lt;/font&gt; data types. The new MAX types are part of the large value types and the TEXT / NTEXT / IMAGE family are part of the large object types. The only distinction I can find between the two that warrants this separation is the use of the infamous text pointer that was used with large object types. So when you come across large value versus large object types in BOL, this is what they mean.&lt;/p&gt;    &lt;p&gt;TEXT, NTEXT and IMAGE are notoriously difficult to use within the database (i.e. within TSQL or stored procedures). It is not possible to use TEXT, NTEXT and IMAGE data types as local variables. SQL Server allows us to use a level of indirection through a 'text pointer'. Essentially this is a handle to a TEXT / NTEXT / IMAGE column of a specific row. In order to access the data in the column we first have to: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;read the text pointer of the column of the row we were interested in (using the TEXTPTR function), then &lt;/li&gt;      &lt;li&gt;use the text pointer as a parameter to the READTEXT, WRITETEXT or UPDATETEXT functions. &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;In addition, we are responsible for controlling the locking / isolation of the text pointer from changes while we are reading it through the use of HOLDLOCK locking hints and within transactions. This prevents anyone from changing the data while we are 'dereferencing' the text pointer. Alternatively you can just SELECT the data into VARCHAR / NVARCHAR / VARBINARY local variables and risk truncating the data. You can also SELECT the column INTO another table that has TEXT / NTEXT / IMAGE columns.&lt;/p&gt;    &lt;p&gt;Whilst this sounds like many hoops to jump through, it is actually beneficial as developers resort to handling TEXT / NTEXT / IMAGE data outside the database. Managing these data types from ADO.NET / OLE DB is far easier than trying to manipulate the data within the database. The data can be extracted in plain SELECT statements (instead of READTEXT) and be streamed back to the client in chunks. In addition, this is a far more scalable alternative as it doesn't consume precious memory resources on the SQL Server.&lt;/p&gt;    &lt;p&gt;New to SQL Server are the 'MAX' versions of VARCHAR / NVARCHAR / VARBINARY. There are the replacements for TEXT / NTEXT / IMAGE that have now been marked as deprecated and will be removed in a future version of SQL Server. These 'MAX' versions are meant to address the in-database manipulation difficulties of their predecessors. They are also designed to be able to handle small to very large data sizes efficiently without having to choose between VARCHAR / NVARCHAR / VARBINARY and TEXT / NTEXT / IMAGE as before.&lt;/p&gt;    &lt;p&gt;All of the following capabilities apply equally to VARCHAR / NVARCHAR / VARBINARY. For brevity I'll just refer to VARCHAR.&lt;/p&gt;    &lt;p&gt;You can now declare local variables of type VARCHAR(MAX) so that you can construct strings greater than 8,000 bytes (8,000 VARCHAR characters or 4,000 NVARCHAR / Unicode characters) in size and select VARCHAR(MAX) column values directly into your local variables. This now makes cursors using large value types usable in the database (again, just because you can doesn't mean you should&amp;#8230;).&lt;/p&gt;    &lt;p&gt;The concept of a text pointer no longer exists. Goodbye TEXTPTR, TEXTVALID, READTEXT, UPDATETEXT and WRITETEXT!&lt;/p&gt;    &lt;p&gt;All of the string functions have been 'upgraded' so that they can be used with VARCHAR(MAX). Previous only PATINDEX, DATALENGTH and SUBSTRING could be used with a TEXT column. Now all of the family favourites like LEN, LEFT, RIGHT, etc. can now be used on large-value data types. However, note that there are implicit overloads to some of these string functions. As an example, execute the following TSQL and note the output:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @smallVarChar &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(1)         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @smallVarChar = &lt;font color="#ff0000"&gt;'*'&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;datalength&lt;/font&gt;(&lt;font color="#ff00ff"&gt;replicate&lt;/font&gt;(@smallVarChar, 8001)) &lt;font color="#0000ff"&gt;as&lt;/font&gt; LengthOfSmallVarChar         &lt;br /&gt;go        &lt;br /&gt;        &lt;br /&gt;         &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;LengthOfSmallVarChar &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;8000 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @maxVarChar &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @maxVarChar = &lt;font color="#ff0000"&gt;'*'&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;datalength&lt;/font&gt;(&lt;font color="#ff00ff"&gt;replicate&lt;/font&gt;(@maxVarChar, 8001)) &lt;font color="#0000ff"&gt;as&lt;/font&gt; LengthOfMaxVarChar         &lt;br /&gt;go        &lt;br /&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;LengthOfMaxVarChar &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;8001 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The first TSQL batch calls the REPLICATE function with a small (non-MAX) VARCHAR. In return, REPLICATE returns a non-MAX VARCHAR that is truncated at 8,000 characters. However, if we pass in a VARCHAR(MAX) to REPLICATE we get a VARCHAR(MAX) back out and no truncation. So watch out for this. The transition from small to MAX VARCHARs is not implicit. If your requirement is to accommodate large VARCHARs then this needs to be baked into your code from the outset.&lt;/p&gt;    &lt;p&gt;VARCHAR(MAX) and its counterpart TEXT can interact with each other. For example, the following TSQL is perfectly legal:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Create a table with a TEXT column...          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; bigTable (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; col1 &lt;font color="#0000ff"&gt;text&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt;)         &lt;br /&gt;go         &lt;br /&gt;&lt;font color="#008000"&gt;         &lt;br /&gt;-- Add some data...           &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @col1 = &lt;font color="#ff00ff"&gt;replicate&lt;/font&gt;(&lt;font color="#ff00ff"&gt;cast&lt;/font&gt;(&lt;font color="#ff0000"&gt;'*'&lt;/font&gt; &lt;font color="#0000ff"&gt;as varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)), 10000)         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; bigTable (col1)         &lt;br /&gt;&lt;font color="#0000ff"&gt;values&lt;/font&gt; (@col1)         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Read out the TEXT into a VARCHAR(MAX)          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; @col1 = col1         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; bigTable         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;len&lt;/font&gt;(@col1)         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This is very useful as it means you don't have to redefine all of your TEXT columns in your database when you migrate from SQL Server 2000 to 2005. If you need to manipulate TEXT data in the database just SELECT it into a VARCHAR(MAX) variable.&lt;/p&gt;    &lt;p&gt;Lastly, a couple of points to keep in mind with VARCHAR(MAX): &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;The size range for VARCHAR is now 1 to 8,000 or MAX. Note there is nothing between 8,000 and MAX. &lt;/li&gt;      &lt;li&gt;VARCHAR(MAX) is more than a replacement from TEXT. It works just as well with short strings as well as long ones. Therefore, if there is any possibility that you'll exceed 8,000 bytes then use VARCHAR(MAX). &lt;/li&gt;      &lt;li&gt;The maximum size for a VARCHAR(MAX) is 2^31 - 1 bytes / characters. &lt;/li&gt;      &lt;li&gt;The &lt;b&gt;[text in row]&lt;/b&gt; option for a table remains specific to TEXT / NTEXT / IMAGE data types. This option lets you tell SQL Server at which size to move TEXT / NTEXT / IMAGE data out of the table's data pages and into its own page allocation (assuming that the [text in row] option is ON). The new &lt;b&gt;[large value types out of row]&lt;/b&gt; setting replaces the TEXT option for VARCHAR(MAX). However, this option is now binary. It is either ON or OFF. The size at which MAX types are moved out of a table's data pages and into its own data pages is now fixed at 8,000 bytes if &lt;b&gt;[large value types out of row]&lt;/b&gt; is set to OFF. &lt;/li&gt;      &lt;li&gt;The UPDATE statement has been enhanced to provide a replacement for UPDATETEXT. To update a portion of a MAX data type use the .WRITE suffix. For example: &lt;/li&gt;   &lt;/ul&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Create a table with a VARCHAR(MAX) column...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; bigTable (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;) &lt;font color="#696969"&gt;not null&lt;/font&gt;)         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Add some data...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @col1 &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @col1 = &lt;font color="#ff00ff"&gt;replicate&lt;/font&gt;(&lt;font color="#ff00ff"&gt;cast&lt;/font&gt;(&lt;font color="#ff0000"&gt;'1'&lt;/font&gt; as &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)), 10000)         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; bigTable (col1)         &lt;br /&gt;&lt;font color="#0000ff"&gt;values&lt;/font&gt; (@col1)         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Update a portion of the VARCHAR(MAX)...          &lt;br /&gt;-- Update the 4th, 5th and 6th characters, truncate the rest           &lt;br /&gt;-- of col1.           &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;update&lt;/font&gt; bigTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; col1.write(&lt;font color="#ff0000"&gt;'222'&lt;/font&gt;, 3, &lt;font color="#696969"&gt;null&lt;/font&gt;)         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; bigTable         &lt;br /&gt;go        &lt;br /&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;col1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;111222 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Next up are CTEs (Common Table Expressions).&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-4767917546423174704?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/07/varcharmax.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-3338796875979829275</guid><pubDate>Thu, 06 Jul 2006 22:19:00 +0000</pubDate><atom:updated>2008-02-04T22:50:40.732Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The OUTPUT Clause</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;If any new feature of TSQL will be a big hit it will be the OUTPUT clause. This is one new addition to TSQL that you'll use everywhere! How many times have you written this code?&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- A typical table...          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; someTable (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Id &lt;font color="#0000ff"&gt;int identity&lt;/font&gt;(1,1) &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; SomeData &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(100) &lt;font color="#696969"&gt;not null&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @id &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#008000"&gt;-- Insert the data.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; someTable (someData)         &lt;br /&gt;&lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'DataOne'&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Figure out what identity the row was given.          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @id = &lt;font color="#ff00ff"&gt;scope_identity&lt;/font&gt;()&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#008000"&gt;-- Return it...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; @id &lt;font color="#0000ff"&gt;as&lt;/font&gt; Id&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Isn't it tedious that you have to read the data that you just inserted as a second operation following the insert? The whole reason scope_identity exists is to protect you from any context that might be introduced between these two operations that you may not be aware of, like triggers (aarrgh!). After all, why can't you just write the following...&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @id &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Insert the data, better...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;insert &lt;/font&gt;someTable (someData)         &lt;br /&gt;&lt;font color="#0000ff"&gt;output&lt;/font&gt; inserted.Id         &lt;br /&gt;&lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'DataTwo'&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This is exactly how the OUTPUT clause works. It allows data that is inserted / updated / deleted to be returned. Anyone that's done any work with triggers would have immediately recognised the 'virtual' inserted table. In fact, drawing parallels between triggers and the OUTPUT clause is the best way to understand what's going on here. The OUTPUT clause allows you to access the virtual trigger tables inline with your INSERT / UPDATE / DELETE statements. You essentially get 'on-the-fly' trigger access. For INSERT you have the inserted table, for update you have both the inserted and deleted tables, and for DELETE just the deleted table. Simple, isn't it? In the above example we use the OUTPUT clause to get to the identity column we inserted. It could have been any column in the table. Think how useful this would be for retrieving timestamp values or computed columns.&lt;/p&gt;    &lt;p&gt;Have a look at these examples to see what some of the practical applications are:&lt;/p&gt;    &lt;h2&gt;(1) What did I just delete?&lt;/h2&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;delete&lt;/font&gt; someTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;output&lt;/font&gt; deleted.*         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 1&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This query returns all of the data that was just deleted. Great for audit records! It also prevents having to take out locks on the rows you're going to delete so that the values read are the ones deleted. No encompassing transaction required here.&lt;/p&gt;    &lt;h2&gt;(2) Before and after values&lt;/h2&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;update&lt;/font&gt; someTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; someData = &lt;font color="#ff0000"&gt;'DataThree'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;output&lt;/font&gt; deleted.someData &lt;font color="#0000ff"&gt;as&lt;/font&gt; Before,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; inserted.someData &lt;font color="#0000ff"&gt;as&lt;/font&gt; After,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;difference&lt;/font&gt;(deleted.someData, inserted.someData) &lt;font color="#0000ff"&gt;as&lt;/font&gt; Delta         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 2&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;Before &lt;/td&gt;              &lt;td&gt;After &lt;/td&gt;              &lt;td&gt;Delta &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;--------- &lt;/td&gt;              &lt;td&gt;---------- &lt;/td&gt;              &lt;td&gt;------ &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;DataTwo &lt;/td&gt;              &lt;td&gt;DataThree &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This query will return the values for someData as it was before the update and it is after the update in a single row. In addition, note that the OUTPUT clause is defined as a &lt;dml_select_list /&gt;meaning that any expressions (i.e. functions / calculations, etc.) you could do in a SELECT you can do in an OUTPUT.&lt;/p&gt;    &lt;h2&gt;(3) Deleting multiple rows&lt;/h2&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Declare a table variable.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @someTableTemp &lt;font color="#0000ff"&gt;table&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Id &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; someData &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(100) &lt;font color="#696969"&gt;not null&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Capture the output into a table variable.          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;delete&lt;/font&gt; someTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;output&lt;/font&gt; deleted.id, deleted.someData         &lt;br /&gt;&lt;font color="#0000ff"&gt;into&lt;/font&gt; @someTableTemp         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; (Id, someData)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- What was deleted...          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; @someTableTemp&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The output can also be captured into a table variable. It doesn't have to be a table variable. It could be any permanent table. In this case think of the OUTPUT ... INTO as an INSERT ... SELECT. Just for the hell of it you can write crazy TSQL like this:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- The delete that never was!&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;delete&lt;/font&gt; someTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;output&lt;/font&gt; deleted.someData         &lt;br /&gt;&lt;font color="#0000ff"&gt;into&lt;/font&gt; someTable (someData)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Lastly, a couple of points to remember: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;The values in the output virtual tables will be populated after any triggers for the table have fired, so remember that you are at the back of the queue. &lt;/li&gt;      &lt;li&gt;Inserting / updating / deleting data and then re-reading the rows affected is always dangerous because it makes you susceptible to deadlocks. Using the OUTPUT clause will avoid this as the action and read occur as a single unit. In SQL Server 2000 there was a trick with the UPDATE statement that allowed you to avoid re-reading updates if you're interested in the value of a specific column that was updated (this only works if the UPDATE affects a single row). Consider the following table: &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; ReserveRange (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; IdName &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(100) &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Id &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; ReserveRange (IdName, Id)         &lt;br /&gt;&lt;font color="#0000ff"&gt;values&lt;/font&gt; (&lt;font color="#ff0000"&gt;'Id#1'&lt;/font&gt;, 0)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;You want to use this table to reserve ranges of Id's. You need to: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;read the current Id, &lt;/li&gt;      &lt;li&gt;increment it by a certain amount, &lt;/li&gt;      &lt;li&gt;return the range that has been reserved in a thread-safe manner. &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;Normally developers wrap the TSQL in a transaction and use a UPDLOCK locking hint to serialize access to the table like this (a couple of variations of this are possible but all require the transaction and a long-running update lock):&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;begin tran&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @range &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @range = 100&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @id &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Get the current value.          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; @id = Id&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; ReserveRange &lt;font color="#0000ff"&gt;with&lt;/font&gt;(updlock)         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; IdName = &lt;font color="#ff0000"&gt;'Id#1'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Reserve a range.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;update&lt;/font&gt; ReserveRange         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; id = @id + @range         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; IdName = &lt;font color="#ff0000"&gt;'Id#1'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;-- Return the range         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; @id + 1 &lt;font color="#0000ff"&gt;as&lt;/font&gt; LowRange,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; @id + @range &lt;font color="#0000ff"&gt;as&lt;/font&gt; HighRange&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;commit tran&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The trick is to notice that the UPDATE statement supported triple assignments. Have a look at the following:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @range &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @range = 100&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;declare&lt;/font&gt; @id &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Update the current range and assign it to          &lt;br /&gt;-- a local variable           &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;update&lt;/font&gt; ReserveRange         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; @id = id = id + @range         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; IdName = &lt;font color="#ff0000"&gt;'Id#1'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Return the range&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; @id - @range + 1 &lt;font color="#0000ff"&gt;as&lt;/font&gt; LowRange,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; @id &lt;font color="#0000ff"&gt;as&lt;/font&gt; HighRange&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Note, no encompassing transaction and no long-running locks. Next we'll look at VARCHAR(MAX).&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-3338796875979829275?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/07/output-clause.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-688020780224065355</guid><pubDate>Thu, 29 Jun 2006 18:41:00 +0000</pubDate><atom:updated>2008-02-04T22:48:46.538Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>dmv</category><title>Dynamic Management Views Part II</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;In this instalment we'll be looking at a single DMV. This DMV provides such a wealth of information that it will take quite a few passes to cover it in depth. If anything, this DMV will make detecting and troubleshooting poorly performing queries something that can be easily done rather than requiring specialised, and potentially performance damaging tools like Profiler. The problem with Profiler is that it is great for debugging and dissecting SQL Server requests in a controlled environment (such as your development or test environment). It isn't good at running perpetually in a production environment, logging every single query that is executed, with its query plan and runtime statistics. Firstly, the amount of data it would generate for even a modest enterprise-level application would be unmanageable. Secondly, depending on what events are being captured and their frequency, the performance of SQL Server could be adversely affected. I'm not saying that SQL Profiler shouldn't be used in a production environment to assist with investigating and diagnosing a specific issue. Rather, it can't be used by the DBA's to monitor the overall performance of SQL Server, so that they can look for potentially troublesome queries, and be proactive in the detection of performance issues. This task normally falls on the middle-tier / application code to log execution times or update custom performance counters. Even then, it is normally support engineers that look at these logs rather than the DBAs.&lt;/p&gt;    &lt;p&gt;In SQL Server 2005 we now have the &lt;b&gt;sys.dm_exec_query_stats&lt;/b&gt; DMV. This view contains a row for each query plan in SQL Server's query plan cache. SQL Server is built on the principle that you prepare a query plan once and execute it many times. Caching of the query plan is central to the architecture of SQL Server. Exposing this core part of the internal architecture really means that there are no more secrets!&lt;/p&gt;    &lt;p&gt;So, what can we see in this DMV? Firstly, we can obtain a handle to the query plan's TSQL text. In SQL Server 2000 we had to use DBCC INPUTBUFFER or the ::fn_get_sql function (which has now been marked as to-be-deprecated) to be able to see what TSQL a session was executing. &lt;/p&gt;    &lt;p&gt;Even then DBCC INPUTBUFFER just showed the last statement executed and ::fn_get_sql only returned the TSQL as it was executing. Neither provided any kind of insight into the actual content of the query plan cache. The only way I knew of was to take a crash dump of the SQL Server process and dissect the dump using Microsoft PSS tools that understood SQL Server's memory layout.&lt;/p&gt;    &lt;p&gt;To have a look at every TSQL statement in the query plan cache, execute the following query over the sys.dm_exec_query_stats DMV:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;db_name&lt;/font&gt;(sql_text.dbid) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Database],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;case&lt;/font&gt; &lt;font color="#ff00ff"&gt;db_id&lt;/font&gt;()         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;when&lt;/font&gt; sql_text.dbid &lt;font color="#0000ff"&gt;then&lt;/font&gt; &lt;font color="#ff00ff"&gt;object_name&lt;/font&gt;(sql_text.objectid)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;else&lt;/font&gt; cast(sql_text.objectid &lt;font color="#0000ff"&gt;as sysname&lt;/font&gt;) &lt;font color="#0000ff"&gt;end as&lt;/font&gt; [Object],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; sql_text.&lt;font color="#0000ff"&gt;text as&lt;/font&gt; [TSQL]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;sys.dm_exec_query_stats&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;cross apply&lt;/font&gt; sys.dm_exec_sql_text(sql_handle) &lt;font color="#0000ff"&gt;as&lt;/font&gt; sql_text&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;If you don't see any rows returned, execute this query twice and you'll see the query above appear in the cache! If you see too many rows use DBCC FREEPROCCACHE to empty out the query plan cache.&lt;/p&gt;    &lt;p&gt;There are a couple of things in this query you might not have seen before. For example, the APPLY operator is new. We'll look at it in detail in another article but for now just think of it as an inner join from a query to a table-valued function. The table-valued function will be invoked for each row in the query. If the table valued function returns multiple rows a query row, the query row will be returned multiple times (as you'd expect from an inner join).&lt;/p&gt;    &lt;p&gt;The CROSS APPLY is taking each sql_handle in the sys.dm_exec_query_stats DMV and uses the sys.dm_exec_sql_text function to return the database, object id and text of the query plan. The case logic is there just to turn the objectid into its object name if the current database is the same as object's database.&lt;/p&gt;    &lt;p&gt;OK, so this is all very interesting, but how does this help me see what the performance of these queries is like? Well, for each query plan, SQL Server will maintain an aggregate of: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;The number of times query plan has been recompiled (find all your SPs containing temp tables here!) &lt;/li&gt;      &lt;li&gt;A handle to the query plan (we'll look at this in depth later). &lt;/li&gt;      &lt;li&gt;The last time the query was executed. &lt;/li&gt;      &lt;li&gt;The number of times it has been executed (how easy is it to spot the most used queries now?!). &lt;/li&gt;      &lt;li&gt;The total amount of the following resources consumed by all invocations of the query plan as well as the least and greatest amount of CPU consumed by a single invocation of the query plan:        &lt;ul&gt;         &lt;li&gt;CPU &lt;/li&gt;          &lt;li&gt;Physical Reads &lt;/li&gt;          &lt;li&gt;Logical Writes &lt;/li&gt;          &lt;li&gt;Logical Reads &lt;/li&gt;          &lt;li&gt;CLR Time (if you use CLR in SQL you'd be able to easily spot a performance problem with it!) &lt;/li&gt;          &lt;li&gt;Elapsed Time &lt;/li&gt;       &lt;/ul&gt;     &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;Wow! Christmas definitely came early this year! This mind boggles at the variety of queries that could be constructed to slice and dice this aggregated data. When digging into the TSQL note that the query plan may only be for a fragment of the TSQL text. You need to use the statement_start_offset and statement_end_offset values to find the chunk. Use this helper function to extract the TSQL fragment:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create function&lt;/font&gt; dbo.GetTSQLFragment (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; @tsql &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;),         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; @start_offset &lt;font color="#0000ff"&gt;int&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; @end_offset &lt;font color="#0000ff"&gt;int&lt;/font&gt;) &lt;font color="#0000ff"&gt;returns nvarchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)         &lt;br /&gt;&lt;font color="#0000ff"&gt;with execute as&lt;/font&gt; caller         &lt;br /&gt;&lt;font color="#0000ff"&gt;as begin&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;declare&lt;/font&gt; @len &lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @len = (&lt;font color="#0000ff"&gt;case&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;when&lt;/font&gt; @end_offset = -1 then &lt;font color="#ff00ff"&gt;len&lt;/font&gt;(@tsql) * 2         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;else&lt;/font&gt; @end_offset &lt;font color="#0000ff"&gt;end&lt;/font&gt; - @start_offset) / 2&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;declare&lt;/font&gt; @TSQLFragment &lt;font color="#0000ff"&gt;nvarchar&lt;/font&gt;(&lt;font color="#ff00ff"&gt;max&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;set&lt;/font&gt; @TSQLFragment = &lt;font color="#ff00ff"&gt;substring&lt;/font&gt;(@tsql, @start_offset / 2 + 1, @len)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;return&lt;/font&gt; @TSQLFragment&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The query now becomes:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;db_name&lt;/font&gt;(sql_text.dbid) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Database],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;case&lt;/font&gt; &lt;font color="#ff00ff"&gt;db_id&lt;/font&gt;()         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;when&lt;/font&gt; sql_text.dbid &lt;font color="#0000ff"&gt;then&lt;/font&gt; &lt;font color="#ff00ff"&gt;object_name&lt;/font&gt;(sql_text.objectid)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;else&lt;/font&gt; &lt;font color="#ff00ff"&gt;cast&lt;/font&gt;(sql_text.objectid &lt;font color="#0000ff"&gt;as sysname&lt;/font&gt;) &lt;font color="#0000ff"&gt;end as&lt;/font&gt; [Object],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; sql_text.&lt;font color="#0000ff"&gt;text as&lt;/font&gt; [AllTSQL],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; dbo.GetTSQLFragment(         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; sql_text.&lt;font color="#0000ff"&gt;text&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; statement_start_offset,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; statement_end_offset) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [FragmentTSQL]         &lt;br /&gt;&lt;font color="#0000ff"&gt;fro&lt;/font&gt;m sys.dm_exec_query_stats stats         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;cross apply&lt;/font&gt; sys.dm_exec_sql_text(sql_handle) &lt;font color="#0000ff"&gt;as&lt;/font&gt; sql_text&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;To see the fragments in action, run the following query and then have another look at the query plan cache using the above query:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Culture&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Location&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;You'll see the above query repeated three times with each select statement in its own TSQL fragment and will therefore have its own query plan. However, note the presence of the plan_handle column. This can be used to group query plans together as they were submitted as part of the same batch. Include plan_handle in the SELECT from sys.dm_exec_query_stats, execute variations of the triple-select batch statement above (just reordering is sufficient) and observe how the query plans for the same SELECT statements multiply. For example, run the following batches: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Location&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Culture&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Culture&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Location&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Location&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Product&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.Culture&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;It's not hard too see why ad-hoc TSQL is bad when you see what it does to your query plan cache. For stored procedures, no matter how large or complex the stored procedure is, there will only ever be one query plan for it. However, unfortunately there is some fine print. Query plans that use different SET (environment) options cannot be shared. More on this later.&lt;/p&gt;    &lt;p&gt;Execute the following batch and have a look at the query plan cache. There is only one query plan for the stored procedure, irrespective of the ordering and parameters:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 800, &lt;font color="#ff0000"&gt;'2003/01/01'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 801, &lt;font color="#ff0000"&gt;'2005/01/01'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 800, &lt;font color="#ff0000"&gt;'2004/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 800, &lt;font color="#ff0000"&gt;'2001/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 799, &lt;font color="#ff0000"&gt;'2004/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 800, &lt;font color="#ff0000"&gt;'2003/01/01'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 780, &lt;font color="#ff0000"&gt;'2004/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 721, &lt;font color="#ff0000"&gt;'2001/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Now for the fine print. There are a number of factors that affect the optimiser's ability to re-use query plans. It is important to be aware of the factors as query plan re-use == performance (compiling query plans can be expensive). One of the most prominent factors is the SET (environment) options of the connection executing the query. For a query plan to be re-used, the environment options of the cached query plan needs to be the same as the query being executed. Query optimisation and query plan re-use is an enormous topic that we'll cover over the course of the 'Discover SQL Server 2005' articles. However, for now, note that executing the following query results in two different query plans being cached for the same stored procedure. Execute the query then have a look in the cache using the above DMV query:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;set ansi_warnings on          &lt;br /&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 721, &lt;font color="#ff0000"&gt;'2001/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;set ansi_warnings off          &lt;br /&gt;exec&lt;/font&gt; dbo.uspGetBillOfMaterials 721, &lt;font color="#ff0000"&gt;'2001/02/08'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;More often than not environment options are a non-issue as all connections to an application's database will be instantiated by some common data access layer code, so all application connections inherently use the same environment options.&lt;/p&gt;    &lt;p&gt;Lastly, to illustrate the power of this DMV, look at this. Want to know which are the 50 most CPU intensive queries in your database? This is all that you need:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select top&lt;/font&gt; 50 &lt;font color="#ff00ff"&gt;db_name&lt;/font&gt;(dbid) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Database],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;case&lt;/font&gt; &lt;font color="#ff00ff"&gt;db_id&lt;/font&gt;()         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;when&lt;/font&gt; sql_text.dbid &lt;font color="#0000ff"&gt;then&lt;/font&gt; &lt;font color="#ff00ff"&gt;object_name&lt;/font&gt;(sql_text.objectid)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;else&lt;/font&gt; &lt;font color="#ff00ff"&gt;cast&lt;/font&gt;(sql_text.objectid &lt;font color="#0000ff"&gt;as sysname&lt;/font&gt;) &lt;font color="#0000ff"&gt;end as&lt;/font&gt; [Object],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; sql_text.&lt;font color="#0000ff"&gt;text as&lt;/font&gt; [AllTSQL],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; dbo.GetTSQLFragment(         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; sql_text.&lt;font color="#0000ff"&gt;text&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; statement_start_offset,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; statement_end_offset) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [FragmentTSQL],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; plan_handle,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; total_worker_time         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;sys.dm_exec_query_stats&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;cross apply&lt;/font&gt; sys.dm_exec_sql_text(sql_handle) &lt;font color="#0000ff"&gt;as&lt;/font&gt; sql_text         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; total_worker_time &lt;font color="#0000ff"&gt;desc&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;We'll be visiting many more DMVs and sample queries off the DMVs in future articles.&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-688020780224065355?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/06/dynamic-management-views-part-ii.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-6597043247105156960</guid><pubDate>Tue, 13 Jun 2006 21:41:00 +0000</pubDate><atom:updated>2008-02-04T22:46:37.314Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>dmv</category><title>Dynamic Management Views (DMVs) Part I</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;In SQL Server 2000 we had to dig into the system tables to take a peek under the covers. Often many of these tables were undocumented and we had to rely on reverse engineering, sp_helptext, and the debugger to figure out what was going on. In addition, Microsoft was at pains to tell us that these tables are undocumented and as such they should be treated as 'internal-only'. In SQL Server 2005, Microsoft has really listened to their customers. We now have a set of virtual tables known as &lt;i&gt;Dynamic Management Views&lt;/i&gt; (DMVs).&lt;/p&gt;    &lt;p&gt;A lot needs to be read into the name. Firstly, they've called them &lt;i&gt;dynamic&lt;/i&gt; not only because they change dynamically with the internal state of SQL Server, but also because Microsoft reserves the right to change the DMV's schema in future versions (leopards and spots comes to mind...). &lt;i&gt;Management&lt;/i&gt; because they're used to inspect the state of SQL Server and diagnose issues. &lt;i&gt;Views&lt;/i&gt; because they're not actual tables (actually, some of them are even functions). They're just a read-only table-like representation of SQL Server in-memory structures (a bit like sysprocesses in SQL Server 2000). The difference between the old system tables and DMVs is that DMVs are documented and fully supported by Microsoft. They reside in the sys schema (so all the DMVs must be prefixed with sys. - more on schemas vs. owner later) and have a dm_ prefix.&lt;/p&gt;    &lt;p&gt;Just listing the DMVs with a one line'r would make this a very boring article. I'd rather show you which ones I think are cool and will be useful to use. However, there are so many that it will take some time for all the buried treasure to be found. Broadly speaking the DMVs are split along functional lines. There are DMVs for CLR-integration, I/O, replication, full-text, and many others. Each of these categories has its own prefix. For example, CLR related DMVs all begin with &lt;i&gt;sys.dm_clr_*&lt;/i&gt;. To be able to view DMVs does not require sysadmin privileges. The new VIEW SERVER STATE permission is all the is required for most DMVs (i.e. can look but cannot touch). I know some people that will be very happy to hear this!&lt;/p&gt;    &lt;p&gt;Whenever I first get my hands on a SQL Server, the first think I like to do is have a look at what processes are running on the SQL Server. In SQL Server 2000 this would be sysprocesses (albeit through sp_who/2). This view still exists in SQL Server 2005 for backwards compatibility.&lt;/p&gt;    &lt;p&gt;There isn't just a single replacement DMV for sysprocesses. There are at least 3! sysprocesses was always a bit confused in that it mixed connection, session and request (what was being executed) into a single table. SQL Server 2005 has split this information up into 3 separate DMVs. This DMVs belong in the &amp;quot;Execution Related Dynamic Views&amp;quot; category and carry the &lt;b&gt;sys.dm_exec_*&lt;/b&gt; prefix: &lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;sys.dm_exec_sessions &lt;/li&gt;      &lt;li&gt;sys.dm_exec_connections &lt;/li&gt;      &lt;li&gt;sys.dm_exec_request &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;To call these DMVs a replacement for sysprocesses is doing a disservice to the DMVs. It would be like saying that the Ford GT90 is a replacement for the Focus! It is so much more, in a different league!&lt;/p&gt;    &lt;p&gt;First, let's have a look at &lt;b&gt;sys.dm_exec_sessions&lt;/b&gt; as this has the closest resemblance to sysprocesses. One row exists in this view for every connection / session to SQL Server. Run the following query to see what gets returned:&lt;/p&gt;    &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;sys.dm_exec_sessions&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;At first glance this looks pretty much the same as sysprocesses, but keep scrolling to the right. You'll find that each session's user / environment options are clearly visible across all connections!&lt;/p&gt;  &lt;p&gt;ANSI_NULLS, DATEFORMAT, etc. are all there! In one foul swoop you can now check that all the sessions to the database are using the correct connection properties. Previously this information was only available by using the undocumented DBCC PSS command. Even then, no aggregated view like this DMV was available. Even better, the transaction isolation level is now visible as well. I don't know how many times I've had to convince a developer that their transaction actually gets upgraded to SERIALIZABLE when started under COM+! With this column, it will be the easiest thing in the world to check! Also observe the row count column (how many rows were returned in the last result set returned), previous error number and last request start / end times - all priceless when diagnosing issues.&lt;/p&gt;  &lt;p&gt;Depending on how familiar you were with sysprocesses you'll have noticed sys.dm_exec_sessions did not contain all of the columns from sysprocesses, particularly the connection oriented columns. The DMV containing connection specific columns is &lt;b&gt;sys.dm_exec_connections&lt;/b&gt;. Run the following query to get a flavour of what the table contains:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;sys.dm_exec_connections&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;You'll notice that this table contains a smaller number of rows than sys.dm_exec_sessions. This is because sys.dm_exec_sessions includes system sessions as well (such as the lazy writer, service broker, etc.).&lt;/p&gt;  &lt;p&gt;The sys.dm_exec_connections DMV only contains local / remote connections to the SQL Server. This does not include any internal system connections (which aren't real connection anyway). Again, this DMV contains far more detail than sysprocesses. Amongst the new columns we can now see the packet size each connection is using, number of bytes that have been sent / received for each connection (see how the number of ways to spot the resource hogs has increased with DMVs?) and which authentication scheme the connection is using. Anyone that tried to figure whether a connection was using NTLM or Kerberos in SQL Server 2000 will love that!&lt;/p&gt;  &lt;p&gt;The last DMV for this instalment is &lt;b&gt;sys.dm_exec_requests&lt;/b&gt;. Again, ping the DMV to see what comes back:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;sys.dm_exec_requests&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;We can use this DMV to get the overall picture of what requests are currently executing, get handle to the TSQL and plan they are executing, see if the request is blocked / blocking (same as SQL Server 2000). Note that this DMV duplicates some of the session information from sys.dm_exec_sessions so you don't have to join the DMVs. Typically you'd want to see session- and request-oriented data together.&lt;/p&gt;  &lt;p&gt;So, you might be wondering, given the amount of detail available in DMV's is there a way to be able to dig into them easily without having to come up with sp_who3...? Luckily Microsoft has integrated portions of Reporting Services into the Query Analyser replacement: SQL Server Management Studio (SSMS). Bring up the Reports window and select one of the many available. Context of the report is driven by the current selection in Object Explorer. For those that don't have ready access to SSMS have a look at the attached screen shot and be wow'd!&lt;/p&gt; &lt;img class="pix" src="http://www.fotia.co.uk/fotia/Blog/Images/200606.SsmsReports.jpg" /&gt;   &lt;p&gt;Now that we've had a bit of an introduction to DMVs as a transition from sysprocesses, next we'll have a look at the power user's DMVs!&lt;/p&gt;  &lt;p&gt;You can download the SQL Server 2005 system view map (which includes DMVs) from the &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=2ec9e842-40be-4321-9b56-92fd3860fb32&amp;amp;DisplayLang=en"&gt;Microsoft Download Center&lt;/a&gt;.&lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-6597043247105156960?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/06/dynamic-management-views-dmvs-part-i.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-3983681506609679923</guid><pubDate>Mon, 29 May 2006 19:24:00 +0000</pubDate><atom:updated>2008-02-04T22:43:37.125Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>transactions</category><title>Snapshot Isolation Part II</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;Previously we started exploring the new row versioning / snapshot isolation features in SQL Server 2005. We saw how writers no longer block readers when the READ_COMMITTED_SNAPSHOT database option has been set. The rule of thumb is that read committed snapshot will be used when a shared (read) lock is being requested. Exclusive or update locks will never use row versioning. Depending on the logic of the TSQL, sometimes it is necessary to drop out of snapshot isolation and ensure that the row being read is current. However, bear in mind that any code which forces SQL Server to serialise access to a resource will become a contention hotspot.&lt;/p&gt;    &lt;p&gt;In this instalment we'll continue our investigation into snapshot isolation and dig into its more advanced usage in the form of the snapshot transaction isolation level (i.e. read committed snapshot versus snapshot transaction isolation) . Whereas read committed snapshot was very specific in terms of only applying to shared locks, snapshot transaction isolation places a context around the entire transaction, affecting all locks. With read committed snapshot the latest version of a row was read by all connections as soon as the owning transaction committed. When a transaction utilising snapshot transaction isolation level begins, SQL Server notes when the transaction began relative to all other transactions executing in the database at that point-in-time. SQL Server uses this marker to determine which version of a row the transaction will read for its entire duration. This behaviour is best demonstrated with an example.&lt;/p&gt;    &lt;p&gt;The following TSQL creates a database, enables snapshot isolation level, creates a table and adds some data:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create database&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;&lt;font color="#0000ff"&gt;alter database&lt;/font&gt; DiscoverYukon         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; allow_snapshot_isolation &lt;font color="#0000ff"&gt;on&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; someTable (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; id &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; someData &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(100))&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; someTable (id, someData)         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; 1, &lt;font color="#ff0000"&gt;'One'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;union&lt;/font&gt; &lt;font color="#696969"&gt;all&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; 2, &lt;font color="#ff0000"&gt;'Two'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;union&lt;/font&gt; &lt;font color="#696969"&gt;all&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; 3, &lt;font color="#ff0000"&gt;'Three'&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Again, we'll need two separate transactions. On connection #1 we start a transaction and update the table:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #1...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;set transaction isolation level&lt;/font&gt; snapshot&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;begin transaction&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;update&lt;/font&gt; someTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; someData = &lt;font color="#ff0000"&gt;'Three.One'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 3&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Note the most obvious difference is the presence of SET TRANSACTION ISOLATION LEVEL. This option has always been present in SQL Server. It governs how transaction locks will be treated for this connection. However, previously the only possible values were READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ and SERIALIZABLE. SNAPSHOT is new to SQL Server 2005. Setting the transaction isolation to snapshot has to be done explicitly, either on the connection or in the TSQL text . On connection #2 we now try read the row that was updated in connection #1. On connection #2 execute:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;          &lt;br /&gt;&lt;/font&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#0000ff"&gt;set transaction isolation level&lt;/font&gt; snapshot&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;begin transaction&lt;/font&gt;&lt;font color="#696969"&gt;;          &lt;br /&gt;          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 3&lt;font color="#696969"&gt;;         &lt;br /&gt;           &lt;br /&gt;&lt;/font&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;id &lt;/td&gt;              &lt;td&gt;someData &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;---- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;3 &lt;/td&gt;              &lt;td&gt;Three &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As you probably guessed, this returns the previous version of the row, before connection #1 updated it. However, now let's try and update the row from connection #2.&lt;/p&gt;    &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;update&lt;/font&gt; someTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; someData = &lt;font color="#ff0000"&gt;'Three.Two'&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 3&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;Same as before, this update blocks as SQL Server will ensure that we only update the current version of a row. Let's commit the transaction on connection #1:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #1...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;commit transaction&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;The query on connection #2 now completes with the following error:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;     &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;&lt;font color="#ff0000"&gt;Msg 3960, Level 16, State 2, Line 2 Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table 'dbo.someTable' directly or indirectly in database 'DiscoverYukon' to update, delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement.&lt;/font&gt; &lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;We got this error because when connection #2's transaction began, SQL Server noted that the row was already being updated by connection #1. With this knowledge, SQL Server ensured that connection #2 would not be allowed to modify any data that was being versioned when connection #2's transaction began. The reason for this behaviour will become apparent if we re-run the sample again. On connection #1 execute:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #1...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;set transaction isolation level&lt;/font&gt; snapshot;       &lt;br /&gt;&lt;font color="#0000ff"&gt;begin transaction&lt;/font&gt;;       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;update &lt;/font&gt;someTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; someData = &lt;font color="#ff0000"&gt;'Two.One'&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 2&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;On connection #2 execute:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;set transaction isolation level&lt;/font&gt; snapshot;       &lt;br /&gt;&lt;font color="#0000ff"&gt;begin transaction&lt;/font&gt;;       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 2&lt;font color="#696969"&gt;;       &lt;br /&gt;&lt;/font&gt;       &lt;br /&gt;      &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;id &lt;/td&gt;            &lt;td&gt;someData &lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;---- &lt;/td&gt;            &lt;td&gt;----------- &lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;2 &lt;/td&gt;            &lt;td&gt;Two &lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;Again, this returns the versioned row as it was when connection #1 began its transaction (the update from the first sample). Now let's commit connection #1's transaction and re-read this row:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #1...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;commit transaction&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;  &lt;p&gt;And on connection #2:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 2&lt;font color="#696969"&gt;;       &lt;br /&gt;        &lt;br /&gt;&lt;/font&gt;       &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;id &lt;/td&gt;            &lt;td&gt;someData &lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;---- &lt;/td&gt;            &lt;td&gt;----------- &lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;2 &lt;/td&gt;            &lt;td&gt;Two &lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;Create another separate connection #3 and run the same SELECT:&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #3...&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;&lt;/font&gt;       &lt;br /&gt;go       &lt;br /&gt;      &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; *       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable       &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 2&lt;font color="#696969"&gt;;       &lt;br /&gt;&lt;/font&gt;       &lt;br /&gt;      &lt;table class="grid"&gt;&lt;tbody&gt;         &lt;tr&gt;           &lt;td&gt;id &lt;/td&gt;            &lt;td&gt;someData &lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;---- &lt;/td&gt;            &lt;td&gt;----------- &lt;/td&gt;         &lt;/tr&gt;          &lt;tr&gt;           &lt;td&gt;2 &lt;/td&gt;            &lt;td&gt;Two.One &lt;/td&gt;         &lt;/tr&gt;       &lt;/tbody&gt;&lt;/table&gt;   &lt;/div&gt; &lt;/font&gt;  &lt;p&gt;Now it should become apparent what's going on. Connection #2's SELECT returns the same versioned row even though connection #1 has committed its transaction. The latest version of the row has someData set to 'Two.One' as can also be witnessed by the data returned by connection #3. SQL Server will ensure that any rows that are versioned when a transaction begins will only be available as versioned rows for the duration of that transaction. The transaction is essentially forbidden to update any rows that were under the influence of another transaction when the transaction began. This also includes not being able to modify any rows that are changed by another transaction after the transaction began.&lt;/p&gt;  &lt;p&gt;For OLTP systems I can see read committed snapshot playing a far bigger role than snapshot isolation due to the update conflict complication with snapshot transaction isolation level. However, snapshot isolation will have a role to play when the probability of causing conflicts is low. For example, if a query needs to generate a report and must ensure that the data was consistent, at a point in time, when the report was run, snapshot transaction isolation level will be beneficial (the report does not update any data). In this manner it can act as a very lightweight alternative to REPEATABLE READ or SERIALIZABLE transaction isolation levels which typically pay a very high concurrency price. An acceptable pattern to follow when using snapshot isolation is to automatically retry in the event of an update conflict. Many large database systems already have data access layers that retry in the event of a deadlock.&lt;/p&gt;  &lt;p&gt;As a taster for the next instalment, here is a very useful query using a DMV. It returns how long a row has been versioned for. Big numbers mean long running transactions (which are always bad!). Start a couple of transaction using either of the two snapshot isolation levels and observe the output. Use this to help find the long running culprits!&lt;/p&gt; &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;   &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; session_id &lt;font color="#0000ff"&gt;as&lt;/font&gt; [spid],       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; elapsed_time_seconds       &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; &lt;font color="#008000"&gt;sys.dm_tran_active_snapshot_database_transactions&lt;/font&gt;       &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; 2 &lt;font color="#0000ff"&gt;desc&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt; &lt;/font&gt;&lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-3983681506609679923?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/05/snapshot-isolation-part-ii.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-3730337277947333423</guid><pubDate>Tue, 09 May 2006 21:06:00 +0000</pubDate><atom:updated>2008-02-04T22:40:18.549Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>transactions</category><title>Snapshot Isolation Part I</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;Our investigation into snapshot isolation will be split into a number of parts. We'll firstly introduce the basic concept of what snapshot isolation means and how it can be used in its most basic form. Then we'll have a look at the advanced use of snapshot isolation as a full-blown transaction isolation level.&lt;/p&gt;    &lt;p&gt;At its core, the SQL Server engine shares many similarities with a source code version control system. In fact, many of the terms used in snapshot isolation level have direct counterparts with applications like Rational's ClearCase version control system. In ClearCase, every file in the repository has an associated version number. When developers wants to make changes to a file, they first need to 'check it out'. This prevents any other developer from being able to make any changes to that file while it is being modified. In reserved mode no other developer can check the file out till the file has been checked back in. This is the same behaviour as SQL Server 2000 and SQL Server 2005 without snapshot isolation. When a row is being inserted / updated / deleted, the row is essentially 'checked out' till the transaction that performed the 'check out' either commits or rolls back. No other connection can even look at the row till it is 'checked back in'. To change this behaviour TSQL allows you specify locking hints such as NOLOCK (look at the row anyway, even though the data may only be partly changed - be very wary of using this) or READPAST (pretend that the row isn't even there).&lt;/p&gt;    &lt;p&gt;Back to the ClearCase analogy. Even though a file has been checked out, developers can still look at the previous version of a file. For example, if HelloWorld.cs was checked out at version 3, developers can still look at versions 1 through 2. Version 3 only becomes visible when the developer has checked HelloWorld.cs back in. Well, this is precisely what snapshot isolation does. SQL Server maintains a version history of all rows that are in the process of being created / changed / removed. Does this mean that you can have a look at all previous versions of a row since it was created? No. SQL Server only maintains the last version of a row while it is being modified. Where are these previous versions stored? Is my database size going to multiply? No, previous version of a row are transient and are stored in TempDb.&lt;/p&gt;    &lt;p&gt;All that snapshot isolation is trying to do is to prevent readers from blocking writers. That is, if a reader is trying to SELECT a row that is currently being altered by another writer, SQL Server will return the last version of the row that was committed instead of forcing the reader to wait till the writer commits. No more blocking...most of the time anyway(read on!).&lt;/p&gt;    &lt;p&gt;As mentioned earlier, there are two modes of operation with snapshot isolation. We'll be looking the most basic mode of operation, known as READ_COMMITTED_SNAPSHOT. All that we need to know at this stage is that this behaviour applies when the transaction is running under the READ COMMITTED transaction isolation level (as opposed to READ UNCOMMITTED, REPEATABLE READ or SERIALIZABLE). This mode of behaviour is best observed with an example.&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;create database&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;&lt;font color="#0000ff"&gt;use&lt;/font&gt; DiscoverYukon&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Enable snapshot isolation for read committed.&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;alter database&lt;/font&gt; DiscoverYukon         &lt;br /&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt; read_committed_snapshot &lt;font color="#0000ff"&gt;on&lt;/font&gt;&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- Create a test table.          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;create table&lt;/font&gt; someTable (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Id &lt;font color="#0000ff"&gt;int&lt;/font&gt; &lt;font color="#696969"&gt;not null&lt;/font&gt; &lt;font color="#0000ff"&gt;primary key clustered&lt;/font&gt;,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; SomeData &lt;font color="#0000ff"&gt;varchar&lt;/font&gt;(255))&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;go&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;READ_COMMITTED_SNAPSHOT needs to be enabled at the database level. The default in the Model database (the template used whenever creating a new database) has READ_COMMITTED_SNAPSHOT set to OFF.&lt;/p&gt;    &lt;p&gt;We'll dig into snapshot isolation using two connections. On the first connection, execute the following TSQL to insert a new record into the test table. We wrap this in a transaction so that we can interact with it from another connection:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #1...          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;begin transaction&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;insert&lt;/font&gt; someTable (Id, Somedata)         &lt;br /&gt;&lt;font color="#0000ff"&gt;values&lt;/font&gt; (1, &lt;font color="#ff0000"&gt;'FirstVersion'&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;On the other connection we simulate a user trying to read the same row:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; * &lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable&lt;font color="#696969"&gt;;&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;This query immediately comes back with no records! In SQL Server 2000 the SELECT would have blocked till connection #1 committed (try it out!). But, what if connection #2 tries to insert a record with the same Id. What if we added some 'insert Id 1 if it does not exist'-type logic?&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...          &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt;if&lt;/font&gt; not exists (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select top&lt;/font&gt; 1 1         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 1) &lt;font color="#0000ff"&gt;begin&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; someTable (Id, Somedata)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;values&lt;/font&gt; (1, &lt;font color="#ff0000"&gt;'FirstVersion'&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;When running this query you'll notice that it has reverted back to the SQL Server 2000 behaviour. It blocks till connection #1 commits. However, when connection #1 commits something that may be unexpected happens. Connection #2's query fails with the following error:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;       &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;&lt;font color="#ff0000"&gt;Msg 2627, Level 14, State 1, Line 6                  &lt;br /&gt;Violation of PRIMARY KEY constraint 'PK__someTable__7C8480AE'. Cannot insert duplicate key in object 'dbo.someTable'.                   &lt;br /&gt;&lt;/font&gt;The statement has been terminated. &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Connection #2 was not blocking on the SELECT statement! That completed successfully. It was the INSERT that was blocking. SQL Server was not allowing a previous version of the row to be changed (in this case a row with the same Id to be created) even though snapshot isolation was enabled. What you read is not always what you change when snapshot isolation is in use. Therefore, care must be taken when trying to write this type of query. In this instance, the SELECT needs to use the new locking hint available in SQL Server 2005, namely READCOMMITTEDLOCK. This is not very intuitive as the existing lock hint is READCOMMITTED. The difference between these two is that READCOMMITTED will still honour the READ_COMMITTED_SNAPSHOT setting while READCOMMITTEDLOCK will not. READCOMMITTEDLOCK will not read previous versions of the row and will wait till it obtains a read lock on the actual row (it is oblivious to previous versions). Therefore the TSQL needs to be rewritten as:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- Connection #2...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;if&lt;/font&gt; &lt;font color="#696969"&gt;not exists&lt;/font&gt; (         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;select top&lt;/font&gt; 1 1         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; someTable &lt;font color="#0000ff"&gt;with&lt;/font&gt;(readcommittedlock)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; id = 1) &lt;font color="#0000ff"&gt;begin&lt;/font&gt;         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; someTable (Id, Somedata)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;values&lt;/font&gt; (1, &lt;font color="#ff0000"&gt;'FirstVersion'&lt;/font&gt;)&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;In this case connection #2 will block on the SELECT till connection #1 commits. When it does, connection #2 completes normally and does not attempt to insert a duplicate row (the IF returns FALSE). Next we'll look at how UPDATEs are affected by snapshot isolation. &lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-3730337277947333423?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/05/snapshot-isolation-part-i.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-809923234699905874</guid><pubDate>Fri, 21 Apr 2006 20:58:00 +0000</pubDate><atom:updated>2008-02-04T22:38:18.124Z</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>errors</category><title>TRY...CATCH</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;I'll start off by saying that I am not a fan of the error handling practises that this syntax promotes. People are often surprised when I tell them that, in my opinion, BEGIN TRAN, ROLLBACK TRAN, SELECT @@ERROR, etc. do not belong in stored procedures. Think of it this way, have you ever seen any code in a stored procedure that actually does anything useful when an unexpected error occurs, besides exiting the stored procedure? Then why do we have to have the standard 10 lines of boiler plate 'error handling' TSQL that:&lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;tests @@ERROR after every DML statement, &lt;/li&gt;      &lt;li&gt;does a GOTO to an ErrorHandler label, and &lt;/li&gt;      &lt;li&gt;does a RAISERROR of 'an unexpected error occurred'? &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;If the intention is just to stop when an error occurs then the SET XACT_ABORT statement is all that is needed at the start of the batch / connection. In addition, if you're using .NET then you have the SqlException class available. This exception class (which gets thrown by the SqlConnection class when a warning / error occurs) includes the name of the procedure and the line number the error occurred on. What more do you need? If you need additional context, like a customer ID, you probably know that already in the middle-tier / client code that is calling the stored procedure. Alternatively, the source of the error is a business rule that has failed and you're performing a custom RAISERROR anyway and are able to substitute values, like the customer ID, into the error message.&lt;/p&gt;    &lt;p&gt;Ultimately, the entity in charge of the transaction must be the arbiter that decides whether the transaction needs to commit or rollback. This has to be the middle-tier code that is calling the stored procedure and not the stored procedure itself. The only situation I can think of where transaction control logic needs to reside in TSQL is when the entity in charge is Query Analyser / SQL Server Management Studio (SSMS), i.e. you're running SQL scripts and don't have the luxury of C# available and you need some actions to be performed if an error occurs. Likewise a stored procedure that will act as a Service Broker internal service program could benefit from TRY...CATCH as the stored procedure is the initiator.&lt;/p&gt;    &lt;p&gt;Anyway, enough of my opinions. I'll be covering recommended error handling practises in a later instalment. Let's look at what TRY...CATCH brings to the table.&lt;/p&gt;    &lt;p&gt;As you've probably guessed, TRY..CATCH is a TSQL implementation of the exception handling semantics we've grown to love in .NET / C++. A TRY block determines the scope of the TSQL statements that will be monitored for RAISERRORs (caused by either SQL Server or custom RAISERRORs). If an error is raised the TRY block is immediately exited and control is passed to the corresponding CATCH block. TRY..CATCH blocks can be nested in the same manner as C#. However, there is no concept of a FINALLY block. The following TSQL is an example of a simple TRY...CATCH block.&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;begin try&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#008000"&gt;-- Fails with a primary key violation&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;insert&lt;/font&gt; Sales.Currency (CurrencyCode, [Name])         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;values&lt;/font&gt; (N&lt;font color="#ff0000"&gt;'GBP'&lt;/font&gt;, N&lt;font color="#ff0000"&gt;'United Kingdom Pound'&lt;/font&gt;)         &lt;br /&gt;        &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;print&lt;/font&gt; N&lt;font color="#ff0000"&gt;'Will not be reached...'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end try          &lt;br /&gt;begin catch&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;print&lt;/font&gt; N'&lt;font color="#ff0000"&gt;In catch block...'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;end catch&lt;/font&gt;&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Running this in SSMS produces the following output:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;       &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;In catch block... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;The good thing about this behaviour is that we no longer need to include the standard @@ERROR test after any DML. In fact, if the code is within a TRY block we'll never get a chance to test @@ERROR anyway. 'Will not be reached...' is never printed. Also note that SSMS did not report any error. What? Where has my PK violation gone? To ensure that I wasn't seeing a quirk of SSMS I ran the following in a C# console application:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;try&lt;/font&gt;         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;using&lt;/font&gt;(SqlConnection con = &lt;font color="#0000ff"&gt;new&lt;/font&gt; SqlConnection(&lt;font color="#008000"&gt;&amp;quot;Data&lt;/font&gt;&lt;font color="#008000"&gt; Source=(local);Initial Catalog=AdventureWorks;Integrated Security=SSPI&amp;quot;&lt;/font&gt;))         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;using&lt;/font&gt; SqlCommand cmd = con.CreateCommand())         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; con.Open();         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cmd.CommandType = CommandType.Text;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cmd.CommandText = &lt;font color="#008000"&gt;@&amp;quot;          &lt;br /&gt;begin try           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; -- Fails with a primary key violation           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; insert Sales.Currency (CurrencyCode, Name)           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; values (N'GBP', N'United Kingdom Pound')           &lt;br /&gt;          &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; print N'Will not be reached'           &lt;br /&gt;end try           &lt;br /&gt;begin catch           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; print N'In catch block'           &lt;br /&gt;end catch&amp;quot;&lt;/font&gt;;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; cmd.ExecuteNonQuery();         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Console.WriteLine(&lt;font color="#008000"&gt;&amp;quot;It&lt;/font&gt;&lt;font color="#008000"&gt; all worked perfectly...&amp;quot;&lt;/font&gt;);         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }         &lt;br /&gt;}         &lt;br /&gt;&lt;font color="#0000ff"&gt;catch&lt;/font&gt;(Exception exception)         &lt;br /&gt;{         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Console.WriteLine(exception);         &lt;br /&gt;}&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Again, no exception. So the TSQL CATCH is behaving exactly like exceptions in .NET. If you catch an exception in TSQL and don't re-raise the exception, the caller will never know that it occurred. Aaargh! Why would you ever want that behaviour in a middle-tier application? The golden rule with exception handling is: &amp;quot;Don't catch exceptions unless you can do something about them&amp;quot;. When could you ever do something about an error in TSQL besides aborting the batch?&lt;/p&gt;    &lt;p&gt;Anyway, if you ever decide to use TRY..CATCH in TSQL there are a couple of new functions that are available to inspect the exception, think of them as @@ERROR replacements. You now have ERROR_LINE(), ERROR_MESSAGE(), ERROR_NUMBER(), ERROR_PROCEDURE(), ERROR_SEVERITY(), ERROR_STATE() available inside the catch block. In addition, there is another function called XACT_STATE() available that tells you whether or not the transaction is capable of being committed. BOL states that if XACT_STATE returns -1 then an error has occurred in the active transaction that has rendered it uncommittable. In this state only read operations are allowed. A transaction will enter this state if XACT_ABORT has been set on and an error was raised.&lt;/p&gt;    &lt;p&gt;I can see how TRY..CATCH will be useful if you are running pure TSQL scripts. However, for any database interactions that are initiated from application servers / clients I don't believe they should be used at all. One of the holy grails I rely on is that, in SQL Server 2000, no errors can ever be swallowed / suppressed in the database. The client always has &lt;b&gt;full&lt;/b&gt; knowledge of all errors that occurred, even if there were multiple RAISERRORs (SQL Server initiated and / or custom errors - SqlException has a SqlErrors collection). Use of TRY..CATCH in these cases could hide these errors from the application server / client resulting in mysterious missing data / errors that the caller has no knowledge of. I feel that the use of TRY..CATCH would be on par with the evil that is catch(...) in C++!&lt;/p&gt;    &lt;p&gt;I had a colleague suggest that as the new TRY...CATCH syntax can catch deadlocks (which are not possible to handle within TSQL in SQL Server 2000), you could move the deadlock retry code from the application servers into the database. Please don't do this unless you want applications that are randomly slow with transactions boundaries as fuzzy as Beyonce's afro!&lt;/p&gt;    &lt;p&gt;Next up, SNAPSHOT_ISOLATION level for all the Oracle pundits. &lt;/p&gt; &lt;/font&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-809923234699905874?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/04/trycatch.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-2092169272416726544</guid><pubDate>Thu, 06 Apr 2006 22:52:00 +0000</pubDate><atom:updated>2008-06-26T15:35:53.017+01:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The OVER Clause Part II</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;The only mention that the OVER clause can be used with aggregate functions (SUM, COUNT, etc.) is in the following statement in BOL (Books Online):&lt;/p&gt;   &lt;img class="pix" alt="The OVER Clause" src="/fotia/Blog/Images/200604.BolOverExcerpt.jpg" /&gt;     &lt;p&gt;It gives no hint or inkling of the wealth of possibilities this opens up for queries that need to contain a mix aggregate and non-aggregate results. Suppose you were asked to write a query that will return a result set of all customers' FirstName, LastName and EmailAddress. For each customer, return a count of how many other customers have the same FirstName and a count of how many have the same LastName. Restrict this to customers whose LastName begins with a 'D' to keep it short. In SQL Server 2000 you'd have to start thinking of sub-selects and correlated sub-queries to mix these aggregate (one grouping on FirstName the other on LastName) and non-aggregate (each customer row needs to be returned independent of any aggregate grouping) requirements. If the requirement was for a pure aggregation (e.g. count of customers grouping by LastName) it would be very straight forward. However, with the mix you'd probably come up with something that looks like one of these:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#008000"&gt;-- #1, Expensive...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; FirstName, LastName, EmailAddress,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; (&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact Cinner         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; Cinner.FirstName = Couter.FirstName         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;and&lt;/font&gt; Cinner.LastName &lt;font color="#696969"&gt;like&lt;/font&gt; &lt;font color="#ff0000"&gt;'D%'&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfFirstName],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; (&lt;font color="#0000ff"&gt;select&lt;/font&gt; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*)         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact Cinner         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; Cinner.LastName = Couter.LastName         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;and&lt;/font&gt; Cinner.LastName &lt;font color="#696969"&gt;like&lt;/font&gt; &lt;font color="#ff0000"&gt;'D%'&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfLastName]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact Couter         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; LastName &lt;font color="#696969"&gt;like&lt;/font&gt; &lt;font color="#ff0000"&gt;'D%'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; LastName, FirstName         &lt;br /&gt;        &lt;br /&gt;&lt;font color="#008000"&gt;-- #2 Better...&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; FirstName, LastName, EmailAddress, CountOfFirstName, CountOfLastName         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact C         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (&lt;font color="#0000ff"&gt;select&lt;/font&gt; FirstName &lt;font color="#0000ff"&gt;as&lt;/font&gt; [FirstNameCounted],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfFirstName]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; LastName &lt;font color="#696969"&gt;like&lt;/font&gt; &lt;font color="#ff0000"&gt;'D%'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;group by&lt;/font&gt; FirstName) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountFirstNames]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; C.FirstName = CountFirstNames.FirstNameCounted         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (&lt;font color="#0000ff"&gt;select&lt;/font&gt; LastName &lt;font color="#0000ff"&gt;as&lt;/font&gt; [LastNameCounted],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [CountOfLastName]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;where&lt;/font&gt; LastName &lt;font color="#696969"&gt;like&lt;/font&gt; '&lt;font color="#ff0000"&gt;D%'&lt;/font&gt;         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;group by&lt;/font&gt; LastName) as [CountLastNames]         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; C.LastName = CountLastNames.LastNameCounted         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; LastName &lt;font color="#696969"&gt;like&lt;/font&gt; &lt;font color="#ff0000"&gt;'D%'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; LastName, FirstName&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;#1 is twice as expensive as #2. Execute these queries and have a look at the execution plans to understand why (we'll covering analysis of execution plans in the future). Both these queries produce the following result set (abbreviated):&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;       &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;FirstName &lt;/td&gt;              &lt;td&gt;LastName &lt;/td&gt;              &lt;td&gt;EmailAddress &lt;/td&gt;              &lt;td&gt;CountOfFirstName &lt;/td&gt;              &lt;td&gt;CountOfLastName &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;--------- &lt;/td&gt;              &lt;td&gt;--------- &lt;/td&gt;              &lt;td&gt;------------------------------ &lt;/td&gt;              &lt;td&gt;----------------- &lt;/td&gt;              &lt;td&gt;--------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;David &lt;/td&gt;              &lt;td&gt;Daniels &lt;/td&gt;              &lt;td&gt;david34@adventure-works.com &lt;/td&gt;              &lt;td&gt;87 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;David &lt;/td&gt;              &lt;td&gt;Daniels &lt;/td&gt;              &lt;td&gt;david31@adventure-works.com &lt;/td&gt;              &lt;td&gt;87 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Ryan &lt;/td&gt;              &lt;td&gt;Danner &lt;/td&gt;              &lt;td&gt;ryan3@adventure-works.com &lt;/td&gt;              &lt;td&gt;60 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Ryan &lt;/td&gt;              &lt;td&gt;Danner &lt;/td&gt;              &lt;td&gt;ryan5@adventure-works.com &lt;/td&gt;              &lt;td&gt;60 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Mike &lt;/td&gt;              &lt;td&gt;Danseglio &lt;/td&gt;              &lt;td&gt;mike2@adventure-works.com &lt;/td&gt;              &lt;td&gt;9 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Alexander &lt;/td&gt;              &lt;td&gt;Davis &lt;/td&gt;              &lt;td&gt;alexander7@adventure-works.com &lt;/td&gt;              &lt;td&gt;23 &lt;/td&gt;              &lt;td&gt;77 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Alexandra &lt;/td&gt;              &lt;td&gt;Davis &lt;/td&gt;              &lt;td&gt;alexandra68@adventure-works.com &lt;/td&gt;              &lt;td&gt;93 &lt;/td&gt;              &lt;td&gt;77 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Alyssa &lt;/td&gt;              &lt;td&gt;Davis &lt;/td&gt;              &lt;td&gt;alyssa4@adventure-works.com &lt;/td&gt;              &lt;td&gt;67 &lt;/td&gt;              &lt;td&gt;77 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As a quick sanity check we can see that anyone with the surname Davis has CountOfLastName as 77 and anyone with the FirstName David has CountOfFirstName as 87.&lt;/p&gt;    &lt;p&gt;In the same manner the OVER clause applies to the window ranking function that immediately precedes it, the OVER clause affects the aggregate function that precedes it. In addition, if there are multiple aggregate functions, each one can have its own OVER clause that can be different from the others. However, as per the documentation snippet from above, aggregate functions only include the PARTITION BY clause in their OVER clause. Unlike ranking functions, there is no concept of an ORDER BY for an aggregate function (this wouldn't have any meaning to an aggregation anyway). The same query can now be rewritten in SQL Server 2005 as follows:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; FirstName, LastName, EmailAddress,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;partition by&lt;/font&gt; FirstName) &lt;font color="#0000ff"&gt;as&lt;/font&gt; CountOfFirstName,         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;count&lt;/font&gt;(*) &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;partition by&lt;/font&gt; LastName) &lt;font color="#0000ff"&gt;as&lt;/font&gt; CountOfLastName         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Contact         &lt;br /&gt;&lt;font color="#0000ff"&gt;where&lt;/font&gt; LastName &lt;font color="#696969"&gt;like&lt;/font&gt; &lt;font color="#ff0000"&gt;'D%'&lt;/font&gt;         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; LastName, FirstName&lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;How cool is that?! This rocks! Not only does it read much better than the queries above, it is the cheapest to execute by a long long shot (65% cheaper than #1 and 45% cheaper than #2 - have a look at the execution plans). This query produces the identical results to the queries above. Note that there is not a GROUP BY to be seen. SQL Server has essentially substituted the GROUP BY that all aggregate functions need with the PARTITION BY clause. As SQL Server is producing the result set, it is streaming the rows through the aggregate functions. The PARTITION BY tells SQL Server how to group the rows when applying the aggregation function in the same manner the PARTITION BY was telling SQL Server when to restart the ranking function.&lt;/p&gt;    &lt;p&gt;Note that if you need to write a query that produces a pure aggregation then the normal GROUP BY form needs to be used as you'll want the source result set collapsed to their distinct values that the aggregate functions iterated over. If you need to mix aggregate and non-aggregate functions then OVER with PARTITION BY is the best option. Next up is the new TRY...CATCH syntax.&lt;/p&gt; &lt;/font&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-2092169272416726544?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/04/over-clause-part-ii.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-8257230207474395431</guid><pubDate>Mon, 20 Mar 2006 17:11:00 +0000</pubDate><atom:updated>2008-06-26T15:36:56.668+01:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The OVER Clause Part I</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;The OVER clause is new to SQL Server 2005. It is used for aggregate (SUM, AVG, COUNT, etc.) and ranking (NTILE, ROW_NUMBER, RANK, etc.) functions. Previously we've seen it used to specify the sorting SQL Server needs to apply to a result set before evaluating its associated window function for each row. For example, the following query results a list of all vendors, their post codes and cities. Each row is assigned a ROW_NUMBER according to its absolute position when the rows are ordered according to their PostalCodes.&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; row_number() &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;order by&lt;/font&gt; PostalCode) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [RowNumber],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; [Name], PostalCode, City         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Address A         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Purchasing.VendorAddress VA         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; A.AddressId = VA.AddressId         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Purchasing.Vendor V         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; VA.VendorId = V.VendorId         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; PostalCode&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;RowNumber &lt;/td&gt;              &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;PostalCode &lt;/td&gt;              &lt;td&gt;City &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;--------- &lt;/td&gt;              &lt;td&gt;---------------------------------- &lt;/td&gt;              &lt;td&gt;---------- &lt;/td&gt;              &lt;td&gt;------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;Premier Sport, Inc. &lt;/td&gt;              &lt;td&gt;02113 &lt;/td&gt;              &lt;td&gt;Boston &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;10007 &lt;/td&gt;              &lt;td&gt;New York &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;3 &lt;/td&gt;              &lt;td&gt;Morgan Bike Accessories &lt;/td&gt;              &lt;td&gt;12210 &lt;/td&gt;              &lt;td&gt;Albany &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;4 &lt;/td&gt;              &lt;td&gt;Wood Fitness &lt;/td&gt;              &lt;td&gt;19107 &lt;/td&gt;              &lt;td&gt;Philadelphia &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;5 &lt;/td&gt;              &lt;td&gt;Competition Bike Training Systems &lt;/td&gt;              &lt;td&gt;30308 &lt;/td&gt;              &lt;td&gt;Atlanta &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;6 &lt;/td&gt;              &lt;td&gt;Bike Satellite Inc. &lt;/td&gt;              &lt;td&gt;37501 &lt;/td&gt;              &lt;td&gt;Downey &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;7 &lt;/td&gt;              &lt;td&gt;Midwest Sport, Inc. &lt;/td&gt;              &lt;td&gt;48226 &lt;/td&gt;              &lt;td&gt;Detroit &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Now, suppose I didn't want the row number to be consecutive over the entire result set. For example, I may want to have the rows numbered from &lt;i&gt;1 to n&lt;/i&gt; for each city. That is, I want the row numbers to be consecutive (1,2,3...n) per city. This is what the PARTITION BY clause allows us to do. By including a PARTITION BY clause in the window function's OVER clause we can essentially restart the function when the value of the column specified in the PARTITION BY changes. For example, to number the rows consecutively per city the query changes to:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; row_number()&lt;font color="#0000ff"&gt; over&lt;/font&gt;(&lt;font color="#0000ff"&gt;partition by&lt;/font&gt; City &lt;font color="#0000ff"&gt;order by&lt;/font&gt; PostalCode) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [RowNumber],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; [Name], PostalCode, City         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Address A         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Purchasing.VendorAddress VA         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; A.AddressId = VA.AddressId         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Purchasing.Vendor V         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; VA.VendorId = V.VendorId         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; City&lt;font color="#696969"&gt;;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;RowNumber &lt;/td&gt;              &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;PostalCode &lt;/td&gt;              &lt;td&gt;City &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;--------- &lt;/td&gt;              &lt;td&gt;--------------------------------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;Morgan Bike Accessories &lt;/td&gt;              &lt;td&gt;12210 &lt;/td&gt;              &lt;td&gt;Albany &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;Leaf River Terrain &lt;/td&gt;              &lt;td&gt;97321 &lt;/td&gt;              &lt;td&gt;Albany &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;Allenson Cycles &lt;/td&gt;              &lt;td&gt;91001 &lt;/td&gt;              &lt;td&gt;Altadena &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;Gardner Touring Cycles &lt;/td&gt;              &lt;td&gt;91001 &lt;/td&gt;              &lt;td&gt;Altadena &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;Speed Corporation &lt;/td&gt;              &lt;td&gt;98221 &lt;/td&gt;              &lt;td&gt;Anacortes &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;Electronic Bike Co. &lt;/td&gt;              &lt;td&gt;98221 &lt;/td&gt;              &lt;td&gt;Anacortes &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;3 &lt;/td&gt;              &lt;td&gt;Northern Bike Travel &lt;/td&gt;              &lt;td&gt;98221 &lt;/td&gt;              &lt;td&gt;Anacortes &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;Competition Bike Training Systems &lt;/td&gt;              &lt;td&gt;30308 &lt;/td&gt;              &lt;td&gt;Atlanta &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As you can see, the ROW_NUMBER function has restarted its numbering as soon as the City changed. The PARTITION BY clause can contain any number of columns available in the tables in the FROM clause. As PARTITION BY defines its arguments as an expression, we can start getting creative in how we perform the partitioning. For example, the following query partitions the row number according to the second letter of the address:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; row_number() &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;partition by&lt;/font&gt; &lt;font color="#ff00ff"&gt;substring&lt;/font&gt;(City, 2, 1) &lt;font color="#0000ff"&gt;order by&lt;/font&gt; PostalCode) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [RowNumber],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; [Name], PostalCode, City         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Person.Address A         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Purchasing.VendorAddress VA         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; A.AddressId = VA.AddressId         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Purchasing.Vendor V         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; VA.VendorId = V.VendorId         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; &lt;font color="#ff00ff"&gt;substring&lt;/font&gt;(City, 2, 1)&lt;font color="#696969"&gt;;         &lt;br /&gt;          &lt;br /&gt;&lt;/font&gt;         &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;RowNumber &lt;/td&gt;              &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;PostalCode &lt;/td&gt;              &lt;td&gt;City &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;--------- &lt;/td&gt;              &lt;td&gt;--------------------------------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;              &lt;td&gt;----------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;15 &lt;/td&gt;              &lt;td&gt;Bicycle Specialists &lt;/td&gt;              &lt;td&gt;97034 &lt;/td&gt;              &lt;td&gt;Lake Oswego &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;16 &lt;/td&gt;              &lt;td&gt;Vista Road Bikes &lt;/td&gt;              &lt;td&gt;97301 &lt;/td&gt;              &lt;td&gt;Salem &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;17 &lt;/td&gt;              &lt;td&gt;Knopfler Cycles &lt;/td&gt;              &lt;td&gt;97301 &lt;/td&gt;              &lt;td&gt;Salem &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;18 &lt;/td&gt;              &lt;td&gt;Recreation Place &lt;/td&gt;              &lt;td&gt;97301 &lt;/td&gt;              &lt;td&gt;Salem &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;19 &lt;/td&gt;              &lt;td&gt;Reliance Fitness, Inc. &lt;/td&gt;              &lt;td&gt;98107 &lt;/td&gt;              &lt;td&gt;Ballard &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;20 &lt;/td&gt;              &lt;td&gt;Electronic Bike Repair &amp;amp; Supplies &lt;/td&gt;              &lt;td&gt;98403 &lt;/td&gt;              &lt;td&gt;Tacoma &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;21 &lt;/td&gt;              &lt;td&gt;Cycling Master &lt;/td&gt;              &lt;td&gt;99362 &lt;/td&gt;              &lt;td&gt;W&lt;font style="color: #ff0000; text-decoration: underline"&gt;&lt;b&gt;a&lt;/b&gt;&lt;/font&gt;lla Walla &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;1 &lt;/td&gt;              &lt;td&gt;A. Datum Corporation &lt;/td&gt;              &lt;td&gt;10007 &lt;/td&gt;              &lt;td&gt;N&lt;font style="color: #ff0000; text-decoration: underline"&gt;&lt;b&gt;e&lt;/b&gt;&lt;/font&gt;w York &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;2 &lt;/td&gt;              &lt;td&gt;Midwest Sport, Inc. &lt;/td&gt;              &lt;td&gt;48226 &lt;/td&gt;              &lt;td&gt;Detroit &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;3 &lt;/td&gt;              &lt;td&gt;Green Lake Bike Company &lt;/td&gt;              &lt;td&gt;80203 &lt;/td&gt;              &lt;td&gt;Denver &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;4 &lt;/td&gt;              &lt;td&gt;Beaumont Bikes &lt;/td&gt;              &lt;td&gt;83301 &lt;/td&gt;              &lt;td&gt;West Covina &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;5 &lt;/td&gt;              &lt;td&gt;Greenwood Athletic Company &lt;/td&gt;              &lt;td&gt;85252 &lt;/td&gt;              &lt;td&gt;Lemon Grove &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;ROW_NUMBER restarted its numbering as soon as the second letter of the City name changed from 'a' to 'e'. All of the above applies equally to RANK / DENSE_RANK and NTILE. The OVER clause can also be used with aggregate functions (like MIN, MAX, AVG, SUM, etc) which makes mixing aggregations and non-aggregations in a single query a lot easier than before. That's what we'll be looking at next. &lt;/p&gt; &lt;/font&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-8257230207474395431?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/03/over-clause-part-i.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-3912586870549609682.post-4570246073439798366</guid><pubDate>Thu, 09 Mar 2006 18:23:00 +0000</pubDate><atom:updated>2008-06-26T15:34:10.658+01:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>sql</category><category domain='http://www.blogger.com/atom/ns#'>functions</category><title>The RANK and DENSE_RANK Ranking Functions</title><description>&lt;font style="font-size: small; font-family: georgia"&gt;   &lt;p&gt;RANK and DENSE_RANK are the last of the new ranking (window) functions. The best analogy of how these functions work is to think of how you'd solve the following problem: You've been asked to write a report that will generate a list of the best selling products. You're required to produce a result set that contains the product name, total quantity sold and assign each product a ranking (i.e. 1st, 2nd, 3rd, etc.). So you might write something like this:&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(TH.Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [TotalQuantity],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; row_number() &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;order by&lt;/font&gt; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(quantity) &lt;font color="#0000ff"&gt;desc&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Ranking]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory TH         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; TH.ProductID &lt;font color="#696969"&gt;=&lt;/font&gt; P.ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;group by&lt;/font&gt; P.[Name]         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; [TotalQuantity] &lt;font color="#0000ff"&gt;desc&lt;/font&gt;&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff"&gt; &lt;/font&gt;        &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;TotalQuantity &lt;/td&gt;              &lt;td&gt;Ranking &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------------- &lt;/td&gt;              &lt;td&gt;------------- &lt;/td&gt;              &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;BB Ball Bearing &lt;/td&gt;              &lt;td&gt;374090 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Seat Stays &lt;/td&gt;              &lt;td&gt;187376 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Blade &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Chain Stays &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;4 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Fork End &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;5 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;HL Crankarm &lt;/td&gt;              &lt;td&gt;64900 &lt;/td&gt;              &lt;td&gt;6 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;HL Hub &lt;/td&gt;              &lt;td&gt;56264 &lt;/td&gt;              &lt;td&gt;7 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Decal 1 &lt;/td&gt;              &lt;td&gt;56250 &lt;/td&gt;              &lt;td&gt;8 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Decal 2 &lt;/td&gt;              &lt;td&gt;56250 &lt;/td&gt;              &lt;td&gt;9 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;LL Mountain Pedal &lt;/td&gt;              &lt;td&gt;50922 &lt;/td&gt;              &lt;td&gt;10 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Whilst this looks to have fulfilled the requirements, it has a very subtle deficiency. If you look closely you'll see that the top 10 products actually include a couple of ties. Blade, Chain Stays, and Fork End all come in at an equal 93,688 sold. By using ROW_NUMBER to assign the ranking we have failed to take account the possibility of ties and assigned lower rankings to products that have sold equal amounts. Imagine if this query was for assigning prize money in a golf tournament! I'm sure the competitors would not be very happy about this 'feature'! &lt;/p&gt;    &lt;p&gt;This is where the RANK function comes into play. In the same manner as all the previous windowed functions we looked at, RANK requires an OVER clause. RANK uses the ORDER BY in the OVER clause to assign a monotonically increasing value to each row, starting from 1. However, RANK looks for duplicates in the range. Where duplicate rows are found, the same RANK value is assigned to each row. In addition, when parsing duplicates, RANK counts the number of duplicates read and assigns the next row, that has a differing value, a RANK equal to the RANK of the duplicates plus the number of duplicates read. Using RANK our query then becomes:     &lt;br /&gt;&lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(TH.Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [TotalQuantity],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; rank() &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;order by&lt;/font&gt; &lt;font color="#ff00ff"&gt;sum&lt;/font&gt;(quantity) &lt;font color="#0000ff"&gt;desc&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Ranking]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory TH         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#696969"&gt;inner join&lt;/font&gt; Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; TH.ProductID &lt;font color="#696969"&gt;=&lt;/font&gt; P.ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;group by&lt;/font&gt; P.[Name]         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; [TotalQuantity] &lt;font color="#0000ff"&gt;desc&lt;/font&gt;&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;TotalQuantity &lt;/td&gt;              &lt;td&gt;Ranking &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;----------------- &lt;/td&gt;              &lt;td&gt;------------- &lt;/td&gt;              &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;BB Ball Bearing &lt;/td&gt;              &lt;td&gt;374090 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Seat Stays &lt;/td&gt;              &lt;td&gt;187376 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Blade &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Chain Stays &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Fork End &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;HL Crankarm &lt;/td&gt;              &lt;td&gt;64900 &lt;/td&gt;              &lt;td&gt;6 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;HL Hub &lt;/td&gt;              &lt;td&gt;56264 &lt;/td&gt;              &lt;td&gt;7 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Decal 1 &lt;/td&gt;              &lt;td&gt;56250 &lt;/td&gt;              &lt;td&gt;8 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Decal 2 &lt;/td&gt;              &lt;td&gt;56250 &lt;/td&gt;              &lt;td&gt;8 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;LL Mountain Pedal &lt;/td&gt;              &lt;td&gt;50922 &lt;/td&gt;              &lt;td&gt;10 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;As you can see, RANK has correctly identified the 'ties' and assigned Blade, Chain Stays and Fork End an identical RANK. In addition, it has correctly assigned HL Crankarm a RANK of sixth as we have 3 products that came in third. &lt;/p&gt;    &lt;p&gt;DENSE_RANK behaves identical to RANK except that it does not increment the rank each time a duplicate is read. Using RANK we have no fourth or fifth placed products as the 3 third placed products have hidden the fourth and fifth placed ranks (i.e. we have non-consecutive ranks). DENSE_RANK does not skip ranks in this manner. Changing our query to use DENSE_RANK becomes: &lt;/p&gt;   &lt;font style="font-size: small; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace"&gt;     &lt;div class="tsql"&gt;&lt;font color="#0000ff"&gt;select&lt;/font&gt; P.[Name],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; sum(TH.Quantity) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [TotalQuantity],         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; dense_rank() &lt;font color="#0000ff"&gt;over&lt;/font&gt;(&lt;font color="#0000ff"&gt;order by&lt;/font&gt; sum(quantity) &lt;font color="#0000ff"&gt;desc&lt;/font&gt;) &lt;font color="#0000ff"&gt;as&lt;/font&gt; [Ranking]         &lt;br /&gt;&lt;font color="#0000ff"&gt;from&lt;/font&gt; Production.TransactionHistory TH         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; inner join Production.Product P         &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;font color="#0000ff"&gt;on&lt;/font&gt; TH.ProductID = P.ProductID         &lt;br /&gt;&lt;font color="#0000ff"&gt;group by&lt;/font&gt; P.Name         &lt;br /&gt;&lt;font color="#0000ff"&gt;order by&lt;/font&gt; [TotalQuantity] &lt;font color="#0000ff"&gt;desc&lt;/font&gt;&lt;font color="#696969"&gt;;         &lt;br /&gt;&lt;/font&gt;         &lt;br /&gt;        &lt;table class="grid"&gt;&lt;tbody&gt;           &lt;tr&gt;             &lt;td&gt;Name &lt;/td&gt;              &lt;td&gt;TotalQuantity &lt;/td&gt;              &lt;td&gt;Ranking &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;------------------ &lt;/td&gt;              &lt;td&gt;------------- &lt;/td&gt;              &lt;td&gt;-------------------- &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;BB Ball Bearing &lt;/td&gt;              &lt;td&gt;374090 &lt;/td&gt;              &lt;td&gt;1 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Seat Stays &lt;/td&gt;              &lt;td&gt;187376 &lt;/td&gt;              &lt;td&gt;2 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Blade &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Chain Stays &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Fork End &lt;/td&gt;              &lt;td&gt;93688 &lt;/td&gt;              &lt;td&gt;3 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;HL Crankarm &lt;/td&gt;              &lt;td&gt;64900 &lt;/td&gt;              &lt;td&gt;4 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;HL Hub &lt;/td&gt;              &lt;td&gt;56264 &lt;/td&gt;              &lt;td&gt;5 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Decal 1 &lt;/td&gt;              &lt;td&gt;56250 &lt;/td&gt;              &lt;td&gt;6 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;Decal 2 &lt;/td&gt;              &lt;td&gt;56250 &lt;/td&gt;              &lt;td&gt;6 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;LL Mountain Pedal &lt;/td&gt;              &lt;td&gt;50922 &lt;/td&gt;              &lt;td&gt;7 &lt;/td&gt;           &lt;/tr&gt;            &lt;tr&gt;             &lt;td&gt;... &lt;/td&gt;           &lt;/tr&gt;         &lt;/tbody&gt;&lt;/table&gt;     &lt;/div&gt;   &lt;/font&gt;    &lt;p&gt;Next we'll explore the OVER clause in a bit more detail to see how partitions work. &lt;/p&gt; &lt;/font&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='//blogger.googleusercontent.com/tracker/3912586870549609682-4570246073439798366?l=www.fotia.co.uk%2Ffotia%2FBlog%2FAllFiredUp...html'/&gt;&lt;/div&gt;</description><link>http://www.fotia.co.uk/fotia/Blog/2006/03/rank-and-denserank-ranking-functions.html</link><author>noreply@blogger.com (Stefan Delmarco)</author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></item></channel></rss>