Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group by datetime ignoring time portion

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?

like image 405
Steven Avatar asked Sep 13 '11 22:09

Steven


People also ask

Can I GROUP BY date in SQL?

To group by date part, use the GROUP BY clause and the EXTRACT() function. Pass EXTRACT() the date parts to isolate.

Does datediff work with datetime?

When I need an answer in units other than days, I will use DateDiff. Note that this only applies to the old DATETIME type, not to DATE , TIME , or DATETIME2 . Also, the value returned will be another DATETIME , so you'll need to cast it to get the human-readable number of days between dates.

Can we use order by and GROUP BY at the same time?

Using Group By and Order By Together When combining the Group By and Order By clauses, it is important to bear in mind that, in terms of placement within a SELECT statement: The GROUP BY clause is placed after the WHERE clause. The GROUP BY clause is placed before the ORDER BY clause.

How can compare date without time in SQL?

To compare dates without the time part, don't use the DATEDIFF() or any other function on both sides of the comparison in a WHERE clause. Instead, put CAST() on the parameter and compare using >= and < operators.


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

enter image description here

like image 164
Martin Smith Avatar answered Oct 11 '22 05:10

Martin Smith


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.

like image 45
Tomalak Avatar answered Oct 11 '22 05:10

Tomalak