Is it possible to write a Microsoft SQL query that will group by a datetime
data-type but ignoring the time part such as the hour and minute?

- 272,866
- 37
- 466
- 490

- 211
- 3
- 5
-
2What version of SQL Server? It does matter for this particular question. (it is *possible* in all versions of SQL Server, but there is a better solution in 2008+) – RBarryYoung Sep 13 '11 at 22:38
-
possible duplicate of [How to truncate a datetime in SQL Server](http://stackoverflow.com/questions/923295/how-to-truncate-a-datetime-in-sql-server) and [many others](http://www.google.com/cse?cx=018205968162215846785%3A7n6ajnwyz-i&ie=UTF-8&q=sql+server+truncate+%28datetime|time%29&sa=Search&siteurl=www.google.com%2Fcse%2Fhome%3Fcx%3D018205968162215846785%3A7n6ajnwyz-i) – Andriy M Sep 13 '11 at 23:11
-
1@AndriyM - Grouping by the `date` part of a `datetime` does present a specific optimisation problem beyond just truncating the date part though. e.g. compare the plans for `CREATE TABLE T1(D DATETIME2 PRIMARY KEY);CREATE TABLE T2(D DATE, T TIME, PRIMARY KEY(D,T));SELECT COUNT(*) FROM T1 GROUP BY CAST(D AS DATE) OPTION (ORDER GROUP);SELECT COUNT(*) FROM T2 GROUP BY D OPTION (ORDER GROUP)` – Martin Smith Oct 14 '13 at 14:00
-
@MartinSmith: Ah, good point! Such a good example of over-eagerness in spotting an easy duplicate, that close vote of mine! Nevertheless, it is possible that I wouldn't have recognised the peculiarity you've just pointed out had I trodden on this question today, so thank you for marking the difference for me (and indeed for everyone else). – Andriy M Oct 14 '13 at 14:37
2 Answers
If you are on SQL Server 2008 this is simple.
GROUP BY CAST(YourCol AS Date)
For previous versions you can use
GROUP BY DATEDIFF(DAY, 0, YourCol)
However neither of these will be able to leverage the fact that an index on YourCol
that is ordered by datetime
will also be ordered by date
and thus use a stream aggregate without a sort operation.
On SQL Server 2008+ you might consider indexing (date,time)
rather than datetime
to facilitate this type of query.
Either by simply storing it as two separate components and possibly providing a calculated column that recombines the parts (a datetime2
is stored the same as a date
and a time
so this won't consume any more space except for if the additional column pushes the NULL_BITMAP
onto a new byte.).
CREATE TABLE T
(
YourDateCol date,
YourTimeCol time,
YourDateTimeCol AS DATEADD(day,
DATEDIFF(DAY,0,YourDateCol),
CAST(YourTimeCol AS DATETIME2(7)))
/*Other Columns*/
)
Or alternatively you could store it combined in the base table and have the indexes use calculated columns that split it out.
An example of this approach
CREATE TABLE T
(
DT DATETIME2,
D AS CAST(DT AS DATE),
T AS CAST(DT AS TIME)
)
CREATE INDEX IX1 ON T(DT);
SELECT COUNT(*),
CAST(DT AS DATE)
FROM T
GROUP BY CAST(DT AS DATE)
CREATE INDEX IX2 ON T(D,T);
SELECT COUNT(*),
CAST(DT AS DATE)
FROM T
GROUP BY CAST(DT AS DATE)
DROP TABLE T

- 438,706
- 87
- 741
- 845
-
3`GROUP BY DATEDIFF(DAY, 0, YourCol)` would probably also be enough in this case... – Tomalak Sep 13 '11 at 22:44
-
@Tomalak - Good point. That will return an `int` that is sufficient to `group by` and no reason to convert back to `datetime` except for display purposes. – Martin Smith Sep 13 '11 at 22:45
If you have a large table and want grouping to happen fast, you cannot rely grouping on an expression.
Create an additional datetime column on your table and have it filled by a trigger that calculates the base date from you "real" date:
UPDATE
MyTable
SET
EffectiveDate = DATEADD(DAY, 0, DATEDIFF(DAY, 0, t.RecordDate))
FROM
MyTable t
INNER JOIN inserted i ON i.RowID = t.RowID
Then put an index on EffectiveDate
and grouping (and searching) will be a lot faster.

- 332,285
- 67
- 532
- 628
-
For SQL Server 2008 casting by date [is sargable](http://connect.microsoft.com/SQLServer/feedback/details/526431/make-more-functions-sargable) though I've not checked whether this gets leveraged in `group by` queries. – Martin Smith Sep 13 '11 at 22:56
-
@Martin Interesting, I didn't know that. But it would still trigger a table scan when there is no index on the column, wouldn't it? – Tomalak Sep 13 '11 at 23:10
-
Why a trigger and not a computed column? `EffectiveDate AS CONVERT(DATETIME, DATEADD(DAY, 0, DATEDIFF(DAY, 0, t.RecordDate)))` - this column could be persisted and indexed as well to remove any doubts about sargability, without paying for the overhead of a trigger. – Aaron Bertrand Sep 14 '11 at 03:40
-
@Aaron That's right. SQL Server 2000 does not support indexes on computed columns with datetime values (for some reason date functions are non-deterministic there). This has been fixed in later versions but I still think "trigger" at first. ;) – Tomalak Sep 14 '11 at 06:41