SQL how to create detail table from summary table - sql

Now I have a game summary table like this:
And I want to split the "starttime" and "endtime" by hours and to convert it into following detailed look:
It's kind of similar as Unpivot but not exactly the same. How should I write the sql statement? Thanks a tons!!

In PostgreSQL is:
SELECT PlayerID
, generate_series(s.StartTime, s.EndTime - 1) AS StartTimeCol
, generate_series(s.StartTime + 1, s.EndTime) AS EndTimeCol
FROM summaryTable s
In this Link you can see how to add generate_series function to SQL Server 2008.
View in action Here

If you're on SQL Server, you can try the following TSQL block. It uses a cursor to traverse those rows which have more than 1 hour between start and end times, and iterates through, adding the individual "hours" into a #gamesTable table variable.
Once the cursor is done and has populated records into the #gamesTable variable, a SELECT statement against the original table for those rows which have 1 or less hours between start and end times is unioned to all the rows that were stored into #gamesTable.
DECLARE Game_Cursor CURSOR FOR
SELECT *
FROM summaryTable
WHERE EndTime - StartTime > 1
OPEN Game_Cursor;
DECLARE #PlayerID char
DECLARE #StartTime int
DECLARE #EndTime int
DECLARE #TempEndTime int
DECLARE #gamesTable TABLE(PlayerID char, StartTime int, EndTime int)
--Fetch the first row
FETCH NEXT FROM Game_Cursor INTO #PlayerID, #StartTime, #EndTime
WHILE ##FETCH_STATUS = 0
BEGIN
WHILE (#EndTime - #StartTime) > 0
BEGIN
SET #TempEndTime = #StartTime + 1
INSERT INTO #gamesTable
SELECT #PlayerID AS PlayerID, #StartTime AS StartTime, #TempEndTime AS EndTime
SET #StartTime = #StartTime + 1
END;
--Fetch the next row
FETCH NEXT FROM Game_Cursor INTO #PlayerID, #StartTime, #EndTime
END;
--Rid of the cursor
CLOSE Game_Cursor;
DEALLOCATE Game_Cursor;
--Output the results
SELECT * FROM summaryTable WHERE (EndTime - StartTime) <= 1
UNION ALL
SELECT * FROM #gamesTable

In SQL Server 2005+ you can use CTE to get the results:
;with cte (playerid, starttime, endtime) as
(
select playerid, min(starttime) starttime, max(endtime) endtime
from yourtable
group by playerid
union all
select playerid, starttime + 1, endtime
from cte
where starttime < endtime
)
select playerid,
starttime,
case
when starttime + 1 < endtime
then starttime + 1
else endtime
end endtime
from cte
where starttime != endtime
order by playerid, starttime
See SQL Fiddle with Demo

Related

Script to populate timeslot table with 1-hour timeslots

I've table in my schema => TimeSlot, which has several attributes with id, start time, end time.
So the question is: how can you make a script that will make 23/24 records, for which starttime - endtime, for example, 0.00-1.00, 1.00-2.00, etc.
i've tried this with some example, but can not figure out how to code it(not even know if it is correct):
SET NOCOUNT ON
TRUNCATE TABLE TimeSlot
DECLARE #InitHour TIME(0) = '1'
DECLARE #FinitHour TIME(0) = '24'
WHILE #InitHour < #FinitHour
BEGIN
INSERT INTO TimeSlot(
TimeSlotID, StartTime, EndTime
)
SELECT
The simplest solution is to generate a sequence of numbers, you can do this using any table that has the required number of rows and use row_number for an arbitrary number sequence.
with seq as (
select top(24) Row_Number() over (order by (select null))-1 n
from sys.syscomments
)
insert into TimeSlot (StartTime, EndTime)
select Convert(time,Concat(n,':00')) StartTime,
DateAdd(hour, 1, Convert(time,Concat(n,':00'))) EndTime
from seq
The basic answer to your question is that you need to generate the results somehow. There are lots of methods. If you have a table lying around with at least 24 rows, then you can use that. A "native" SQL method uses a recursive CTE>
Then, the TimeSlotID should be generated automatically (typically as an identity column):
WITH CTE as (
SELECT convert(datetime, '01:00:00') as StartTime
UNION ALL
SELECT DATEADD(HOUR, 1, StartTime)
FROM CTE
WHERE StartTime < convert(datetime, '23:00:00')
)
INSERT INTO TimeSlot (StartTime, EndTime)
SELECT StartTime, DATEADD(HOUR, 1, StartTime)
FROM CTE;
Fiddle here: http://sqlfiddle.com/#!18/9bac2/12
Please, check this solution: http://sqlfiddle.com/#!18/4cb97/17
set nocount on
declare #hour int = 0
while #hour <= 23
begin
declare #time datetime =
convert(
datetime,
convert(varchar, #hour) + ':00:00'
)
insert into TimeSlot(StartTime, EndTime)
values(
#time, dateadd(hour, 1, #time)
)
set #hour = #hour + 1
end
select *
from TimeSlot

Eliminate and reduce overlapping data ranges using SQL

i got a dataset in SQL Server Management Studio. The data looks like the following. i have a identifier for each people userID, date of the record, start timestartime and finish time endtime.
UserID date startime endtime
1 20110203 09:30 09:35
1 20110203 09:31 09:38
1 20110203 10:03 10:05
1 20110203 10:04:00 10:35:00
2 20110203 11:02 11:05
For each people, i want check if there is any overlapping time. If there is, I want to keep the smallest startime and largest endtime. if no overlapping time, I keep the original data. In addition, I want to calculate the duration of maxi endtime and smallest startime.
The result I want should looks like the following. Can anyone teach me how to code this please.
UserID date startime endtime diff
1 20110203 09:30 09:38 00:08
1 20110203 10:03 10:35 00:02
2 20110203 11:02 11:05 00:03
It seems that SELECT with CTE needs to recursively merge undetermined number of rows. In that case I would prefer safe CURSOR based solution:
DECLARE #t TABLE
(
UserId int,
[Date] date,
StartTime time,
EndTime time
);
INSERT INTO #t VALUES
(1, '2011-02-03', '09:30:00', '09:35:00'),
(1, '2011-02-03', '09:31:00', '09:38:00'),
(1, '2011-02-03', '09:36:00', '09:41:00'),
(1, '2011-02-03', '09:40:00', '09:45:00'),
(1, '2011-02-03', '09:42:00', '09:43:00'),
(1, '2011-02-03', '10:03:00', '10:05:00'),
(2, '2011-02-03', '11:02:00', '11:05:00'),
(1, '2011-02-03', '12:00:00', '12:05:00'),
(1, '2011-02-03', '12:04:00', '12:06:00');
------------------
DECLARE #result TABLE
(
UserId int,
[Date] date,
StartTime time,
EndTime time
)
DECLARE cur CURSOR FOR
SELECT UserId, [Date], StartTime, EndTime
FROM #t
ORDER BY UserId, [Date], StartTime;
DECLARE #UserId int
DECLARE #Date date
DECLARE #StartTime time
DECLARE #EndTime time
DECLARE #LastUserId int
DECLARE #LastDate date
DECLARE #LastStartTime time
DECLARE #LastEndTime time
OPEN cur
FETCH NEXT FROM cur INTO #UserId, #Date, #StartTime, #EndTime
SET #LastUserId = #UserId
SET #LastDate = #Date
SET #LastStartTime = #StartTime
SET #LastEndTime = #EndTime
WHILE ##FETCH_STATUS = 0
BEGIN
IF #UserId = #LastUserId AND #Date = #LastDate AND #StartTime <= #LastEndTime
SET #LastEndTime = CASE WHEN #LastEndTime > #EndTime THEN #LastEndTime ELSE #EndTime END
ELSE
BEGIN
INSERT #result(UserId, [Date], StartTime, EndTime) VALUES (#LastUserId, #LastDate, #LastStartTime, #LastEndTime)
SET #LastUserId = #UserId
SET #LastDate = #Date
SET #LastStartTime = #StartTime
SET #LastEndTime = #EndTime
END
FETCH NEXT FROM cur INTO #UserId, #Date, #StartTime, #EndTime
END
INSERT #result(UserId, [Date], StartTime, EndTime) VALUES (#LastUserId, #LastDate, #LastStartTime, #LastEndTime)
CLOSE cur
DEALLOCATE cur
SELECT UserId,
[Date],
StartTime,
EndTime,
CAST(DATEADD(second,DATEDIFF(second,StartTime,EndTime),'2000-01-01') AS time) Diff
FROM #result
which returns
1 2011-02-03 09:30:00.0000000 09:45:00.0000000 00:15:00.0000000
1 2011-02-03 10:03:00.0000000 10:05:00.0000000 00:02:00.0000000
1 2011-02-03 12:00:00.0000000 12:06:00.0000000 00:06:00.0000000
2 2011-02-03 11:02:00.0000000 11:05:00.0000000 00:03:00.0000000
Following a redesigned Version of my previous cte Approach. However, it will still have Problems if there are multiple records for the same user with identical start time... didn't have time to fix that one, but as far as I understood this is not possible in the described process!?
--
-- This part is temporary and has to be replaced by your tables
-- There several more records included now
-- There is still a glitch if the starttime is identical for two records - but as far as I understood, this is not possible in the described case?
--
declare #t table (userid int, date int, starttime time, endtime time);
insert into #t values (1, 20110203, '09:30:00', '09:35:00'), (1, 20110203, '09:31:00', '09:38:00'), (1, 20110203, '09:36:00', '09:41:00'), (1, 20110203, '10:03:00', '10:05:00'),(1, 20110203, '10:04:00', '10:35:00'),
(2, 20110203, '11:02:00', '11:05:00'), (2, 20110203, '11:03:00', '11:20:00'), (2, 20110203, '11:04:00', '11:35:00'), (2, 20110203, '13:02:00', '13:05:00'), (2, 20110203, '13:04:00', '13:15:00');
--
-- First cte: selects all start and endtimes and their - if existing - "overlaps"; recursive cte
--
WITH cte AS(
SELECT 1 lvl, a.userid
,CASE WHEN a.starttime <= ISNULL(b.starttime, a.starttime) THEN a.starttime ELSE b.starttime END AS starttime
,CASE WHEN a.endtime >= ISNULL(b.endtime, a.endtime) THEN a.endtime ELSE b.endtime END AS endtime
FROM #t as a
LEFT OUTER JOIN #t AS b ON b.userid = a.userid
AND b.starttime < a.starttime
AND b.endtime > a.starttime
UNION ALL
select a.lvl+1, a.userid
,CASE WHEN a.starttime <= ISNULL(b.starttime, a.starttime) THEN a.starttime ELSE b.starttime END AS xStart
,CASE WHEN a.endtime >= ISNULL(b.endtime, a.endtime) THEN a.endtime ELSE b.endtime END AS xEnd
from cte as a
INNER JOIN #t AS b ON b.userid = a.userid
AND b.starttime < a.starttime
AND b.endtime > a.starttime
),
--
-- Second cte: get the max. lvl result per user from the recursive cte
--
cteUserMaxLvl AS (
SELECT userid, max(lvl) AS MaxLvl
FROM cte
GROUP BY userid
),
--
-- third cte: get the rows matching the max.lvl; their timespan should cover the desired min. start and max. end
--
cteNoMoreOverlap AS(
SELECT a.userid, starttime, endtime
FROM cte AS a
JOIN cteUserMaxLvl AS b ON a.userid = b.userid AND a.lvl = b.MaxLvl
)
--
-- Select the rows from the "No more overlap" cte
--
SELECT userid, starttime, endtime
FROM cteNoMoreOverlap
UNION ALL
--
-- And finally select all rows, which are not covered by the previously selected timespan
--
SELECT a.userid, min(a.starttime) AS starttime, max(a.endtime) AS endtime
FROM cte AS a
JOIN cteNoMoreOverlap AS b ON a.userid = b.userid AND a.starttime NOT BETWEEN b.starttime AND b.endtime
GROUP BY a.userid
order by userid, starttime, endtime
I believe when you say overlapping time, you are saying within the same hour on the same day. If that is what you mean, following solution might work. Attached is the screenshot of my results.
CREATE TABLE #OverlappingDates
(
UserID INT
, [date] DATE
, starttime VARCHAR(5)
, endtime VARCHAR(5)
);
INSERT INTO #OverlappingDates
( UserID, date, starttime, endtime )
VALUES ( 1 -- UserID - int
, '20110203' -- date - date
, '09:30' -- starttime - time
, '09:35' -- endtime - time
),
( 1 -- UserID - int
, '20110203' -- date - date
, '09:31' -- starttime - time
, '09:38' -- endtime - time
),
( 1 -- UserID - int
, '20110203' -- date - date
, '10:03' -- starttime - time
, '10:05' -- endtime - time
),
( 2 -- UserID - int
, '20110203' -- date - date
, '11:02' -- starttime - time
, '11:05' -- endtime - time
),
( 2 -- UserID - int
, '20110203' -- date - date
, '11:05' -- starttime - time
, '11:15' -- endtime - time
),
( 2 -- UserID - int
, '20110203' -- date - date
, '11:05' -- starttime - time
, '12:00' -- endtime - time
);
WITH cte
AS ( SELECT UserID
, date
, MIN(starttime) AS StartTime
, MAX(endtime) AS EndTime
FROM #OverlappingDates
GROUP BY UserID
, date
, LEFT(starttime, 2)
, LEFT(endtime, 2)
)
SELECT cte.UserID
, cte.date
, cte.StartTime
, cte.EndTime
, ( RIGHT('0'
+ CAST(( DATEDIFF(SECOND,
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ', cte.StartTime) AS DATETIME) ),
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ', cte.EndTime) AS DATETIME) )) )
/ 3600 AS VARCHAR(2)), 2) + ':' + RIGHT('0'
+ CAST(( ( DATEDIFF(SECOND,
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ',
cte.StartTime) AS DATETIME) ),
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ', cte.EndTime) AS DATETIME) )) )
/ 60 ) % 60 AS VARCHAR(2)),
2) ) AS Diff
FROM cte;

Autofill SQL table field with Datetime value based on input parameter

I have a datetime field in my SQL table. I have created a procedure that takes count as a variable and generates records for the table. If the count is 5 it will generate 5 records.The logic i want is that when i provide 5 as an input parameter the datetime field in the table should be autofilled with values
12/20/2015 9:00
12/20/2015 11:00
12/20/2015 13:00
12/20/2015 15:00
12/20/2015 17:00
So every time a record is inserted into a table,the 2 hours of time should be added.
Recursive CTEs are one way to create records on the fly. The key here is to create an anchor (this is the first SELECT inside the CTE, which is your starting point). And an exit check (which is the WHERE clause).
Read up on MAXRECURSION if you want to create more than 100 records at a time.
Example
DECLARE #RecordsRequired INT = 5;
DECLARE #BaseDateTime SMALLDATETIME = GETDATE();
WITH [Sample] AS
(
/* This CTE uses recursion to create the required number of
* records.
*/
SELECT
1 AS RowNumber,
#BaseDateTime AS [DateTime]
UNION ALL
SELECT
RowNumber + 1 AS RowNumber,
DATEADD(HOUR, 2, [DateTime]) AS [DateTime]
FROM
[Sample]
WHERE
RowNumber < #RecordsRequired
)
SELECT
RowNumber,
[DateTime]
FROM
[Sample]
;
You could also look into WHILE blocks.
Use this code:
------------------ INPUT ------------------------
declare #start_date datetime = '01/01/2000 14:00'
declare #loops int = 5
-------------------------------------------------
declare #i int = 0
while (#i < #loops) begin
select dateadd(hour, #i * 2, #start_date)
set #i = #i + 1
end
Try this without LOOP
Declare #count int = 5,
#incrementer int =2 -- in case if you want to change the incrementer
SELECT Dateadd(hh, num * #incrementer, dates)
FROM (SELECT Cast(CONVERT(VARCHAR(20), Dateadd(dd, 1, Getdate()), 111)
+ ' 9:00 AM' AS DATETIME) AS Dates,
num
FROM (VALUES(0),(1),(2),(3),(4),(5)) TC (num)) A
WHERE num <= #count - 1
SQL FIDDLE DEMO
Please find the sample code below, it contains the logic that you needed. Hope it helps!!
--Create a temp table for sample output
CREATE TABLE #temp
(
CreatedDate datetime
)
--Declaring variables
DECLARE #Count int
DECLARE #TimeCounter int
--intializing values
SET #Count=5
SET #TimeCounter=0
WHILE(#Count>0)
BEGIN
--SELECT getdate()+1
insert into #temp(#temp.CreatedDate) Select DATEADD(hour,#TimeCounter,getdate())
SET #TimeCounter=#TimeCounter+2
SET #Count=#Count-1
END
--Final values
SELECT * FROM #temp tmp
--Dropping table
DROP TABLE #temp
This is one of those problems that's best solved with a numbers table / function. Much less code than recursion or loops, usually faster for anything non-trivial and more reusable too.
The core code you want is
CREATE PROCEDURE usp_PopulateAppointments
(
#StartDateTime datetime2(3),
#Records int,
#Interval int = 120 --Time between appointment slots in minutes. Default to 2h if not manually specified.
)
INSERT INTO Appointments
SELECT
DATEADD(m, #Interval * Number, #StartDateTime)
FROM dbo.udfNumbers(0, #Recs)
I've assumed in this a numbers function that takes #StartAt and #NumberResults. I use one derived from Adam's final code in the comments on http://dataeducation.com/you-require-a-numbers-table/ - in my experience it's faster than a real table, and takes less space too.
Create Table dates
(
datetimefield datetime not null
)
go
Create Procedure FillDateTimeField
#insertxrows int
AS
begin
Declare #LastDateTimeInserted as datetime
set #LastDateTimeInserted = (select isnull(max(datetimefield),getdate()) from Dates)
;WITH norows AS (
SELECT 1 as num, Dateadd(hour,2,#LastDateTimeInserted) as FirstRecord
UNION ALL
SELECT num + 1, dateadd(hour,2,firstrecord) FROM
norows
WHERE num < #insertxrows
)
insert into dates
select firstrecord from norows
end

Average per quarter hour

I have a table with these columns:
Id, Method, DateTime, time taken
Ex
1, Done, 2014-06-22 08:18:00.000, 2000
2, Not Done, 2014-06-23 04:15:00.000, 5000
3, Done, 2014-06-23 14:15:00.000, 6000
I want to have a result set as, "average time taken by DONE methods in each 15 min interval between 8AM to 15PM"
Please guide me on how to proceed on this, I am not sure if cursor fits in this req.
You can use a CTE to generate a list of quarters. Then left join to look up the run times per quarter. A group by will allow you to calculate the average.
In SQL Server 2012, the time type is available, and you can:
; with quarters as
(
select cast('08:00' as time) as time
union all
select dateadd(minute, 15, time)
from quarters
where time <= '14:30'
)
select q.time
, avg(rt.time_taken) as avg_time_taken
from quarters q
left join
RunTime rt
on q.time <= cast(rt.dt as time)
and cast(rt.dt as time) < dateadd(minute, 15, q.time)
and method = 'Done'
group by
q.time
Live example at SQL Fiddle.
For SQL Server 2008R2 and earler, you can use integer math instead:
; with quarters as
(
select 8*60 as min
union all
select min + 15
from quarters
where min < 15*60
)
select q.min / 60 as hour
, q.min % 60 as minute
, avg(rt.time_taken) as avg_time_taken
from quarters q
left join
(
select datepart(minute, dt) +
60 * datepart(hour, dt) as min
, time_taken
from RunTime
where method = 'Done'
) rt
on q.min <= rt.min and rt.min < q.min + 15
group by
q.min;
Live example at SQL Fiddle.
I'm not entirely sure if this is what you want, but here ist the code:
CREATE TABLE #Test(
id int IDENTITY(1,1) PRIMARY KEY,
Method nvarchar(50),
[Datetime] datetime,
timeTaken Bigint
)
CREATE TABLE #Result(
[Between] datetime,
[And] datetime,
[Avg] bigint)
INSERT INTO #Test (Method,Datetime,timeTaken)
VALUES(
'Done', '2014-06-22 08:18:00.000', 2000),
('Not Done', '2014-06-23 04:15:00.000', 5000),
('Done', '2014-06-23 14:15:00.000', 6000)
DECLARE #MaxTime datetime,#StartTime datetime,#Next datetime
SELECT #MaxTime = MAX([datetime]),
#StartTime = MIN([datetime])
FROM #TEST
WHILE #StartTime <= #MaxTime
BEGIN
SET #Next = (SELECT Dateadd(MINUTE,15,#StartTime))
INSERT INTO #Result
SELECT #StartTime AS [Between], #Next AS [And],AVG(timeTaken) AS [AVG]
FROM #Test
WHERE [Datetime] Between #StartTime AND #Next
AND Method = 'Done'
SET #StartTime = #Next
END
SELECT * FROM #Result
DROP TABLE #Test
DROP TABLE #Result
You can now set a where to the Select * from #result in which you can say between 8 AM and 3 PM
Please let me know if this is what you want
Etienne

TSQL Averaging over datepart

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.