views:

19636

answers:

5

I've been doing a convert(varchar,datefield,112) on each date field that I'm using in 'between' queries in SQL server to ensure that I'm only accounting for dates and not missing any based on the time part of datetime fields.

Now, I'm hearing that the converts aren't indexable and that there are better methods, in SQL Server 2005, to compare the date part of datetimes in a query to determine if dates fall in a range.

What is the optimal, indexable, method of doing something like this:

select * from appointments where appointmentDate>='08-01-2008' and appointmentDate<'08-15-2008'

+1  A: 

It's correct - doing the conversion will execute the conversion for every row queried. It's better to leave the date columns as dates, and pass in your where clauses as dates:

select * from appointments where appointmentdate between 
'08/01/2008' AND '08/16/2008'

Note: Leaving off the time means midnight (00:00.000), so you will include all times for 08/01, and all times from 08/15, and anything that is exactly 08/16/2008 00:00:00

Kieveli
+23  A: 

The best way to strip the time portion of a datetime field is using datediff and dateadd functions.

   DateAdd(day, datediff(day,0, MydateValue), 0)

This takes advantedge of the fact that SQL Server stores dates as two integers, one representing the number of days since day "0" - (1 jan 1900), and the second one which represents the number of milleseconds since midnight (for the time).

the formula above simply has to only read the first integer. There is no conversion or processing required, so it is extremely fast.

To make your queries use an index... use this formula on the input filtering parameters first, or on the "other" side of the equal sign from the tables date time field, so that the query optimizer does not have to run the calculation on every datetime field in the table to determine which rows satisfy the filter predicate. This makes your search argument "SARG-able" (Search ARGument)

Where MyDateTimeColumn > DateAdd(day, 
      datediff(day,0, @MydateParameter), 0)    -- SARG-able

rather than

Where DateAdd(day, datediff(day,0, 
      MyDateTimeColumn ), 0) > MydateParameter -- Not SARG-able
Charles Bretana
FREAKING AWESOME! This just saved me a ton of work, processing time and sanity.
Jay Stevens
If SO had a "Recommend new accepted answer", I'd pick this one. Nice work!
rwmnau
Thanks, worked like a charm.
Chuck Conway
+1  A: 

Have a computed persisted column calculate the expression you need. If columns are computed and persisted, they can also be indexed.

devio
+1  A: 

Converting numeric types to string values (a type of Boxing) is not the best performing method of doing what you are looking for. Its not really about index-able, because the actual column type is date time.

If you are looking for the best way query for dates, then your example is right, but you may want to take into account the 3 ms precision difference in MSSQL. It can mean that records from one day can show up in another day's result.

This

select * from appointments where appointmentDate>='08-01-2008' and appointmentDate<'08-15-2008'

Should be this

select * from appointments where appointmentDate>='08-01-2008' and appointmentDate<='08-14-2008 23:59:59.996'
StingyJack
The final bit here is not good advice. Using <= and the .997 value will cause someone problems some time because it relies on the resolution of the datetime data type. If the table was changed to smalldatetime, datetime2, or date, the queries would break. Using the correct notation of >= AND < will still work with any of those changes.
Emtucifor
By not including it, you can easily fall victim to the lack of precision. DateTime2 and date are n/a on SQL 2005 =<
StingyJack
A: 

To compare date parts only, then it is the perfect solution.

Compare Date only in DateTime Field

Rare Solutions
Perfect solution if you want a table scan!
Martin Smith