问题
In SQL SERVER How do you group by any time based interval?
To save someone time I have come up with this solution, For me it works very well. You can generate any time base then group by any interval. Great for doing time weighted averages. If someone has a better way of doing this I would love to hear from you.
Hours
declare @startdate datetime2
declare @enddate datetime2
declare @interval int
set @startdate = '2017-01-01 00:00:00'
set @enddate = '2017-01-31 00:00:00'
set @interval = 4 --Group by Every 4 hours
;with
ALL_INTERVALS
AS (
SELECT TOP (DATEDIFF(HOUR,@startdate,@enddate))
TIMES = DATEADD(HOUR,CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id])),@startdate),
1 AS VALUE
FROM sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
)
select DATEADD(HOUR,((DATEDIFF(HOUR, @startdate,TIMES)/@interval)*@interval),@startdate) AS TIMES,SUM(VALUE) AS TESTDATA
from ALL_INTERVALS
group by DATEADD(HOUR,((DATEDIFF(HOUR, @startdate,TIMES)/@interval)*@interval),@startdate)
order by DATEADD(HOUR,((DATEDIFF(HOUR, @startdate,TIMES)/@interval)*@interval),@startdate)
Minutes
Note. you can set your interval to 60 to achieve hours, 1440 to achieve days....
declare @startdate datetime2
declare @enddate datetime2
declare @interval int
set @startdate = '2017-01-01 00:00:00'
set @enddate = '2017-01-31 00:00:00'
set @interval = 7
;with
ALL_INTERVALS
AS (
SELECT TOP (DATEDIFF(MINUTE,@startdate,@enddate))
TIMES = DATEADD(MINUTE,CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id])),@startdate),
1 AS VALUE
FROM sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
)
select DATEADD(MINUTE,((DATEDIFF(MINUTE, @startdate,TIMES)/@interval)*@interval),@startdate) AS TIMES,SUM(VALUE) AS TESTDATA
from ALL_INTERVALS
group by DATEADD(MINUTE,((DATEDIFF(MINUTE, @startdate,TIMES)/@interval)*@interval),@startdate)
order by DATEADD(MINUTE,((DATEDIFF(MINUTE, @startdate,TIMES)/@interval)*@interval),@startdate)
回答1:
I think you are over complicating thins.
You can use GROUP BY (DATEDIFF(MINUTE, '2017-01-01', TheDateTime) / 30
for grouping by every 30 minutes. Of course, the date I've chosen is a just a random date. You can choose, if you want, the first (or last) date in your sample data.
And you can also use this technique to get every interval of any time part - just change the keyword MINUTE
to any date part you want to use, and the intreval 30
to any interval you want.
Consider the following sample data:
;WITH CTE AS
(
SELECT CAST('2017-01-01T00:00:00' as datetime) As TheDateTime, 0 as rn
UNION ALL
SELECT DATEADD(MINUTE, 1, TheDateTime), rn + 1
FROM CTE
WHERE rn < 60
)
SELECT TheDateTime, rn INTO #T
FROM CTE
OPTION(MAXRECURSION 0)
#T
now contains the following data:
TheDateTime rn
2017-01-01 00:00:00.000 0
2017-01-01 00:01:00.000 1
2017-01-01 00:02:00.000 2
2017-01-01 00:03:00.000 3
...
2017-01-01 00:59:00.000 59
2017-01-01 01:00:00.000 60
To get the maximum rn grouped by 30 minutes you just need this:
SELECT DATEDIFF(MINUTE, '2017-01-01', TheDateTime) / 30, MAX(rn)
FROM #T
GROUP BY DATEDIFF(MINUTE, '2017-01-01', TheDateTime) / 30
Results:
interval max_rn
0 29
1 59
2 60
来源:https://stackoverflow.com/questions/41908362/how-do-you-group-by-any-time-based-interval