Check hours from one day to another in tsql - sql

I have a table, which has columns to store the start time and end of the service (I do not have date and time, only the day of the week). I have to check if a time is available for the week, considering that a service starts on Monday and ends on Tuesday. Can someone help me?
CREATE TABLE #Calendar (
Name VARCHAR(100),
DayWeek INT,
HourStart TIME,
HourEnd TIME
);
INSERT INTO #Calendar (Name, DayWeek, HourStart, HourEnd)
VALUES ('Make a Cake', 1, '19:00:00', '07:00:00');
INSERT INTO #Calendar (Name, DayWeek, HourStart, HourEnd)
VALUES ('Make a cookie', 1, '07:00:00', '19:00:00');
INSERT INTO #Calendar (Name, DayWeek, HourStart, HourEnd)
VALUES ('Make a cookie', 2, '19:00:00', '23:00:00');

OK, this must work. It shows every line which has a time conflict; if you uncomment the last where it will show lines without conflict too
declare #test_DateTimeStart datetime=dateadd(day,4,'20:00:00') -- Check from Monday 20:00
declare #test_DateTimeEnd datetime=dateadd(day,5,'20:00:00') -- Check to Tuesday 20:00
select q2.result,c.*
from
#Calendar c
cross apply
( select
dateadd(day,DayWeek,convert(datetime,HourStart)) as DateTimeStart,
dateadd(day,DayWeek+1,convert(datetime,HourEnd)) as DateTimeEnd
)q
cross apply (select
case when exists
(
select 1 from #Calendar c2 where
(#test_DateTimeStart>DateTimeStart and #test_DateTimeStart<DateTimeEnd)
or
(#test_DateTimeEnd >DateTimeStart and #test_DateTimeEnd <DateTimeEnd)
)
then 'Time is taken due to this row -->'
else 'Time is free due to this row -->'
end as result
)q2
--where result='Time is taken due to this row -->'

This will solve it. It will not be able to use index because it uses calculation on columns when comparing:
DECLARE #from time = '22:59', #to time = '23:01'
DECLARE #DayWeek int = 2
;WITH CTE as
(
SELECT
dateadd(d, #DayWeek, cast(#from as datetime)) startdate,
dateadd(d,
CASE WHEN #to < #from
THEN #DayWeek + 1
ELSE #DayWeek
END,
cast(#to as datetime)) enddate
)
SELECT CASE WHEN
not exists
(
SELECT *
FROM #Calendar c
WHERE cte.startdate < dateadd(d, CASE WHEN HourEnd < HourStart
THEN DayWeek + 1 ELSE DayWeek END, cast(HourEnd as datetime))
AND cte.enddate > dateadd(d, DayWeek, cast(HourEnd as datetime))
) THEN 'Available' ELSE 'Not available' END
FROM CTE

Use below code to get the current day of week real time. then compare with your save data
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://www.google.com");
var response = myHttpWebRequest.GetResponse();
string todaysDates = response.Headers["date"];
return DateTime.ParseExact(todaysDates,"ddd, dd MMM yyyy HH:mm:ss'GMT'",CultureInfo.InvariantCulture.DateTimeFormat,DateTimeStyles.AssumeUniversal).DayOfWeek;

Related

Define number of week between two dates starting with start of the period

I have, for example:
create table #test ( job int, dateL date)
insert into #test values
(1, '2022-04-01'),
(2, '2022-04-02'),
(3, '2022-04-12'),
(4, '2022-04-27'),
(5, '2022-05-01')
declare
#startdate date = '2022-04-01',
#enddate date = '2022-04-30'
set #startdateweekstart = (select DATEPART(WEEKDAY, #startdate))
SELECT DATEADD( DAY , #startdateweekstart - DATEPART(WEEKDAY, #startdate), CAST (#startdate AS DATE )) [Week_Start_Date]
Select DATEADD( DAY , #startdateweekstart + 6 - DATEPART(WEEKDAY, #startdate), CAST (#startdate AS DATE )) [Week_End_Date]
So I searched for a solution but could really find one. And not sure I am on the right track
I need to get my output look like this, so I could group it later in ssrs by weeks
job dateL Week
1 '2022-04-01' 1
2 '2022-04-02' 1
3 '2022-04-12' 2
4 '2022-04-27' 4
5 '2022-05-01' 0
where 0 is something that doesn't belong in range between #startdate and #enddate.
Would be grateful for any kick in the right direction! Thanks!
Here is the code:
SELECT job,dateL,
(
SELECT CASE
WHEN dateL BETWEEN #startdate AND #enddate THEN
(SELECT MAX(v) FROM (VALUES (1), (DATEDIFF(wk, #startdate, dateL))) AS VALUE(v))
ELSE 0
END
)
AS Week
FROM #test;

Based on day fetch all dates - sql

I have start date, end date and name of days. How can fetch all dates between those two dates of that specific days in sql?
example data:
start_date:4/11/2018
end_date: 5/11/2018
days: monday, thursday
expected output: all dates between start and end date which comes on monday and thursday and store them in table
updated
my present code(not working)
; WITH CTE(dt)
AS
(
SELECT #P_FROM_DATE
UNION ALL
SELECT DATEADD(dw, 1, dt) FROM CTE
WHERE dt < #P_TO_DATE
)
INSERT INTO Table_name
(
ID
,DATE_TIME
,STATUS
,CREATED_DATE
,CREATED_BY
)
SELECT #P_ID
,(SELECT dt FROM CTE WHERE DATENAME(dw, dt) In ('tuesday','friday',null))
,'NOT SENT'
,CAST(GETDATE() AS DATE)
,#USER_ID
Another approach for generating dates between ranges can be like following query. This will be faster compared to CTE or WHILE loop.
DECLARE #StartDate DATETIME = '2018-04-11'
DECLARE #EndDate DATETIME = '2018-05-15'
SELECT #StartDate + RN AS DATE FROM
(
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))-1 RN
FROM master..[spt_values] T1
) T
WHERE RN <= DATEDIFF(DAY,#StartDate,#EndDate)
AND DATENAME(dw,#StartDate + RN) IN('Monday','Thursday')
Note:
If the row count present in master..[spt_values] is not sufficient for the provided range, you can make a cross join with the same to get a bigger range like following.
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))-1 RN
FROM master..[spt_values] T1
CROSS JOIN master..[spt_values] T2
By this you will be able to generate date between a range with gap of 6436369 days.
You can use a recursive common table expression (CTE) to generate a list of days. With datepart(dw, ...) you can filter for specific days of the week.
An example that creates a list of Mondays and Thursdays between March 1st and today:
create table ListOfDates (dt date);
with cte as
(
select cast('2018-03-01' as date) as dt -- First day of interval
union all
select dateadd(day, 1, dt)
from cte
where dt < getdate() -- Last day of interval
)
insert into ListOfDates
(dt)
select dt
from cte
where datepart(dw, dt) in (2, 5) -- 2=Monday and 5=Thursday
option (maxrecursion 0)
See it working at SQL Fiddle.
This will work for you:
DECLARE #table TABLE(
ID INT IDENTITY(1,1),
Date DATETIME,
Day VARCHAR(50)
)
DECLARE #Days TABLE(
ID INT IDENTITY(1,1),
Day VARCHAR(50)
)
INSERT INTO #Days VALUES ('Monday')
INSERT INTO #Days VALUES ('Thursday')
DECLARE #StartDate DATETIME='2018-01-01';
DECLARE #EndDate DATETIME=GETDATE();
DECLARE #Day VARCHAR(50)='Friday';
DECLARE #TempDate DATETIME=#StartDate;
WHILE CAST(#TempDate AS DATE)<=CAST(#EndDate AS DATE)
BEGIN
IF EXISTS (SELECT 1 FROM #Days WHERE DAY IN (DATENAME(dw,#TempDate)))
BEGIN
INSERT INTO #table
VALUES (
#TempDate, -- Date - datetime
DATENAME(dw,#TempDate) -- Day - varchar(50)
)
END
SET #TempDate=DATEADD(DAY,1,#TempDate)
END
SELECT * FROM #table
INSERT INTO TargetTab(dateCOL)
SELECT dateCOL
FROM tab
WHERE dateCOL >= startdate AND dateCOL <= enddate
AND (DATENAME(dw,dateCOL) ='Thursday' OR DATENAME(dw,dateCOL) = 'Monday')
Try this query to get your result.
Use a recursive CTE to generate your dates, then filter by week day.
SET DATEFIRST 1 -- 1: Monday, 7 Sunday
DECLARE #StartDate DATE = '2018-04-11'
DECLARE #EndDate DATE = '2018-05-15'
DECLARE #WeekDays TABLE (WeekDayNumber INT)
INSERT INTO #WeekDays (
WeekDayNumber)
VALUES
(1), -- Monday
(4) -- Thursday
;WITH GeneratingDates AS
(
SELECT
GeneratedDate = #StartDate,
WeekDay = DATEPART(WEEKDAY, #StartDate)
UNION ALL
SELECT
GeneratedDate = DATEADD(DAY, 1, G.GeneratedDate),
WeekDay = DATEPART(WEEKDAY, DATEADD(DAY, 1, G.GeneratedDate))
FROM
GeneratingDates AS G -- Notice that we are referencing a CTE that we are also declaring
WHERE
G.GeneratedDate < #EndDate
)
SELECT
G.GeneratedDate
FROM
GeneratingDates AS G
INNER JOIN #WeekDays AS W ON G.WeekDay = W.WeekDayNumber
OPTION
(MAXRECURSION 30000)
Try this:
declare #start date = '04-11-2018'
declare #end date = '05-11-2018'
declare #P_ID int = 1
declare #USER_ID int = 11
;with cte as(
select #start [date]
union all
select dateadd(DAY, 1, [date]) from cte
where [date] < #end
)
--if MY_TABLE doesn't exist
select #P_ID,
[date],
'NOT SENT',
cast(getdate() as date),
#USER_ID
into MY_TABLE
from cte
--here you can specify days: 1 - Sunday, 2 - Monday, etc.
where DATEPART(dw,[date]) in (2, 5)
option (maxrecursion 0)
--if MY_TABLE does exist
--insert into MY_TABLE
--select #P_ID,
-- [date],
-- 'NOT SENT',
-- cast(getdate() as date),
-- #USER_ID
--from cte
--where DATEPART(dw,[date]) in (2, 5)
--option (maxrecursion 0)

How to find the total playing time per day for all the users in my sql server database

I have a table which contains following columns
userid,
game,
gameStarttime datetime,
gameEndtime datetime,
startdate datetime,
currentdate datetime
I can retrieve all the playing times but I want to count the total playing time per DAY and 0 or null if game not played on a specific day.
Take a look at DATEDIFF to do the time calculations. Your requirements are not very clear, but it should work for whatever you're looking to do.
Your end result would probably look something like this:
SELECT
userid,
game,
DATEDIFF(SS, gameStarttime, gameEndtime) AS [TotalSeconds]
FROM [source]
GROUP BY
userid,
game
In the example query above, the SS counts the seconds between the 2 dates (assuming both are not null). If you need just minutes, then MI will provide the total minutes. However, I imagine total seconds is best so that you can convert to whatever unit of measure you need accurate, such as hours that might be "1.23" or something like that.
Again, most of this is speculation based on assumptions and what you seem to be looking for. Hope that helps.
MSDN Docs for DATEDIFF: https://msdn.microsoft.com/en-us/library/ms189794.aspx
You may also look up DATEPART if you want the minutes and seconds separately.
UPDATED BASED ON FEEDBACK
The query below breaks out the hour breakdowns by day, splits time across multiple days, and shows "0" for days where no games are played. Also, for your output, I have to assume you have a separate table of users (so you can show users who have no time in your date range).
-- Define start date
DECLARE #BeginDate DATE = '4/21/2015'
-- Create sample data
DECLARE #Usage TABLE (
userid int,
game nvarchar(50),
gameStartTime datetime,
gameEndTime datetime
)
DECLARE #Users TABLE (
userid int
)
INSERT #Users VALUES (1)
INSERT #Usage VALUES
(1, 'sample', '4/25/2015 10pm', '4/26/2015 2:30am'),
(1, 'sample', '4/22/2015 4pm', '4/22/2015 4:30pm')
-- Generate list of days in range
DECLARE #DayCount INT = DATEDIFF(DD, #BeginDate, GETDATE()) + 1
;WITH CTE AS (
SELECT TOP (225) [object_id] FROM sys.all_objects
), [Days] AS (
SELECT TOP (#DayCount)
DATEADD(DD, ROW_NUMBER() OVER (ORDER BY x.[object_id]) - 1, #BeginDate) AS [Day]
FROM CTE x
CROSS JOIN CTE y
ORDER BY
[Day]
)
SELECT
[Days].[Day],
Users.userid,
SUM(COALESCE(CONVERT(MONEY, DATEDIFF(SS, CASE WHEN CONVERT(DATE, Usage.gameStartTime) < [Day] THEN [Day] ELSE Usage.gameStartTime END,
CASE WHEN CONVERT(DATE, Usage.gameEndTime) > [Day] THEN DATEADD(DD, 1, [Days].[Day]) ELSE Usage.gameEndTime END)) / 3600, 0)) AS [Hours]
FROM [Days]
CROSS JOIN #Users Users
LEFT OUTER JOIN #Usage Usage
ON Usage.userid = Users.userid
AND [Days].[Day] BETWEEN CONVERT(DATE, Usage.gameStartTime) AND CONVERT(DATE, Usage.gameEndTime)
GROUP BY
[Days].[Day],
Users.userid
The query above yields the output below for the sample data:
Day userid Hours
---------- ----------- ---------------------
2015-04-21 1 0.00
2015-04-22 1 0.50
2015-04-23 1 0.00
2015-04-24 1 0.00
2015-04-25 1 2.00
2015-04-26 1 2.50
2015-04-27 1 0.00
I've edited my sql on sql fiddle and I think this might get you what you asked for. to me it looks a little more simple then the answer you've accepted.
DECLARE #FromDate datetime, #ToDate datetime
SELECT #Fromdate = MIN(StartDate), #ToDate = MAX(currentDate)
FROM Games
-- This recursive CTE will get you all dates
-- between the first StartDate and the last CurrentDate on your table
;WITH AllDates AS(
SELECT #Fromdate As TheDate
UNION ALL
SELECT TheDate + 1
FROM AllDates
WHERE TheDate + 1 <= #ToDate
)
SELECT UserId,
TheDate,
COALESCE(
SUM(
-- When the game starts and ends in the same date
CASE WHEN DATEDIFF(DAY, GameStartTime, GameEndTime) = 0 THEN
DATEDIFF(HOUR, GameStartTime, GameEndTime)
ELSE
-- when the game starts in the current date
CASE WHEN DATEDIFF(DAY, GameStartTime, TheDate) = 0 THEN
DATEDIFF(HOUR, GameStartTime, DATEADD(Day, 1, TheDate))
ELSE -- meaning the game ends in the current date
DATEDIFF(HOUR, TheDate, GameEndTime)
END
END
),
0) As HoursPerDay
FROM (
SELECT DISTINCT UserId,
TheDate,
CASE
WHEN CAST(GameStartTime as Date) = TheDate
THEN GameStartTime
ELSE NULL
END As GameStartTime, -- return null if no game started that day
CASE
WHEN CAST(GameEndTime as Date) = TheDate
THEN GameEndTime
ELSE NULL
END As GameEndTime -- return null if no game ended that day
FROM Games CROSS APPLY AllDates -- This is where the magic happens :-)
) InnerSelect
GROUP BY UserId, TheDate
ORDER BY UserId, TheDate
OPTION (MAXRECURSION 0)
Play with it your self on sql fiddle.

SQL populate total working days per month minus bank holidays for current financial year

I am after a view which will look like my first attached picture however with right hand column populated and not blank. The logic is as follows:
The data must be for current financial period. Therfore April will be 2011 and March will be 2012 and so on.
The calculation for Days Available for the single months will be:
Total number of working days (Monday-Friday) minus any bank holidays that fall into that particular month, for that particular financial year (Which we have saved in a table - see second image).
Column names for holiday table left to right: holidaytypeid, name, holstart, holend.
Table name: holidaytable
To work out the cumulative months 'Days Available' it will be a case of summing already populated data for the single months. E.g April-May will be April and May's data SUMMED and so on and so forth.
I need the SQL query in perfect format so that this can be pasted straight in and will work (i.e with the correct column names and table names)
Thanks for looking.
DECLARE #StartDate DATETIME, #EndDate DATETIME
SELECT #StartDate = '01/04/2011',
#EndDate = '31/03/2012'
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL)
;WITH DaysCTE ([Date]) AS
( SELECT #StartDate
UNION ALL
SELECT DATEADD(DAY, 1, [Date])
FROM DaysCTE
WHERE [Date] <= #Enddate
)
INSERT INTO #Data
SELECT MIN([Date]),
COUNT(*) [Day]
FROM DaysCTE
LEFT JOIN HolidayTable
ON [Date] BETWEEN HolStart AND HolEnd
WHERE HolidayTypeID IS NULL
AND DATENAME(WEEKDAY, [Date]) NOT IN ('Saturday', 'Sunday')
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date])
OPTION (MAXRECURSION 366)
DECLARE #Date DATETIME
SET #Date = (SELECT MIN(FirstDay) FROM #Data)
SELECT Period,
WorkingDays [Days Available (Minus the Holidays)]
FROM ( SELECT DATENAME(MONTH, Firstday) [Period],
WorkingDays,
0 [SortField],
FirstDay
FROM #Data
UNION
SELECT DATENAME(MONTH, #Date) + ' - ' + DATENAME(MONTH, Firstday),
( SELECT SUM(WorkingDays)
FROM #Data b
WHERE b.FirstDay <= a.FirstDay
) [WorkingDays],
1 [SortField],
FirstDay
FROM #Data a
WHERE FirstDay > #Date
) data
ORDER BY SortField, FirstDay
DROP TABLE #Data
If you do this for more than 1 year you will need to change the line:
OPTION (MAXRECURSION 366)
Otherwise you'll get an error - The number needs to be higher than the number of days you are querying.
EDIT
I have just come accross this old answer of mine and really don't like it, there are so many things that I now consider bad practise, so am going to correct all the issues:
I did not terminate statements with a semi colon properly
Used a recursive CTE to generate a list of dates
Generate a set or sequence without loops – part 1
Generate a set or sequence without loops – part 2
Generate a set or sequence without loops – part 3
Did not include the column list for an insert
Used DATENAME to elimiate weekends, which is language specific, much better to explicitly set DATEFIRST and use DATEPART
Used LEFT JOIN/IS NULL instead of NOT EXISTS to elimiate records from the holiday table. In SQL Server LEFT JOIN/IS NULL is less efficient than NOT EXISTS
These are all minor things, but they are things I would critique (at least in my head if not outloud) when reviewing someone else's query, so can't really not correct my own work! Rewriting the query would give.
SET DATEFIRST 1;
DECLARE #StartDate DATETIME = '20110401',
#EndDate DATETIME = '20120331';
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL);
WITH DaysCTE ([Date]) AS
( SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM sys.all_objects a
)
INSERT INTO #Data (FirstDay, WorkingDays)
SELECT FirstDay = MIN([Date]),
WorkingDays = COUNT(*)
FROM DaysCTE d
WHERE DATEPART(WEEKDAY, [Date]) NOT IN (6, 7)
AND NOT EXISTS
( SELECT 1
FROM dbo.HolidayTable ht
WHERE d.[Date] BETWEEN ht.HolStart AND ht.HolEnd
)
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date]);
DECLARE #Date DATETIME = (SELECT MIN(FirstDay) FROM #Data);
SELECT Period,
[Days Available (Minus the Holidays)] = WorkingDays
FROM ( SELECT DATENAME(MONTH, Firstday) [Period],
WorkingDays,
0 [SortField],
FirstDay
FROM #Data
UNION
SELECT DATENAME(MONTH, #Date) + ' - ' + DATENAME(MONTH, Firstday),
( SELECT SUM(WorkingDays)
FROM #Data b
WHERE b.FirstDay <= a.FirstDay
) [WorkingDays],
1 [SortField],
FirstDay
FROM #Data a
WHERE FirstDay > #Date
) data
ORDER BY SortField, FirstDay;
DROP TABLE #Data;
As a final point, this query becomes much simpler with a calendar table that stores all dates, and has flags for working days, holidays etc, rather than using a holiday table that just stores holidays.
Let me add few cents to this post. Just got assignment to calculate difference between planned hours and actual hour. The code below was converted to a function. So far no issue with the logic:
declare #date datetime = '11/07/2012'
declare #t table (HolidayID int IDENTITY(1,1) primary key,
HolidayYear int,
HolidayName varchar(50),
HolidayDate datetime)
INSERT #t
VALUES(2012, 'New Years Day', '01/02/2012'),
(2012,'Martin Luther King Day', '01/16/2012'),
(2012,'Presidents Day', '02/20/2012'),
(2012,'Memorial Day', '05/28/2012'),
(2012,'Independence Day', '07/04/2012'),
(2012,'Labor Day', '09/03/2012'),
(2012,'Thanksgiving Day', '11/22/2012'),
(2012,'Day After Thanksgiving', '11/23/2012'),
(2012,'Christmas Eve', '12/24/2012'),
(2012,'Christmas Day', '12/25/2012'),
(2013, 'New Years Day', '01/01/2013'),
(2013,'Martin Luther King Day', '01/21/2013'),
(2013,'Presidents Day', '02/18/2013'),
(2013,'Good Friday', '03/29/2013'),
(2013,'Memorial Day', '05/27/2013'),
(2013,'Independence Day', '07/04/2013'),
(2013,'Day After Independence Day', '07/05/2013'),
(2013,'Labor Day', '09/02/2013'),
(2013,'Thanksgiving Day', '11/28/2013'),
(2013,'Day After Thanksgiving', '11/29/2013'),
(2013,'Christmas Eve', NULL),
(2013,'Christmas Day', '12/25/2013')
DECLARE #START_DATE DATETIME,
#END_DATE DATETIME,
#Days int
SELECT #START_DATE = DATEADD(MONTH, DATEDIFF(MONTH, 0, #date), 0)
SELECT #END_DATE = DATEADD(month, 1,#START_DATE)
;WITH CTE AS
(
SELECT DATEADD(DAY, number, (DATEADD(MONTH, DATEDIFF(MONTH, 0, #date), 0) )) CDate
FROM master.dbo.spt_values where type = 'p' and number between 0 and 365
EXCEPT
SELECT HolidayDate FROM #t WHERE HolidayYear = YEAR(#START_DATE)
)
SELECT #Days = COUNT(CDate) --, datepart(dw, CDate) WDay
FROM CTE
WHERE (CDate >=#START_DATE and CDate < #END_DATE) AND DATEPART(dw, CDate) NOT IN(1,7)
SELECT #Days

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.