35

I have a SQL datetime field in a very large table. It's indexed and needs to be queried.

The problem is that SQL always stores the time component (even though it's always midnight), but the searches are to the day, rather than time.

declare @dateVar datetime = '2013-03-11;

select t.[DateColumn]
from MyTable t
where t.[DateColumn] = dateVar;

Won't return anything, as the t.[DateColumn] always includes a time component.

My question is what is the best way round this?

There seem to be two main groups of options:

  1. Create a second variable using dateadd and use a between ... and or >= ... and ... <=.

  2. Convert the t.[DateColumn] into a date-only component - I think this will cause any indexes to be ignored.

Both of these seem very messy - I don't really want to be making a range comparison or scan the table.

Is there a better way?

If one of these options is consistently optimal way then how and why?

Keith
  • 150,284
  • 78
  • 298
  • 434

4 Answers4

38

Converting to a DATE or using an open-ended date range in any case will yield the best performance. FYI, convert to date using an index are the best performers. More testing a different techniques in article: What is the most efficient way to trim time from datetime? Posted by Aaron Bertrand

From that article:

DECLARE @dateVar datetime = '19700204';

-- Quickest when there is an index on t.[DateColumn], 
-- because CONVERT can still use the index.
SELECT t.[DateColumn]
FROM MyTable t
WHERE = CONVERT(DATE, t.[DateColumn]) = CONVERT(DATE, @dateVar);

-- Quicker when there is no index on t.[DateColumn]
DECLARE @dateEnd datetime = DATEADD(DAY, 1, @dateVar);
SELECT t.[DateColumn] 
FROM MyTable t
WHERE t.[DateColumn] >= @dateVar AND 
      t.[DateColumn] < @dateEnd;

Also from that article: using BETWEEN, DATEDIFF or CONVERT(CHAR(8)... are all slower.

Keith
  • 150,284
  • 78
  • 298
  • 434
Aleksandr Fedorenko
  • 16,594
  • 6
  • 37
  • 44
16

Here is an example:

I've an Order table with a DateTime field called OrderDate. I want to retrieve all orders where the order date is equals to 01/01/2006. there are next ways to do it:

1) WHERE DateDiff(dd, OrderDate, '01/01/2006') = 0
2) WHERE Convert(varchar(20), OrderDate, 101) = '01/01/2006'
3) WHERE Year(OrderDate) = 2006 AND Month(OrderDate) = 1 and Day(OrderDate)=1
4) WHERE OrderDate LIKE '01/01/2006%'
5) WHERE OrderDate >= '01/01/2006'  AND OrderDate < '01/02/2006'

Is found here

paraselena
  • 251
  • 1
  • 5
  • of all of those, #1 is considered the most efficient: http://www.sqlservercentral.com/Forums/Topic937765-338-1.aspx – RandomUs1r Mar 11 '13 at 17:33
  • 1
    @RandomUs1r option #1 needs to run the `datediff` calculation for every row in the table, it certainly won't be the quickest or most efficient. Same for option #2 with the `varchar(20)` conversion. Options #4 and #5 are more promising, and I'd add two more: 6) `where OrderDate between @dateVar and dateadd(day, 1, @dateVar)` and 7) `WHERE convert(date, OrderDate) = @dateVar` – Keith Mar 12 '13 at 09:41
  • 3
    Of these 5 suggested methods 1-4 are all really slow. 5 is quick for heaped tables and the quickest method (`convert(date, OrderDate)` see the selected answer) is not even in the list. – Keith Mar 12 '13 at 12:08
0

You could add a calculated column that includes only the date without the time. Between the two options, I'd go with the BETWEEN operator because it's 'cleaner' to me and should make better use of indexes. Comparing execution plans would seem to indicate that BETWEEN would be faster; however, in actual testing they performed the same.

Michael L.
  • 620
  • 3
  • 17
  • 1
    It turns out that [`between` is a bad idea here](https://sqlblog.org/2011/10/19/what-do-between-and-the-devil-have-in-common). – Keith Mar 12 '13 at 12:04
  • 1
    @Keith +1 Thanks for the link. I was aware of most of the issues (I knew it was inclusive and I habitually add a time value to the end date when needed), but the last few were new to me. At work all of the time values we select on are truncated to either the minute or the second (no, that was not my decision. yes, the lack of precision is annoying.) so the nano-second issues haven't come up yet, but they're potentially serious enough I'll probably bring up changing use of `BETWEEN` to `=>...<` for discussion with the other developers today. – Michael L. Mar 12 '13 at 12:26
0

Get items when the date is between fromdate and toDate.

where convert(date, fromdate, 103 ) <= '2016-07-26' and convert(date, toDate, 103) >= '2016-07-26'

gaze
  • 1
  • 3
    Hi @gaze, welcome to Stack Overflow. Your answer doesn't really add anything here - if you read the accepted answer you'll see that they've already covered `convert` and explained whether changing the type or using is better based on indexes, which is really what I was asking for. To get up-votes you really need to either add something others haven't, or answer first. – Keith Jul 26 '16 at 06:57