The GROUP BY TIME clause is an enhancement to the SELECT statement that allows a set of aggregate functions to be computed on data grouped in terms of time.
In order to group data in a Query within a given time interval, you can use an algorithm that generates a rounded up date respecting the 45-minute interval. Since records are grouped every 45 minutes, the other selected fields must employ an aggregation function; these functions are AVG, COUNT, MAX, MIN, and SUM.
MySQL INTERVAL() function returns the index of the argument that is more than the first argument. Syntax: INTERVAL(N,N1,N2,N3,...) It returns 0 if 1st number is less than the 2nd number and 1 if 1st number is less than the 3rd number and so on or -1 if 1st number is NULL.
finally done with
GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)
I'm super late to the party, but this doesn't appear in any of the existing answers:
GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', date_column) / 10 * 10, '2000')
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
AS [date_truncated],
COUNT(*) AS [records_in_interval],
AVG(aa.[value]) AS [average_value]
FROM [friib].[dbo].[archive_analog] AS aa
-- WHERE aa.[date] > '1900-01-01'
GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
ORDER BY [date_truncated]
The MINUTE
and 10
terms can be changed to any DATEPART
and integer,1 respectively, to group into different time intervals.
e.g. 10
with MINUTE
is ten minute intervals; 6
with HOUR
is
six hour intervals.
If you change the interval a lot, you might benefit from DECLARE
ing it as a variable.
DECLARE @interval int = 10;
…
GROUP BY DATEADD(MINUTE, … / @interval * @interval, '2000')
It is a DATETIME
value, which means:
SELECT
statement will give your output a column that has pretty output truncated at the level you specify.The truncating integer division (a FLOOR
shortcut) makes the date output shown in a SELECT
the beginning of each time interval. If you want the middle or end of the interval, you can tweak the division in the second term of DATEADD
with the bold part below:
…) / 10 * 10
+ 10
, '2000')
, credit to Daniel Elkington.…) / 10 * 10
+ (10 / 2.0)
, '2000')
.'2000'
is an "anchor date" around which SQL will perform the date math. Most sample code uses 0
for the anchor, but JereonH discovered that you encounter an integer overflow when grouping more-recent dates by seconds or milliseconds.2
If your data spans centuries,3 using a single anchor date in the GROUP BY
for seconds or milliseconds will still encounter the overflow. For those queries, you can ask each row to anchor the binning comparison to its own date's midnight:
Use DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0)
instead of '2000'
wherever it appears above. Your query will be totally unreadable, but it will work.
An alternative might be CONVERT(DATETIME, CONVERT(DATE, aa.[date]))
as the replacement.
1 Technically, you can use any integer that the maximum for that DATEPART
can divide into.4 If you want to group your results into 13-minute bins or 37-hour ones, you may end up with unequally-filled bins or time-shifted results.
2 The math says 232 ≈ 4.29E+9. This means for a DATEPART
of SECOND
, you get 4.3 billion seconds on either side, which works out to "anchor date ± 136 years." Similarly, 232 milliseconds is ≈ 49.7 days.
3 If your data actually spans centuries or millenia and is still accurate to the second or millisecond… congratulations! Whatever you're doing, keep doing it.
4 If you ever wondered why our clocks have a 12 at the top, reflect on how 5 is the only integer from 6 (half of 12) or below that is not a factor of 12. Then note that 5 × 12 = 60. You have lots of choices for bin sizes with hours, minutes, and seconds.
In T-SQL you can:
SELECT [Date]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY [Date], DATEPART(hh, [Date])
or
by minute use DATEPART(mi, [Date])
or
by 10 minutes use DATEPART(mi, [Date]) / 10
(like Timothy suggested)
For a 10 minute interval, you would
GROUP BY (DATEPART(MINUTE, [Date]) / 10)
As was already mentioned by tzup and Pieter888... to do an hour interval, just
GROUP BY DATEPART(HOUR, [Date])
Should be something like
select timeslot, count(*)
from
(
select datepart('hh', date) timeslot
FROM [FRIIB].[dbo].[ArchiveAnalog]
)
group by timeslot
(Not 100% sure about the syntax - I'm more an Oracle kind of guy)
In Oracle:
SELECT timeslot, COUNT(*)
FROM
(
SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot
FROM
(
SELECT l_time FROM mytab
)
) GROUP BY timeslot
The original answer the author gave works pretty well. Just to extend this idea, you can do something like
group by datediff(minute, 0, [Date])/10
which will allow you to group by a longer period then 60 minutes, say 720, which is half a day etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With