views:

61

answers:

1

I've been storing all dates in the database as UTC time for several years now, but recently, it's becoming more of a pain to deal with date range queries in reporting services where the date range is specified in local time.

Specifically, let's take this example data set:

EmailAddress                DateCreated
[email protected]    2009-09-01 00:00:00
[email protected]    2009-09-01 00:00:00
[email protected]    2009-02-28 04:00:00
[email protected]    2009-04-27 14:33:00
[email protected]    2009-08-31 17:28:00
[email protected]    2009-03-19 18:57:00
[email protected]    2009-03-01 00:49:00
[email protected]    2009-02-28 04:00:00
[email protected]    2009-09-01 00:00:00
[email protected]    2009-09-16 00:00:00

Now let's say the client wants to see how many records exist for the month of February using central standard time. I cannot simply query for records using UTC time, because the UTC timestamp must be converted to CST before the grouping takes place. In other words, record #7 (2009-03-01 00:49:00) should be counted under February using CST dates, even though the UTC date puts it squarely in the month of March.

What usually ends up happening is that I write a function to convert the date such that a query looks like so:

select 
    dbo.ConvertToLocalDate(DateCreated), 
    count(*) as [Count]
from 
    example_table
group by 
    dbo.ConvertToLocalDate(DateCreated)

but the performance of such a query is less than desirable for anything more than a few hundred thousand rows. I've tried several variations on the theme, including adding a column to pre-calculate the local date, and also a modified query such as:

select 
    t3.LocalDateCreated, 
    count(*) as [Count]
from 
    example_table t1 
        inner join (
            select 
                t2.Email, 
                dbo.ConvertToLocalDate(t2.DateCreated) as LocalDateCreated
            from
                example_table t2) t3 on t3.Email = t1.Email
group by 
    t3.LocalDateCreated

This all seems really hackish to me though. Is there a better way?

A: 

Use a persisted computed column in your table to store the Local Date, and then index it and use it in your query.

That way, the value will be calculated when the value is inserted, but you don't need to do the maths yourself anywhere.

Also, read my blog post about Scalar Functions at http://msmvps.com/blogs/robfarley/archive/2009/12/05/dangers-of-begin-and-end.aspx - you may want to rethink the way you're using that function.

Rob Farley
I'm aware of the effect of the function on the query optimizer and the fact that the function call is the main reason for my performance woes. I've used a secondd column for holding the local date before to get rid of the function call, but it seems weird to me to have a DateCreatedUtc and then a DateCreatedLocal column right next to it. Maybe I'm worrying about something I shouldn't?
Chris
I mean to say that it seems weird to require a second column strictly for reporting purposes.
Chris
Yeah, you shouldn't worry. If it's a computed column (persisted or not), you don't have to worry about integrity issues, and the performance gain will be significant.
Rob Farley