<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'T-SQL Tuesday' and 'datetime'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=T-SQL+Tuesday,datetime&amp;orTags=0</link><description>Search results matching tags 'T-SQL Tuesday' and 'datetime'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Unambiguous date formats : T-SQL Tuesday #001</title><link>http://sqlblog.com/blogs/jamie_thomson/archive/2009/12/08/unambiguous-date-formats-t-sql-tuesday-001.aspx</link><pubDate>Tue, 08 Dec 2009 20:23:41 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:19629</guid><dc:creator>jamiet</dc:creator><description>&lt;p&gt;One of the most commonly used data types in SQL Server is &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; which unfortunately has some vagaries around how values get casted. A typical method for defining a &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; literal is to write it as a character string and then cast it appropriately. The cast syntax looks something like this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;table cellspacing="0" cellpadding="0" bgcolor="#eeeeee"&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre style="padding-bottom:10px;padding-left:20px;padding-right:20px;font-family:consolas,lucida console,courier new,courier;font-size:12px;padding-top:10px;-moz-background-clip:border;-moz-background-origin:padding;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;DECLARE &lt;/font&gt;&lt;font color="#434343"&gt;@dt &lt;/font&gt;&lt;font color="blue"&gt;NVARCHAR&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;19&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="red"&gt;'2009-12-08 18:00:00'&lt;/font&gt;&lt;font color="gray"&gt;;
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="magenta"&gt;CAST&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@dt &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;datetime&lt;/font&gt;&lt;font color="gray"&gt;);
&lt;br /&gt;&lt;/font&gt;&lt;/pre&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately in SQL Server 2005 the result of the cast operation may be dependent on your current language setting. You can discover your current language setting by executing:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;table cellspacing="0" cellpadding="0" bgcolor="#eeeeee"&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;pre style="padding-bottom:10px;padding-left:20px;padding-right:20px;font-family:consolas,lucida console,courier new,courier;font-size:12px;padding-top:10px;-moz-background-clip:border;-moz-background-origin:padding;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="magenta"&gt;@@LANGUAGE&lt;/font&gt;&lt;/pre&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;To demonstrate how your language setting can influence the results of a cast take a look at the following code:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;table cellspacing="0" cellpadding="0" bgcolor="#eeeeee"&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;pre style="padding-bottom:10px;padding-left:20px;padding-right:20px;font-family:consolas,lucida console,courier new,courier;font-size:12px;padding-top:10px;-moz-background-clip:border;-moz-background-origin:padding;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;ALTER&amp;#160; DATABASE &lt;/font&gt;&lt;font color="black"&gt;tempdb
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SET&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;COMPATIBILITY_LEVEL &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;90 &lt;/font&gt;&lt;font color="gray"&gt;; &lt;/font&gt;&lt;font color="green"&gt;--Behave like SQL Server 2005
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;USE &lt;/font&gt;&lt;font color="black"&gt;tempdb
&lt;br /&gt;GO
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;DECLARE &lt;/font&gt;&lt;font color="#434343"&gt;@t &lt;/font&gt;&lt;font color="blue"&gt;TABLE &lt;/font&gt;&lt;font color="gray"&gt;(
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;dateString&amp;#160; &lt;/font&gt;&lt;font color="blue"&gt;NVARCHAR&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;19&lt;/font&gt;&lt;font color="gray"&gt;)
&lt;br /&gt;);
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;INSERT &lt;/font&gt;&lt;font color="#434343"&gt;@t &lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;dateString&lt;/font&gt;&lt;font color="gray"&gt;)
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;VALUES &lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="red"&gt;'2009-12-08 18:00:00'&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="green"&gt;--'yyyy-MM-dd hh24:mi:ss'
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (&lt;/font&gt;&lt;font color="red"&gt;'2009-12-08T18:00:00'&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="green"&gt;--'yyyy-MM-ddThh24:mi:ss'
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (&lt;/font&gt;&lt;font color="red"&gt;'20091208 18:00:00'&lt;/font&gt;&lt;font color="gray"&gt;)&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="green"&gt;--'yyyyMMdd hh24:mi:ss'
&lt;br /&gt;
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SET&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;LANGUAGE french&lt;/font&gt;&lt;font color="gray"&gt;;
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="red"&gt;'french' &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;lang
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="magenta"&gt;DATENAME&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="magenta"&gt;MONTH&lt;/font&gt;&lt;font color="gray"&gt;,&lt;/font&gt;&lt;font color="black"&gt;q.[dt]&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;mnth
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;q.[dt]
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;FROM&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="gray"&gt;(
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="blue"&gt;SELECT&amp;#160; &lt;/font&gt;&lt;font color="magenta"&gt;CAST&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;dateString &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;DATETIME&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;dt
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="blue"&gt;FROM&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="#434343"&gt;@t
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="gray"&gt;)&lt;/font&gt;&lt;font color="black"&gt;q&lt;/font&gt;&lt;font color="gray"&gt;;
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SET&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;LANGUAGE us_english&lt;/font&gt;&lt;font color="gray"&gt;;
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="red"&gt;'us_english' &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;lang
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="magenta"&gt;DATENAME&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="magenta"&gt;MONTH&lt;/font&gt;&lt;font color="gray"&gt;,&lt;/font&gt;&lt;font color="black"&gt;q.[dt]&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;mnth
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;q.[dt]
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;FROM&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="gray"&gt;(
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="blue"&gt;SELECT&amp;#160; &lt;/font&gt;&lt;font color="magenta"&gt;CAST&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;dateString &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;DATETIME&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;dt
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="blue"&gt;FROM&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="#434343"&gt;@t
&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="gray"&gt;)&lt;/font&gt;&lt;font color="black"&gt;q&lt;/font&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;/pre&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are taking the value which can be described in words as “6pm on 8th December 2009”, defining it in three different ways, then seeing how the &lt;font color="#ff00ff" face="Courier New"&gt;@@LANGUAGE&lt;/font&gt; setting can affect the results. Here are those results:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/image_1CC2DDFC.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="french language datetime" border="0" alt="french language datetime" src="http://sqlblog.com/blogs/jamie_thomson/image_thumb_3B2D1EE5.png" width="321" height="203" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice how the interpretation of the month can change depending on &lt;font color="#ff00ff" face="Courier New"&gt;@@LANGUAGE&lt;/font&gt;. If &lt;font face="Courier New"&gt;&lt;font color="#ff00ff"&gt;@@LANGUAGE&lt;/font&gt;=’french’ &lt;/font&gt;then the string '2009-12-08 18:00:00' is interpreted as 12&lt;sup&gt;th&lt;/sup&gt; August 2009 (‘août’ is French for August for those that don’t know) whereas if &lt;font face="Courier New"&gt;&lt;font color="#ff00ff"&gt;@@LANGUAGE&lt;/font&gt;=’us_english’ &lt;/font&gt;it is interpreted as 8&lt;sup&gt;th&lt;/sup&gt; December 2009.&lt;/p&gt;

&lt;p&gt;Clearly this is a problem because the results of our queries have a dependency on a server-level or connection-level setting and that is NOT a good thing. Hence I recommend that you only define &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; literals in one of the two unambiguous date formats:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;yyyy-MM-dd&lt;strong&gt;&lt;u&gt;T&lt;/u&gt;&lt;/strong&gt;HH24:mi:ss &lt;/li&gt;

  &lt;li&gt;yyyyMMdd HH24:mi:ss &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;That was going to be the end of this blog post but then I found out that this behaviour changed slightly in SQL Server 2008. Take the following code (see if you can figure out what the results will be before I tell you):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;table cellspacing="0" cellpadding="0" bgcolor="#eeeeee"&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;pre style="padding-bottom:10px;padding-left:20px;padding-right:20px;font-family:consolas,lucida console,courier new,courier;font-size:12px;padding-top:10px;-moz-background-clip:border;-moz-background-origin:padding;-moz-background-inline-policy:continuous;"&gt;&lt;font color="blue"&gt;ALTER&amp;#160; DATABASE &lt;/font&gt;&lt;font color="black"&gt;tempdb
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SET&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;COMPATIBILITY_LEVEL &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="black"&gt;100 &lt;/font&gt;&lt;font color="gray"&gt;; &lt;/font&gt;&lt;font color="green"&gt;--Behave like SQL Server 2008
&lt;br /&gt;&lt;/font&gt;&lt;font color="black"&gt;GO
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;USE &lt;/font&gt;&lt;font color="black"&gt;tempdb
&lt;br /&gt;GO
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SET&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="black"&gt;LANGUAGE french&lt;/font&gt;&lt;font color="gray"&gt;;
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;DECLARE&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="#434343"&gt;@dt &lt;/font&gt;&lt;font color="magenta"&gt;NCHAR&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="black"&gt;10&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;= &lt;/font&gt;&lt;font color="red"&gt;'2009-12-08 18:00:00'&lt;/font&gt;&lt;font color="gray"&gt;; &lt;/font&gt;&lt;font color="green"&gt;--Ambiguous date format
&lt;br /&gt;&lt;/font&gt;&lt;font color="blue"&gt;SELECT &lt;/font&gt;&lt;font color="magenta"&gt;CAST&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@dt &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;datetime&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;[ExplicitCast]
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="magenta"&gt;DATENAME&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="magenta"&gt;MONTH&lt;/font&gt;&lt;font color="gray"&gt;,&lt;/font&gt;&lt;font color="#434343"&gt;@dt&lt;/font&gt;&lt;font color="gray"&gt;) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;[MonthFromImplicitCast]
&lt;br /&gt;&lt;/font&gt;&lt;font color="gray"&gt;,&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/font&gt;&lt;font color="magenta"&gt;DATENAME&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="magenta"&gt;MONTH&lt;/font&gt;&lt;font color="gray"&gt;,&lt;/font&gt;&lt;font color="magenta"&gt;CAST&lt;/font&gt;&lt;font color="gray"&gt;(&lt;/font&gt;&lt;font color="#434343"&gt;@dt &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;datetime&lt;/font&gt;&lt;font color="gray"&gt;)) &lt;/font&gt;&lt;font color="blue"&gt;AS &lt;/font&gt;&lt;font color="black"&gt;[MonthFromExplicitCast]&lt;/font&gt;&lt;font color="gray"&gt;;&lt;/font&gt;&lt;/pre&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here we are doing three different things with our nchar literal:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;explicitly cast it as a &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; &lt;/li&gt;

  &lt;li&gt;extract the month name from the char literal using the &lt;font color="#ff00ff"&gt;DATENAME&lt;/font&gt; function (which results in an under-the-covers implicit cast) &lt;/li&gt;

  &lt;li&gt;extract the month name from the char literal using the &lt;font color="#ff00ff"&gt;DATENAME&lt;/font&gt; function after it has been explicitly casted as a &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that the compatibility level is set to SQL Server 2008 and &lt;font face="Courier New"&gt;&lt;font color="#ff00ff"&gt;@@LANGUAGE&lt;/font&gt;=’french’&lt;/font&gt;. Here are the results:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/image_79B26C8B.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/jamie_thomson/image_thumb_519FBD6C.png" width="431" height="87" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Were you correct?)&lt;/p&gt;

&lt;p&gt;Let’s take a look at what is happening here. The behaviour when we are explicitly casting as &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; hasn’t changed, our nchar literal is still getting interpreted as 12th August rather than 8th December when &lt;font face="Courier New"&gt;&lt;font face="Courier New"&gt;&lt;font color="#ff00ff"&gt;@@LANGUAGE&lt;/font&gt;&lt;/font&gt;=’french’&lt;/font&gt;. The &lt;font face="Courier New"&gt;[MonthFromExplicitCast]&lt;/font&gt; field is interesting though, it seems as though the implicit cast has resulted in the desired value of 8th December. Why is that?&lt;/p&gt;

&lt;p&gt;To get the answer we can turn to &lt;a href="http://msdn.microsoft.com/en-us/library/ms174395.aspx"&gt;BOL’s description&lt;/a&gt; of the &lt;font color="#ff00ff"&gt;DATENAME&lt;/font&gt; function syntax:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/image_6C6BE378.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/jamie_thomson/image_thumb_6B937D8E.png" width="244" height="85" /&gt;&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The implicit cast is not casting to &lt;font face="Courier New"&gt;[datetime]&lt;/font&gt; at all, it is actually casting to [date] which is a new datatype in SQL Server 2008. The new date-related datatypes in SQL Server 2008 (i.e. &lt;font face="Courier New"&gt;[date], [datetime2], [time], [datetimeoffset]&lt;/font&gt;) dis&lt;em&gt;regard&lt;/em&gt;&amp;#160;&lt;font face="Courier New"&gt;&lt;font color="#ff00ff"&gt;@@LANGUAGE&lt;/font&gt;&lt;/font&gt; and hence we get behaviour that is more predictable and, frankly, better.&lt;/p&gt;

&lt;p&gt;These new behaviours for SQL Server 2008 were unknown to me when I began this blog post so I have learnt something in the course of authoring it, I hope it has helped you too. No doubt someone somewhere is going to get nastily burnt by this at some point, make sure that it isn’t you by always using unambiguous date formats:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;yyyy-MM-ddTHH24:mi:ss &lt;/li&gt;

  &lt;li&gt;yyyyMMdd HH24:mi:ss &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;regardless of which version you are on!&lt;/p&gt;

&lt;p&gt;Don’t forget to check out other &lt;a href="http://sqlblog.com/tags/T-SQL+Tuesday/default.aspx"&gt;T-SQL Tuesday&lt;/a&gt; blog posts, see Adam Machanic’s blog post &lt;a href="http://sqlblog.com/blogs/adam_machanic/archive/2009/11/30/invitation-to-participate-in-t-sql-tuesday-001-date-time-tricks.aspx"&gt;Invitation to Participate in T-SQL Tuesday #001: Date/Time Tricks&lt;/a&gt; for details!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://twitter.com/jamiet"&gt;@Jamiet&lt;/a&gt;&lt;/p&gt;</description></item><item><title>T-SQL Tuesday #001: Exploring &amp;quot;Fuzzy&amp;quot; Interval Islands Using SQLCLR</title><link>http://sqlblog.com/blogs/adam_machanic/archive/2009/12/08/t-sql-tuesday-001-exploring-fuzzy-interval-islands-using-sqlclr.aspx</link><pubDate>Tue, 08 Dec 2009 19:13:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:19621</guid><dc:creator>Adam Machanic</dc:creator><description>&lt;p&gt;When working with time intervals, we often want to ask a couple of basic questions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which time periods are not covered by our intervals? These are known as "gaps".&lt;/li&gt;

&lt;li&gt;What are the time ranges that we are fully covering? These are known as "islands".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're unfamiliar with "gaps" and "islands" I highly recommend reading some of Itzik Ben-Gan's recent work in SQL Server Magazine. He's had a great series going on the topic. But one thing that he hasn't found a good T-SQL solution for is a problem that I call "fuzzy islands." &lt;/p&gt;

&lt;p&gt;&lt;b&gt;When is an island fuzzy?&lt;/b&gt; When it doesn't necessarily have a fixed end time. For an example of this, consider a store, selling a number of products. Management might want to see a report showing when each product was selling. This is, effectively, an island question. The goal is to find all of the covered ranges during which sales occurred. But running such a report, you might find that way too much data is returned. A given product may have sold units on Monday, Tuesday, and Thursday, but for some reason no one bought one on Wednesday. Creating a new island every time there is a small gap will create a 300-page report where a 1-page dashboard might suffice--not a good user experience, nor a good way of representing the data. The solution? Introduce a bit of fuzziness--a rule that says, for instance, that a gap is only a gap if it's longer than 7 days.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Answering the fuzzy islands question &lt;/b&gt;is not a very difficult thing to do in T-SQL. The basic algorithm follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find all of the "start" dates or times. These are simply those dates or times for which a previous date or time in the fuzzy interval does not exist. So if a row is dated 2009-12-08 and our fuzzy granularity is 7 days, we know we have a start date if there is no other covered data from the end of November.&lt;/li&gt;

&lt;li&gt;For each start date identified, find the minimum date greater than the start date. This is done by looking ahead rather than behind, so if our date is 2009-12-08 and we have a granularity of 7 days, we'll look forward until December 15th.&lt;/li&gt;

&lt;li&gt;Optionally, add the fuzzy factor to the end date. This is something that I think is a good idea, as it introduces the concept of an "active" interval--a period over which, for example, a product is considered to have been selling. The interval shouldn't necessarily terminate the day that the last sale occurred. But of course this depends on the situation in question.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following query produces a fuzzy islands report for each product in the AdventureWorks (or AdventureWorks2008) Production.TransactionHistory table. You can modify the @active_interval variable to tweak the fuzziness and change the output. &lt;br&gt;&lt;/p&gt;&lt;blockquote&gt;
&lt;pre&gt;--Find all "active" product time ranges, meaning that the product&lt;br&gt;--has sold within the previous 7 days&lt;br&gt;DECLARE @active_interval INT = 7&lt;br&gt;&lt;br&gt;SELECT DISTINCT&lt;br&gt;    t_s.ProductID,&lt;br&gt;    t_s.TransactionDate AS StartDate,&lt;br&gt;    DATEADD&lt;br&gt;    (&lt;br&gt;        dd,&lt;br&gt;        @active_interval,&lt;br&gt;        (&lt;br&gt;            SELECT&lt;br&gt;                MIN(t_e.TransactionDate)&lt;br&gt;            FROM Production.TransactionHistory AS t_e&lt;br&gt;            WHERE&lt;br&gt;                t_e.ProductID = t_s.ProductID&lt;br&gt;                AND t_e.TransactionDate &amp;gt;= t_s.TransactionDate&lt;br&gt;                AND NOT EXISTS&lt;br&gt;                (&lt;br&gt;                    SELECT *&lt;br&gt;                    FROM Production.TransactionHistory AS t_ae&lt;br&gt;                    WHERE&lt;br&gt;                        t_ae.ProductID = t_s.ProductID&lt;br&gt;                        AND t_ae.TransactionDate BETWEEN &lt;br&gt;                            DATEADD(dd, 1, t_e.TransactionDate) &lt;br&gt;                            AND DATEADD(dd, @active_interval, t_e.TransactionDate)&lt;br&gt;                )&lt;br&gt;        )&lt;br&gt;    ) AS EndDate&lt;br&gt;FROM &lt;br&gt;(&lt;br&gt;    SELECT DISTINCT&lt;br&gt;        ProductID,&lt;br&gt;        TransactionDate&lt;br&gt;    FROM Production.TransactionHistory&lt;br&gt;) AS t_s&lt;br&gt;WHERE&lt;br&gt;    NOT EXISTS&lt;br&gt;    (&lt;br&gt;        SELECT *&lt;br&gt;        FROM Production.TransactionHistory AS t_ps&lt;br&gt;        WHERE&lt;br&gt;            t_ps.ProductID = t_s.ProductID&lt;br&gt;            AND t_ps.TransactionDate BETWEEN &lt;br&gt;                DATEADD(dd, -@active_interval, t_s.TransactionDate) &lt;br&gt;                AND DATEADD(dd, -1, t_s.TransactionDate)&lt;br&gt;    )&lt;br&gt;ORDER BY&lt;br&gt;    ProductID,&lt;br&gt;    StartDate&lt;br&gt;GO&lt;/pre&gt;
&lt;/blockquote&gt;

&lt;p&gt;Running this query you'll find that it works... But the results are returned a bit more slowly than we might desire--15 to 16 seconds on my end. Looking at the query plan, the reason for this becomes quite obvious: Lots and lots of table scans. How can we eliminate all of the overhead?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;SQLCLR to the rescue.&lt;/b&gt; The best way to solve this problem--at least until the SQL Server team adds proper OVER clause support (LAG and LEAD, specifically)--is to use a cursor algorithm. We could do this in a T-SQL cursor, but why bother? Cursor logic in SQLCLR is much, much faster. &lt;/p&gt;
&lt;p&gt;To solve the problem, I implemented an enumerator, called active_products_enumerator. The enumerator is initialized using a SqlDataReader and an "active interval" -- the number of days we're allowing for fuzziness. The DataReader is expected to return rows ordered by ProductID and TransactionDate. The enumeration process uses the following algorithm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the current ProductID is not the same as the previous ProductID, return an end date for the previous interval and start a new one&lt;/li&gt;
&lt;li&gt;If the current period date is greater than the previous period date plus the active interval, return an end date for the previous interval and start a new one&lt;/li&gt;
&lt;li&gt;Otherwise, continue&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Following is the MoveNext method for the enumerator (the complete code is attached to this post so that you can run it on your end without my bombarding you with a gigantic code-filled post):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;p&gt;public bool MoveNext()&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; current_results = null;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (r.Read())&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new_ProductID = r.GetSqlInt32(0);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new_period_date = r.GetDateTime(1);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (new_ProductID != ProductID)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (ProductID != 0)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; current_results = new results(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductID,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StartDate,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; period_plus_interval);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductID = new_ProductID;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StartDate = new_period_date;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; period_plus_interval = new_period_date.AddDays(activeInterval);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (current_results != null)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (true);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (period_plus_interval &amp;lt; new_period_date)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; current_results = new results(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductID,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StartDate,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; period_plus_interval);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StartDate = new_period_date;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; period_plus_interval = new_period_date.AddDays(activeInterval);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (current_results != null)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (true);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //return the last row of data&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (ProductID != 0)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; current_results = new results(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductID,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StartDate,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; period_plus_interval);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //set this to 0 so we don't return another row&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductID = 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (current_results != null)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (true);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; r.Dispose();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (false);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; r.Dispose();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;}&lt;/p&gt;&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;Put into a table-valued function, as the attached code does, this algorithm will return the same data as the T-SQL query in under a third of a second on my end--&lt;span style="font-weight:bold;"&gt;around 45 times faster than the T-SQL version&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;Note that I've played some games with a loopback connection to get this whole thing to work. That's a topic for a future blog post, so stay tuned. In the meantime, please realize that you'll have to catalog the assembly with EXTERNAL_ACCESS permission to make this happen.&lt;/p&gt;&lt;p&gt;This post was created for T-SQL Tuesday, the revolving SQL Server blog party, hosted this month by... &lt;a href="http://sqlblog.com/blogs/adam_machanic/archive/2009/11/30/invitation-to-participate-in-t-sql-tuesday-001-date-time-tricks.aspx"&gt;me&lt;/a&gt;. Enjoy! &lt;br&gt;&lt;/p&gt;</description></item><item><title>T-SQL Tuesday #001: My Datetime FAQ</title><link>http://sqlblog.com/blogs/kalen_delaney/archive/2009/12/08/my-datetime-faq.aspx</link><pubDate>Tue, 08 Dec 2009 18:49:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:19616</guid><dc:creator>Kalen Delaney</dc:creator><description>&lt;P&gt;&lt;EM&gt;I am rising to Adam's challenge for his very first &lt;/EM&gt;&lt;A href="http://sqlblog.com/blogs/adam_machanic/archive/2009/11/30/invitation-to-participate-in-t-sql-tuesday-001-date-time-tricks.aspx" target=_blank&gt;&lt;EM&gt;T-SQL Tuesday&lt;/EM&gt;&lt;/A&gt;&lt;EM&gt;, and blogging about datetime issues. Since I just finished a 9-hour teaching day in Stockholm, and I'm incredibly jetlagged, I decided to keep this simple. I'm very much looking forward to what everyone else is going to say!&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;For as long as I've been providing TSQL support, starting over 22 years ago while working for Sybase,&amp;nbsp; there have been confusions about datetime data storage and retrieval. I have seen the same questions over and over for all of those years, so I am listing the all-time most common datetime questions here. My FAQ stands for "Frequently ANSWERED Questions"! To keep the post simple, I am not going into complete detail for every answer. If you want more details you can read one of my books.&amp;nbsp; I am also just listing the tip of the iceberg in terms of common datetime questions. &lt;/P&gt;
&lt;P&gt;1. I have my dates stored like this: "March 4, 1948" but SQL Server is returning them like this: "1948-03-04 13:17:32.120".&amp;nbsp; How can I get the dates returned in the manner I stored them? &lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;This question actually can't be answered as written, because it is NOT true that a date could be stored as "March 4, 1948".&amp;nbsp; A character string could be stored that way, but not a date. Internally, datetime data is stored in a format you never see displayed, no matter how you enter the datetime value. It is stored as two integers, the first being the number of days before or after the base date of "January 1 1900" and the second integer being the number of milliseconds after midnight. &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;You can actually see these two parts if you convert a datetime value to a binary string of 8 hexadecimal bytes. The first 4 hexadecimal bytes are the number of days before or after the base date, and the second 4 bytes are the number of clock ticks after midnight. You can then convert these 4-byte hexadecimal strings to integers. &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;You can see the component parts of the current date and time by using the system function getdate(). I first store the current date and time in a local variable so I can be sure I'm using the same value for both computations. &lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=1&gt;DECLARE @today datetime &lt;BR&gt;SELECT @today = getdate() &lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=1&gt;SELECT @today &lt;BR&gt;SELECT CONVERT (varbinary(8), @today) &lt;BR&gt;SELECT CONVERT (int, SUBSTRING (CONVERT (varbinary(8), @today), 1, 4)) &lt;BR&gt;SELECT CONVERT (int, SUBSTRING (CONVERT (varbinary(8), @today), 5, 4))&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;The way a datetime value is displayed depends on several factors, including the tool you are using and your machine's regional settings. If you want to control the way a datetime value is displayed, you can convert it to a character string.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;2. Why doesn't this query find any rows:&amp;nbsp; &lt;FONT face="courier new" size=1&gt;SELECT * FROM orders WHERE orderdate = 'March 4, 2009'&lt;/FONT&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;The most like reason is because datetime data always includes a time portion as well as a date portion. Unless your orders were entered at a time of midnight (the default), they won't match the datetime value shown. (This answer frequently leads to the next question.)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;3. Why can't I store just a date or just a time, why do I always need both?&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;The simple answer, prior to SQL Server 2008 is "just because". That is the way SQL Server works. Microsoft has been promising individual date and time datatypes for several versions now, and they finally showed up in SQL Server 2008, as well as a couple of other new datetime datatypes, plus the ability to specify a precision for fractional part a second.&lt;/FONT&gt; &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;4.&amp;nbsp; Why doesn't this query show me how old I am? &lt;FONT face="Courier New" size=1&gt;SELECT datediff(yy, @my_birthdate, getdate() )&lt;/FONT&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;Arithmetic with datetime values works only with whatever datepart is specified as the first parameter and does no rounding. Since my first parameter is yy, for year, the datediff simply subtracts the year component of my birth year from the year component of today's date. If I haven't had my birthday yet, it will show me how old I will be on my birthday this year, which will not be my current age. And since birthday is almost at the end of the year, for most of the year I will get an incorrect number.&lt;/FONT&gt; &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;5. Why can't I store dates earlier than 1753 using the datetime datatype? &lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;This was done for historical reasons, and started with the original Sybase specification for the datetime datatype. In what we sometimes refer to as the western world, we have had two calendars in modern time: the Julian and the Gregorian calendars. These calendars were a number of days apart (depending on which century you look at), so when a culture that used the Julian calendar moved to the Gregorian calendar, they dropped between 10 to 13 days from the calendar. Great Britain made this shift in 1752, and in that year, September 2&lt;SUP&gt;nd&lt;/SUP&gt; was followed by September 14&lt;SUP&gt;th&lt;/SUP&gt;. Sybase decided not to stored dates earlier than 1753 because the date arithmetic functions would be ambiguous. However, other countries made the change at other times, and in Turkey the calendar was not shifted until 1927.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Times New Roman" size=1&gt;The new date and datetime2 times in SQL Server 2008 do allow us to store dates back to 0001-01-01. &lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;For one of the best articles on the web about datetime issues in SQL Server, see Tibor Karaszi's website:&lt;/P&gt;
&lt;P&gt;&lt;A title=http://www.karaszi.com/SQLServer/info_datetime.asp href="http://www.karaszi.com/SQLServer/info_datetime.asp"&gt;http://www.karaszi.com/SQLServer/info_datetime.asp&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;I love the calendars in Europe, especially Germany and Sweden, because the first part of the word is my name!&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff00ff size=4&gt;~Kalen&lt;/FONT&gt;&lt;/P&gt;</description></item><item><title>T-SQL Tuesday #001: Yesterday it worked, today it's not working...</title><link>http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/12/08/t-sql-tuesday-001-yesterday-it-worked-today-it-s-not-working.aspx</link><pubDate>Tue, 08 Dec 2009 16:06:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:19623</guid><dc:creator>Alexander Kuznetsov</dc:creator><description>&lt;p&gt;But did it actually work yesterday? &lt;/p&gt;&lt;p&gt;If the day is Friday, Noverber 13th, or the 13th day of any other month, and your query blows up for the first time, one place to search for is character strings converted to datetime values. The following script illustrates the problem:&lt;br&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;code style="font-size:12px;"&gt;&lt;span style="color:green;"&gt;--&amp;nbsp;yesterday&amp;nbsp;it&amp;nbsp;worked,&amp;nbsp;or&amp;nbsp;did&amp;nbsp;it?
&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SET&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;LANGUAGE&amp;nbsp;US_English&lt;/span&gt;&lt;span style="color:gray;"&gt;;
&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT&amp;nbsp;&lt;/span&gt;&lt;span style="color:magenta;"&gt;CAST&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'11/12/2009'&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue;"&gt;AS&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;DATETIME&lt;/span&gt;&lt;span style="color:gray;"&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code style="font-size:12px;"&gt;&lt;span style="color:gray;"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SET&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;LANGUAGE&amp;nbsp;Norwegian&lt;/span&gt;&lt;span style="color:gray;"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT&amp;nbsp;&lt;/span&gt;&lt;span style="color:magenta;"&gt;CAST&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'11/12/2009'&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue;"&gt;AS&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;DATETIME&lt;/span&gt;&lt;span style="color:gray;"&gt;);
&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Changed language setting to us_english.&lt;br&gt;&lt;br&gt;-----------------------&lt;br&gt;2009-11-12 00:00:00.000&lt;br&gt;&lt;br&gt;(1 row(s) affected)&lt;br&gt;&lt;br&gt;Changed language setting to Norsk.&lt;br&gt;&lt;br&gt;-----------------------&lt;br&gt;2009-12-11 00:00:00.000&lt;br&gt;&lt;br&gt;(1 row(s) affected) &lt;br&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;code style="font-size:12px;"&gt;&lt;span style="color:gray;"&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;--&amp;nbsp;today&amp;nbsp;it's&amp;nbsp;not&amp;nbsp;working&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SET&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;LANGUAGE&amp;nbsp;US_English&lt;/span&gt;&lt;span style="color:gray;"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT&amp;nbsp;&lt;/span&gt;&lt;span style="color:magenta;"&gt;CAST&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'11/13/2009'&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue;"&gt;AS&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;DATETIME&lt;/span&gt;&lt;span style="color:gray;"&gt;);
&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code style="font-size:12px;"&gt;&lt;span style="color:gray;"&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SET&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;LANGUAGE&amp;nbsp;Norwegian&lt;/span&gt;&lt;span style="color:gray;"&gt;;
&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT&amp;nbsp;&lt;/span&gt;&lt;span style="color:magenta;"&gt;CAST&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'11/13/2009'&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue;"&gt;AS&amp;nbsp;&lt;/span&gt;&lt;span style="color:black;"&gt;DATETIME&lt;/span&gt;&lt;span style="color:gray;"&gt;);
&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/code&gt;                &amp;nbsp;
              &lt;/p&gt;&lt;p&gt;&amp;nbsp;Changed language setting to us_english.&lt;br&gt;&lt;br&gt;-----------------------&lt;br&gt;2009-11-13 00:00:00.000&lt;br&gt;&lt;br&gt;(1 row(s) affected)&lt;br&gt;&lt;br&gt;Changed language setting to Norsk.&lt;br&gt;&lt;br&gt;-----------------------&lt;br&gt;Msg 242, Level 16, State 3, Line 11&lt;br&gt;The conversion of a char data type to a datetime data type resulted in an out-of-range datetime value.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Note that the query did not quite work yesterday either - although it did not raise errors, it was selecting the wrong data.&lt;br&gt;&lt;/p&gt;</description></item><item><title>Invitation to Participate in T-SQL Tuesday #001: Date/Time Tricks</title><link>http://sqlblog.com/blogs/adam_machanic/archive/2009/11/30/invitation-to-participate-in-t-sql-tuesday-001-date-time-tricks.aspx</link><pubDate>Mon, 30 Nov 2009 17:47:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:19338</guid><dc:creator>Adam Machanic</dc:creator><description>&lt;p&gt;T-SQL Tuesday is the SQL Server blogosphere's first &lt;b&gt;recurring, revolving blog party&lt;/b&gt;.
The idea is simple: Each month a blog will host the party, and about a
week before the second Tuesday of the month a theme will be posted. Any
blogger that wishes to participate is invited to write a post on the
chosen topic. The event is called "T-SQL Tuesday", but &lt;b&gt;any post that is related to both SQL Server and the theme is fair game&lt;/b&gt;.
So feel free to post about SSIS, SSRS, Java integration, or whatever
other technologies you're working with in conjunction with SQL Server.
Even if your post includes no T-SQL we still want to see it. &lt;/p&gt;&lt;p&gt;The
posts must go live on the second Tuesday of the month, by (or before)
23:59 PST. Each post must link back to the host blog's post, and the
blogger should make sure that a trackback has been generated and
successfully posted to the host blog; if not, a comment should be
posted on the host blog with a link, so that the host blogger can find
the post. The host blogger will then take all of the posts and
within a day or two will compile a "roundup"--a brief summary with links--so
that readers can find all of the relevant posts.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Why do this?&lt;/b&gt;
Several reasons: It's fun, it's going to generate a lot of interesting
content, and it's going to bring lots of bloggers into the fold and
improve our overall community by creating a lot of links between
blogs. Any blogger is invited to participate, whether the blog has been
live for six days or six years. If you can write, you should join in
the fun.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Rules of engagement for hosts&lt;/b&gt;: Although anyone
can join in the party, hosting has a slightly higher bar: Your blog
must have been active in the last six months ("active" is defined as at
least one post per month), and you must have participated in T-SQL
Tuesday events on at least two prior occasions. Since that's impossible
until we've had two events, I'll host the first two myself. After that
it will move around to as many blogs as we can get involved--and I hope
that a number of you out there decide to go for it. The host blogger is not required to write a participating post to be included in
the roundup, but is free to do so if he or she chooses. &lt;b&gt;Want to host?&lt;/b&gt; Contact me through my blog--I'll
keep a waiting list and control it centrally so that it doesn't devolve
into anarchy too quickly.&lt;/p&gt;&lt;p&gt;So that's that. If you bothered to read the title of this post you're aware that &lt;b&gt;the topic for this month is Date/Time Tricks&lt;/b&gt;.
Write a blog post that talks about dates and times--this can be based
around T-SQL programming, data modeling, ETL, reporting, or whatever
else you're using dates and times for (and who isn't?). Make sure that
your post goes live on &lt;b&gt;Tuesday, December 8, between 00:00 and 23:59 PST&lt;/b&gt;. Make it clear in your post that it's for T-SQL Tuesday, and make sure it links back here. Finally, &lt;b&gt;make absolutely certain that a comment shows up here&lt;/b&gt; so that I can find the post.&lt;/p&gt;&lt;p&gt;Questions? Comments? Post here.&lt;/p&gt;&lt;p&gt;Enjoy! I'm looking forward to the start of a great blog tradition in the SQL Server community.&lt;br&gt;&lt;/p&gt;</description></item></channel></rss>