I have a typical log table with many details and a datetime of when the record was created.
I'm trying to analyse how many times does an event occurs per a time period (each 30 mins). I need to be able to analyze for a period bigger than one day. Basically, my desired output would be something like:
Period | Total
Day 1 00:00 - 00:30 | 23
Day 1 00:30 - 01:00 | 0
Day 1 01:00 - 01:30 | 534
...
Day 2 23:00 - 23:30 | 23
I'm flexible on the date column layout - I just need to be "readable"!
Here's my try but it's not working very well
declare #startdatetime datetime = '2016-02-03 00:00:00'
declare #enddatetime datetime = '2016-02-19 23:59:59'
declare #apiserviceid int = 21
select DATEPART(MINUTE, usr.STARTDATETIME) % 30, COUNT(*)
from TABLE 1 usr
where usr.APIREQUESTID = #apiserviceid
and usr.STARTDATETIME >= #startdatetime and usr.STARTDATETIME <= #enddatetime
group by
(DATEPART(MINUTE, usr.STARTDATETIME) % 30)
order by 1;
Thanks :)
DATEPART(MINUTE,...) are just the minutes within one hour, not the minutes within one day. Consider the hours as well
select (60 * DATEPART(HOUR, usr.STARTDATETIME) +
DATEPART(MINUTE, usr.STARTDATETIME)) % 30, COUNT(*)
...
I would recommend using a recursive CTE to generate all of your 30 min time segments and then JOIN on your table to find the occurrences.
DECLARE #startdate DATETIME = '2016-02-23'--your_starting_date
DECLARE #enddate DATETIME = '2016-03-01' --your_ending_date
;WITH cte AS (
SELECT
#startdate AS start_time
, DATEADD(MINUTE, 30, #startdate) AS end_time
UNION ALL
SELECT DATEADD(MINUTE, 30, start_time) AS start_time
, DATEADD(MINUTE, 30, end_time) AS end_TIME
FROM cte
WHERE end_time <= #enddate
)
SELECT *
INTO #time_table
FROM CTE
OPTION (MAXRECURSION 32727)
GO
SELECT
start_time
, end_time
, SUM(CASE WHEN your_time_column BETWEEN start_time AND end_time THEN 1 ELSE 0 END) AS total_count
FROM #time_table
INNER JOIN your_table --left join if you want all time slots with 0 occurrences
ON your_time_column BETWEEN start_time AND end_time
GROUP BY
start_time
, end_time
This will group by on the hour (not 30 min)
declare #startdatetime datetime = '2016-02-03 00:00:00'
declare #enddatetime datetime = '2016-02-19 23:59:59'
declare #apiserviceid int = 21
select min(usr.STARTDATETIME), max(usr.STARTDATETIME), count(0)
from TABLE 1 usr
where usr.APIREQUESTID = #apiserviceid
and usr.STARTDATETIME >= #startdatetime and usr.STARTDATETIME <= #enddatetime
group by convert(char(13),usr.STARTDATETIME,121)
order by 1;
select cast(year(usr.STARTDATETIME) as varchar)+''
+cast(month(usr.STARTDATETIME) as varchar)+
'-'+cast(day(usr.STARTDATETIME) as varchar)+' '+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':'+
CASE WHEN DATEPART(MINUTE, usr.STARTDATETIME)>30 THEN '31 - '+
CAST((DATEPART(HOUR, usr.STARTDATETIME)+1) AS VARCHAR) +':00' ELSE '00 -
'+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':30' END , COUNT(*)
from TABLE 1 usr
where usr.APIREQUESTID = #apiserviceid
and usr.STARTDATETIME >= #startdatetime and usr.STARTDATETIME <= #enddatetime
group by
cast(year(usr.STARTDATETIME) as varchar)+'-'+
cast(month(usr.STARTDATETIME) as varchar)+'-'+cast(day(usr.STARTDATETIME) as
varchar)+' '+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':'+
CASE WHEN DATEPART(MINUTE, usr.STARTDATETIME)>30 THEN '31 - '+
CAST((DATEPART(HOUR, usr.STARTDATETIME)+1) AS VARCHAR) +':00' ELSE '00 -
'+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':30' END
order by 1;
Related
I'm trying to write a SQL query (SQL Server) and part of it is determining the number of minutes per hour between two datetimes.
Example: 11/1/2018 09:05 - 11/1/2018 13:15
Hour 09: 55 minutes
Hour 10: 60 minutes
Hour 11: 60 minutes
Hour 12: 60 minutes
Hour 13: 15 minutes
These would then get put into a temp table and grouped by some other data which will then be used to calculate dollar amounts from these minutes.
Is there a way to accomplish something like this via SQL that isn't too slow or laborious?
Thanks!
I think a recursive CTE is possibly the best approach:
with cte as (
select startTime, endTime,
startTime_hour as hourStart,
(case when endTime < dateadd(hour, 1, startTime_hour) then endTime
else dateadd(hour, 1, startTime_hour)
end) as hourEnd
from (select t.*,
dateadd(hour, datediff(hour, 0, startTime), 0) as startTime_hour
from t
) t
union all
select startTime, endTime,
dateadd(hour, 1, hourStart) as hourStart,
(case when endTime < dateadd(hour, 2, hourStart) then endTime
else dateadd(hour, 2, hourStart)
end) as endHour
from cte
where hourEnd < endTime
)
select cte.hourStart,
(case when hourStart > startTime then datediff(minute, hourStart, hourEnd) else datediff(minute, startTime, hourEnd) end) as minutes
from cte
order by hourStart;
Here is a db<>fiddle.
Here is an alternative dynamic solution that you can work with two parameters (start/end dates) only:
create table #temp
([hour] int, [minutes] int)
declare #startTime datetime='11/1/2018 09:05'
declare #EndTime datetime='11/1/2018 13:15'
declare #tempStartTime datetime = #startTime
declare #nextTimeRounded datetime
declare #hourdiff int = DATEDIFF(HOUR,#startTime,#EndTime)
declare #counter int = DATEPART(HH,#startTime)
declare #limit int = #counter + #hourdiff + 1
while #counter < #limit
begin
insert into #temp ([hour]) values (#counter)
set #nextTimeRounded= (dateadd(hour,
1 + datepart(hour, #tempStartTime),
cast(convert(varchar(10),#tempStartTime, 112) as datetime))
)
if #nextTimeRounded > #EndTime
begin
set #nextTimeRounded = #EndTime
end
update #temp
set [minutes] = (case when DATEDIFF(MINUTE,#tempStartTime,#nextTimeRounded)=0 then 60 else DATEDIFF(MINUTE,#tempStartTime,#nextTimeRounded) end)
where [hour] = #counter
set #counter = #counter + 1
set #tempStartTime = DATEADD(MINUTE,DATEDIFF(MINUTE,#tempStartTime,#nextTimeRounded),#tempStartTime);
end
select * from #temp
Sample Data
Below, we pump four time ranges, with associated values, into a table. All time ranges are different, but the first two are 10h 30m apart. The second two are 9h 45m apart.
declare #times table (
startTime time,
endTime time,
val float
);
insert #times values
('2018-10-01 01:00:00', '2018-10-01 10:45:00', 7),
('2018-10-02 01:00:00', '2018-10-02 10:45:00', 8),
('2018-10-01 01:00:00', '2018-10-01 11:30:00', 1),
('2018-10-02 01:00:00', '2018-10-02 11:30:00', 3);
Solution
You can use the 'datediff' function to aggregate as you so desire. Use the modulo operator to convert your minutes into just the minutes that remain over when whole hours are discounted.
select ap.h,
ap.m,
sumVal = sum(val)
from #times
cross apply (select
h = datediff(hour, startTime, endTime),
m = datediff(minute, startTime, endTime) % 60
) ap
group by ap.h,
ap.m
I have sample data here:
ID Val dt
1 Mohan 2017-10-13 13:02:49.493
2 Manasa 2017-10-12 20:02:49.493
3 maren 2017-10-13 18:02:49.493
When I run the statement today at 2 PM, it should give the result set of the last day 6 PM after Data, and today data up to this time and when I run the statement today at 7 PM, it should give today's data after 6 PM.
If I ran it at Today at 2 PM, the result will be like this
ID Val dt
1 Mohan 2017-10-13 13:02:49.493
2 Manasa 2017-10-12 20:02:49.493
If I ran at Today at 7 PM:
ID Val dt
3 maren 2017-10-13 18:02:49.493
Basic thing, if I ran before 6 PM it should give last day data before 6 PM and if i ran after 6 PM it should give today Data after 6 PM. I have tried with DATE DIFF conditions but am not able to justify the result - can any one please suggest?
Table Script
Declare #tab table
(ID INT,Val Varchar(10),dt datetime)
Insert into #tab (ID,val,dt)
values (1,'Mohan','2017-10-13 13:02:49.493'),
(2,'Manasa','2017-10-12 20:02:49.493'),
(3,'maren','2017-10-13 18:02:49.493')
You can achieve this by creating two dates that simulate your day "window", i.e. 6pm yesterday and 6pm today, adjusting the concept of yesterday/today depending on whether the current time is before or after 6pm currently. You then simply select your data where dt is between those two dates (or use <= and > or whatever you need to include or exclude the relevant rows) e.g....
DECLARE #t table (ID int,Val varchar(30),dt datetime2(3))
INSERT #t(ID,Val,dt)
VALUES(1,'Mohan' ,'2017-10-13T19:02:49.493'),
(2,'Manasa','2017-10-12T20:02:49.493'),
(3,'maren' ,'2017-10-13T07:02:49.493');
DECLARE #now datetime2 = dateadd(HOUR,12,sysdatetime());
SELECT *,
#now,
x.Today6pm,
x.Yesterday6pm
FROM #t AS t
CROSS APPLY (VALUES(DATEADD(day, DATEDIFF(day,'19000101',cast(#now AS date))-(CASE WHEN cast(#now AS time) < timefromparts(18,00,00,0,0) THEN 1 ELSE 0 END), CAST(timefromparts(18,00,00,0,0) AS DATETIME2(7))),
DATEADD(day, DATEDIFF(day,'19000101',cast(#now AS date))+(CASE WHEN cast(#now AS time) > timefromparts(18,00,00,0,0) THEN 1 ELSE 0 END), CAST(timefromparts(18,00,00,0,0) AS DATETIME2(7))))) x(Yesterday6pm,Today6pm)
WHERE dt BETWEEN x.Yesterday6pm AND x.Today6pm
The CROSS APPLY here is to simplfy the code otherwise you could put those functions directly in the WHERE predicate.
the variable #now is simply used for testing to ensure the adjustment works rather than waiting until 6:01pm this evening. Obviously in your code just replace #now with sysdatetime().
Note: if your dates are datetime then adding a date and time together "works" but it does not work for datetime2. The above approach will work for both datetime and datetime2 so is more resilient.
DECLARE #T TABLE
(
ID INT,
Val VARCHAR(50) ,
dt DATETIME
)
INSERT INTO #T
VALUES
(1, 'Mohan ', '2017-10-13 13:02:49.493'),
(2, 'Manasa ', '2017-10-12 20:02:49.493'),
(3, 'maren ', '2017-10-13 18:02:49.493')
DECLARE #CURRENT datetime = '13 oct 2017 18:00'
SELECT
*
FROM #T
WHERE
(
CAST(#CURRENT as time) BETWEEN '00:00' AND '18:00' AND
dt BETWEEN DATEADD(day,-1,DATEADD(hh,18,CAST(CAST(#CURRENT as date) as datetime))) AND DATEADD(hh,18,CAST(CAST(#CURRENT as date) as datetime))
) OR
(
CAST(#CURRENT as time) NOT BETWEEN '00:00' AND '18:00' AND
dt >=DATEADD(hh,18,CAST(CAST(#CURRENT as date) as datetime))
)
You can try this script.
SELECT * FROM MyTable WHERE
( (CAST(GETDATE() AS TIME) < '18:00')
AND dt < DATEADD(HOUR,18, CAST(CAST(GETDATE() AS DATE) AS DATETIME) ) )
OR
(( CAST(GETDATE() AS TIME) >= '18:00' )
AND dt >= DATEADD(HOUR,18, CAST(CAST(GETDATE() AS DATE) AS DATETIME) ) )
Try this:
DECLARE #Now DATETIME, #FromDate DATETIME
SET #Now = GETDATE()
SET #FromDate = DATEADD( HOUR, 18, DATEADD( DAY, DATEDIFF( DAY, 0, #Now )
- ( CASE WHEN DATEPART( HOUR, #Now ) < 18 THEN 1 ELSE 0 END ), 0 ))
SELECT #Now, #FromDate
SELECT *
FROM #tab
WHERE #FromDate < dt AND dt <= #Now
Result:
Now FromDate
----------------------- -----------------------
2017-10-13 19:36:40.963 2017-10-13 18:00:00.000
ID Val dt
----------- ---------- -----------------------
3 maren 2017-10-13 18:02:49.493
Explanation:
#FromDate is calculated as follows:
( CASE WHEN DATEPART( HOUR, #Now ) < 18 THEN 1 ELSE 0 END ) - if hour is less than 18 (6PM) then return 1
DATEADD( HOUR, 18, DATEADD( DAY, DATEDIFF( DAY, 0, #Now ) - ... , 0 )) - return "6PM date", which is either yesterday or today depending on the result of the CASE expression above
You could try this:
SELECT * FROM [yourTable] WHERE
(dt between CONCAT(CAST(GETDATE() AS DATE), ' ', '18:00:00.000') AND
CONCAT(CAST(GETDATE() AS DATE), ' ', '23:59:59.999')
AND HOUR(GETDATE()) > 18)
OR
(dt between CONCAT(CAST(dt AS DATE) - INTERVAL 1 DAY, ' ', '18:00:00.000')
AND GETDATE()
AND HOUR(GETDATE()) < 18)
Using SQL Server 2008 I would like to count the amount of minutes that land between 16:30 and 18:00 for a given date range.
So inputs are for example #fromdate = '2017-04-17 17:00:00' and #todate = '2017-04-19 17:00:00'
So for the above example using lamens maths.
Day 1 (17th) : 60
Day 2 (18th) : 90
Day 3 (19th) : 30
Total 180
Day 1 is 60 because there is 60 minutes between the start date 17:00 and 18:00 Day 2 is 90 because the middle date must be the full 90 minutes between 16:30 and 18:00 Day 3 is 30 because there is 30 minutes between 16:30 and 17:00 (the time of the end date) So I am searching for the ammount of minutes between 16:30 and 18:00 for each day if that makes sense.
I would like the total to be the output.
This will work in sql server. We populate our start and end datetimes in a cte. After that, it is as simple as summing the date difference in minutes:
declare #fromdate datetime = convert(datetime,'2017-04-17 17:00:00',20)
declare #todate datetime = convert(datetime,'2017-04-19 17:00:00',20)
declare #startRangeTime time = '16:30';
declare #endRangeTime time = '18:00';
with dates as (
select top (datediff(day, #fromdate, #toDate) + 1)
case when dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) = cast(#fromdate as date)
then #fromdate
else cast(dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) as datetime) + cast(#startRangeTime as datetime)
end as startDt,
case when dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) = cast(#todate as date)
then #todate
else cast(dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) as datetime) + cast(#endRangeTime as datetime)
end as endDt
from sys.all_objects a
cross join sys.all_objects b
)
select sum(datediff(mi, startDt, endDt)) from dates
returns:
180
generating dates without a loop taken from this series on generating a sequence without loops
You cannot subtract 2 strings so do it this way. The sample code to get the time in minutes between 2 dates.
ALTER SESSION SET NLS_LANGUAGE="American"
WITH source as (
SELECT '30-OCT-2016 21:08:34' as A, '30/10/2016 21:06:34' as B
FROM Dual
)
SELECT TO_DATE(A, 'DD-MON-YYYY HH24:MI:SS') as new_a,
TO_DATE(B, 'DD/MM/YYYY HH24:MI:SS') as new_b,
( TO_DATE(A, 'DD-MON-YYYY HH24:MI:SS')
- TO_DATE(B, 'DD/MM/YYYY HH24:MI:SS')
) * 24 * 60 as result
FROM source
Hope this helps...
I’ve a table which contains different time ranges:
Id Start Time End Time Points
1 0:00 3:00 10
2 3:01 6:00 20
3 6:01 23:59 30
Now I need to calculate the points achieved between two date ranges with respect to time specified.
Start date = 11/9/2016 18:17:00 and
End date = 11/10/2016 01:20:00
I need to calculate the sum of points gained between these two dates.
The time of start date that is 18:17 falls under Id 3, whose point is 30. So the calculation will be,
18:17 to 23:59 -> 6 hrs -> 6 * 30 = 180 points
The end time 01:20 falls under Id 1
0:00 to 1:20 -> 2 hrs
(if minute is greater than zero, it is rounded to next hour, ie; 2) -> 2 * 10 = 20 points
So the total points gained will be 200 points.
Taking the time difference, does not help me, if the start and end date difference is greater than one day.
Table Structure:
Id - int,
StartTime - time(7),
EndTime - time(7),
Points - int
How to write a query for this using SQL?
This question was good.
You can as the below:
DECLARE #Tbl TABLE (Id INT, StartTime TIME, EndTime TIME, Points INT)
INSERT INTO #Tbl
VALUES
(1, '0:00', '3:00' , 10),
(2, '3:01', '6:00' , 20),
(3, '6:01', '23:59', 30)
DECLARE #StartDate DATETIME = '2016.11.09 18:17:00'
DECLARE #EndDate DATETIME = '2016.11.10 01:20:00'
;WITH CTE
AS
(
SELECT 1 AS RowId, #StartDate CurrentDate, 0 Point, #StartDate DateVal UNION ALL
SELECT
A.RowId ,
IIF((A.CurrentDate + A.EndTime) > #EndDate, #EndDate, DATEADD(MINUTE, 1, (A.CurrentDate + A.EndTime))) AS CurrentDate,
A.Points,
IIF((A.CurrentDate + A.EndTime) > #EndDate, #EndDate, (A.CurrentDate + A.EndTime)) DateVal
FROM
(
SELECT
C.RowId + 1 AS RowId,
CAST(CAST(CurrentDate AS DATE) AS DATETIME) CurrentDate,
CAST((SELECT T.EndTime FROM #Tbl T WHERE CAST(CurrentDate AS TIME) BETWEEN T.StartTime AND T.EndTime) AS DATETIME) AS EndTime,
(SELECT T.Points FROM #Tbl T WHERE CAST(CurrentDate AS TIME) BETWEEN T.StartTime AND T.EndTime) AS Points,
C.CurrentDate AS TempDate
FROM CTE C
) A
WHERE
A.TempDate <> IIF((A.CurrentDate + A.EndTime) > #EndDate, #EndDate, DATEADD(MINUTE, 1, (A.CurrentDate + A.EndTime)))
), CTE2
AS
(
SELECT
C.RowId ,
C.CurrentDate ,
C.Point ,
C.DateVal,
DATEDIFF(MINUTE, LAG(C.DateVal) OVER (ORDER BY C.RowId), C.DateVal) MinuteOfDateDiff
FROM
CTE C
)
SELECT
SUM(CEILING(C.MinuteOfDateDiff * 1.0 / 60.0) * C.Point)
FROM
CTE2 C
Result: 200
I have a table with a datetime column in it, consider it an event log for simple, analogous purposes.
I want to produce a report detailing the average number of events that occur at each time of day, to 30 min accuracy.
so the logic is,
get just the time component of each date
round the time to the nearest 30 min window (it can be floored, i.e. 00:29 -> 00:00)
count these (grouped by date)
average all these counts over all days
I also don't want to have any time holes in my data, for example, if nothing occurred in the 00:00 - 00:30 range, i want to report a 0, rather than having a missing row.
How can I achieve this?
WITH TestDates (date) AS (
SELECT CONVERT(DATETIME, '2011-11-15 10:00') UNION ALL
SELECT CONVERT(DATETIME, '2011-11-15 11:31') UNION ALL
SELECT CONVERT(DATETIME, '2011-11-16 10:00')
-- CTE to generate 4 million rows with a sequential integer starting at 0
), GeneratedRows (seq) AS (
SELECT ROW_NUMBER() OVER (ORDER BY N1.number) - 1
FROM master..spt_values AS N1
CROSS JOIN master..spt_values AS N2
WHERE N1.name IS NULL
AND N2.name IS NULL
), RoundedTestDates (date) AS (
SELECT CASE
-- Subtract the minute part
WHEN DATEPART(MINUTE, date) < 25 THEN DATEADD(MINUTE, -1 * DATEPART(MINUTE, date), date)
-- Subtract the minute part, then add an hour
WHEN DATEPART(MINUTE, date) >= 45 THEN DATEADD(HOUR, 1, DATEADD(MINUTE, -1 * DATEPART(MINUTE, date), date))
-- Subtract the minute part, then add an half-hour
ELSE DATEADD(MINUTE, 30, DATEADD(MINUTE, -1 * DATEPART(MINUTE, date), date))
END
FROM TestDates
)
SELECT rounded_date = GeneratedPeriod.date
, ocurrences = COUNT(RoundedTestDates.date)
FROM (SELECT DATEADD(MINUTE, 30 * seq, (SELECT MIN(date) FROM RoundedTestDates))
FROM GeneratedRows
) AS GeneratedPeriod (date)
LEFT JOIN RoundedTestDates
ON GeneratedPeriod.date = RoundedTestDates.date
WHERE GeneratedPeriod.date <= (SELECT MAX(date) FROM RoundedTestDates)
GROUP BY GeneratedPeriod.date
ORDER BY 1
Here is the code you need: (tested in sql2008 and works fine!)
-- Table with the 48 30mins periods of the day
CREATE TABLE #Periods
(
Num INT
)
DECLARE #idt INT
SET #idt = 1
WHILE (#idt <= 48)
BEGIN
INSERT INTO #Periods VALUES (#idt)
SET #idt = #idt + 1
END
--Average of the count for each period on all days.
SELECT DayTable.Num, AVG(CAST(DayTable.DayCount AS DECIMAL))
FROM
( --Total incidents for each interval on each day.
SELECT CAST(FLOOR(CAST(#MyLog.LogDate AS FLOAT)) AS DATETIME) AS DayWithOutTime,
#Periods.Num AS Num,
COUNT(#MyLog.ID) AS DayCount
FROM #Periods LEFT JOIN #MyLog
ON #Periods.Num = (DATEPART(hh, #MyLog.LogDate)*60 + DATEPART(mi,#MyLog.LogDate))/30
GROUP BY CAST(FLOOR(CAST(#MyLog.LogDate AS FLOAT)) AS DATETIME),
#Periods.Num
) AS DayTable
GROUP BY DayTable.Num
DROP TABLE #Periods
Where #NyLog is the table where your datetime is. It shows the count of incidences for each 30min period. The Period 1 is 00:00 -> 00:30 and Period 48 is 23:30 -> 24:00.
In sybase sql is something like this, in sql-server you might need to do some changes but not much :)
create procedure Test #startDay varchar(8), #endDay varchar(8)
as
declare #ocurrence int
declare #numberOfDays int
select #numberOfDays = 0
create table #intervals (
interval_hour int,
interval_min_minute int,
interval_max_minute int,
ocurrences int
)
create table #insertions (
hour int,
minute int
)
declare #hour int, #minute int
select #hour = 0
-- create the intervals
while (#hour <> 24)
begin
insert into #intervals values(#hour,0,29,0)
insert into #intervals values(#hour,30,59,0)
select #hour = #hour + 1
end
while(#startDay <> #endDay)
begin
insert into #insertions
select datepart(hh, *yourcolumn*), datepart(mm, *yourcolumn*) from *yourdb..yourtable* where convert(varchar(8), *yourcolumn*, 112) = #startDay
select #startDay = convert(varchar(8), dateadd(dd, 1, convert(datetime, #startDay, 112)), 112)
select #numberOfDays = #numberOfDays + 1
end
declare cursor1 cursor for
select hour, minute from #insertions
open cursor1
fetch cursor1 into #hour, #minute
while (##sqlstatus=0)
begin
update #intervals
set i.ocurrences = i.ocurrences + 1
from #intervals i
where interval_hour = #hour and #minute between interval_min_minute and interval_max_minute
fetch cursor1 into #hour, #minute
end
close cursor1
select interval_hour 'hour', interval_min_minute 'min minute', interval_max_minute 'max minute', ocurrences,
case when ocurrences > 0 then convert(float, ocurrences) / convert(float, #numberOfDays) else 0 end 'ocurrences average' from #intervals
drop table #intervals
drop table #insertions
go
What I've done is use an auxiliary table of numbers (a 1 column table with number 1 to 1 million) and join to it, adding the value of the number with the dateadd function to the midnight of the date.
since you want 30 minute intervals, then you want to use the dateadd(minute, number*30, yourdate) where number <= 48 (since there are 1440 minutes in a day)/30 = 48 intervals. This will create your time intervals.
Then simply count your occurrences that happen in between the time intervals.