How to calculate different between times in different hours? - sql

I have the time-in and time-out of the employees. I can calculate the total hours out of it. The allow late mins are 30 per month. After crossing 30 mins threshold every minute will be calculated late.
The problem is that our office is divided into 4 times.
9:00 am to 11:00 am
11:15 am to 11:30 am (Break)
01:00 pm to 02:00 pm (Break)
02:00 pm to 05:00 pm
05:15 pm to 06:00 pm (Break)
Now if you come late after 09:00 am, it will be calculated late, if you come after 11:30 again late, same for 02:00 pm and 05:15 pm.
How would I calculate something like and wold contribute to late mins > 30.
For this I have 5 columns TimeIN, TimeOUT , TotalHours, AllowedLateMins adn employeeID.
SELECT DATEDIFF(MINUTE, TimeIN, , TimeOUT) AS TotalHours
But for the different breaks etc I can not calculate.
Update: For now I m doing this.
Select InTime, OutTime, DATEDIFF(Minute, InTime, OutTime) as Diff, Sum(DATEDIFF(Minute, InTime, OutTime))
Over(Order by e_id rows unbounded preceding) as Total
from [UNIS 22Dec].[dbo].[Tb_Compile_Attendance]
where e_id= 1001
Updte:
till now I hve done this.
Select top(1) TotalTimeTobeSpent from MinutesConfig
Declare #TimetoBeSpent int
Set #TimetoBeSpent = (Select top(1) TotalTimeTobeSpent from MinutesConfig)
Select top(1) TotalTimeTobeSpent from MinutesConfig
Declare #FlexiMins int
Set #FlexiMins = (Select top(1) FlexiMins from MinutesConfig)
Select InTime, OutTime, DATEDIFF(Minute, InTime, OutTime) as Diff,
SUM(DATEDIFF(Minute, InTime, OutTime)) Over() TotalHrs
from [UNIS 22Dec].[dbo].[Tb_Compile_Attendance]
Declare #RemFlxMins int
/****** Script for SelectTopNRows command from SSMS ******/
SELECT TOP 1000 [id] 'S.No'
,[e_date] 'Date'
,[e_id] 'Employee ID'
,[e_name] 'EmployeeName'
,[InTime]
,[OutTime]
,[Indate]
,[outdate],
DATEDIFF(Minute, Case when CAST(InTime AS time) < '08:30' then '08:30' else InTime end, Case when CAST(OutTime AS Time) > '18:00' then '18:00' when (CAST(OutTime AS Time) > '10:00' AND CAST(OutTime AS Time) < '10:15') then '10:00' else OutTime end) as 'Difference (Mins)',
#TimetoBeSpent 'TimeToBeSpent (Mins)',
SUM(DATEDIFF(Minute, Case when CAST(InTime AS time) < '08:30' then '08:30' else InTime end, Case when CAST(OutTime AS Time) > '18:00' then '18:00' when (CAST(OutTime AS Time) > '10:00' AND CAST(OutTime AS Time) < '10:15') then '10:00' else OutTime end)) Over() 'ActualTimeSpent (Mins)',
(#TimetoBeSpent - SUM(DATEDIFF(Minute, Case when CAST(InTime AS time) < '08:30' then '08:30' else InTime end,Case when CAST(OutTime AS Time) > '18:00' then '18:00' when (CAST(OutTime AS Time) > '10:00' AND CAST(OutTime AS Time) < '10:15') then '10:00' else OutTime end)) Over()) 'Late (Mins)',
(#FlexiMins - (#TimetoBeSpent - SUM(DATEDIFF(Minute, Case when CAST(InTime AS time) < '08:30' then '08:30' else InTime end,Case when CAST(OutTime AS Time) > '18:00' then '18:00' when (CAST(OutTime AS Time) > '10:00' AND CAST(OutTime AS Time) < '10:15') then '10:00' else OutTime end)) Over())) 'RemainingFlexiMins',
#FlexiMins as AllowedFlexiMins
FROM [Tb_Compile_Attendance]
where InTime is not null and outtime is not null
Output:
S.No Date Employee ID InTime OutTime Indate outdate Difference (Mins) TimeToBeSpent (Mins) ActualTimeSpent (Mins) Late (Mins) RemainingFlexiMins AllowedFlexiMins
145 20190721 1001 08:30 10:01 20190721 20190721 90 480 476 4 86 90
164 20190721 1001 10:16 13:00 20190721 20190721 164 480 476 4 86 90
165 20190721 1001 14:03 16:15 20190721 20190721 132 480 476 4 86 90
166 20190721 1001 16:30 18:01 20190721 20190721 90 480 476 4 86 90

I created a table with work periods (the periods when a worker MUST be at work, without breaks). Here's my version of periods, change these data as you need:
create table dbo.WorkTimePeriods
(
FromTime time not null,
TillTime time not null,
constraint pk_dbo_worktimeperiods primary key (FromTime, TillTime),
)
go
insert dbo.WorkTimePeriods
(FromTime,TillTime)
values (cast('09:00:00 am' as time),cast('11:00:00 am' as time)),
(cast('11:30:00 am' as time),cast('01:00:00 pm' as time)),
(cast('02:00:00 pm' as time),cast('06:00:00 pm' as time))
So, the resulting query looks like this (with my sample data for Employees visiting):
;with cte as
(
select *
from (
values (1, cast('20190101 08:59:00' as datetime), cast('20190101 11:00:53' as datetime)), -- this guy is normal one
(1, cast('20190101 11:29:50' as datetime), cast('20190101 13:05:00' as datetime)),
(1, cast('20190101 13:58:31' as datetime), cast('20190101 18:05:10' as datetime)),
(2, cast('20190101 08:59:00' as datetime), cast('20190101 18:00:53' as datetime)), -- this guy works without breaks - workaholic!
(3, cast('20190101 09:09:11' as datetime), cast('20190101 12:59:20' as datetime)), -- this guy was late and went after the afternoon - lazy one!
(4, cast('20190101 09:00:55' as datetime), cast('20190101 11:02:30' as datetime)), -- this guy is a normal one as well, but he is a bit late sometimes
(4, cast('20190101 11:28:22' as datetime), cast('20190101 13:05:10' as datetime)),
(4, cast('20190101 13:55:09' as datetime), cast('20190101 18:01:12' as datetime))
) as EmplWT (EmployeeId, InTime, OutTime)
),
cte2 as
(
select cast('20190101' as date) as CalendarDate
)
select v1.EmployeeId,
v1.CalendarDate,
sum(v1.PastDueTimeInSec/60.0) as PastDueTimeInMin,
sum((v1.WorkTimeInSec-v1.PastDueTimeInSec) / 60.0) as WorkTimeInMin
from (
select v3.EmployeeId,
t2.CalendarDate,
case when v4.EmployeeId is not null then 0
when v5.EmployeeId is null then datediff(SECOND, cast(t1.FromTime as datetime), cast(t1.TillTime as datetime))
else
case when v5.InTime > cast(t1.FromTime as datetime) + cast(t2.CalendarDate as datetime) then datediff(SECOND, cast(t1.FromTime as datetime) + cast(t2.CalendarDate as datetime), v5.InTime) else 0 end +
case when v5.OutTime < cast(t1.TillTime as datetime) + cast(t2.CalendarDate as datetime) then datediff(SECOND, v5.InTime, cast(t1.TillTime as datetime) + cast(t2.CalendarDate as datetime)) else 0 end
end as PastDueTimeInSec,
datediff(SECOND,cast(t1.FromTime as datetime), cast(t1.TillTime as datetime)) as WorkTimeInSec
from dbo.WorkTimePeriods t1
cross join cte2 t2
cross join (
select EmployeeId
from cte
group by EmployeeId
) v3
left join (
select t1.EmployeeId,
t1.InTime,
t1.OutTime
from cte t1
) v4 on cast(t1.FromTime as datetime) + cast(t2.CalendarDate as datetime) between v4.InTime and v4.OutTime
and cast(t1.TillTime as datetime) + cast(t2.CalendarDate as datetime) between v4.InTime and v4.OutTime
and v4.EmployeeId = v3.EmployeeId
left join (
select t1.EmployeeId,
t1.InTime,
t1.OutTime
from cte t1
) v5 on v5.InTime between cast(t1.FromTime as datetime) + cast(t2.CalendarDate as datetime) and cast(t1.TillTime as datetime) + cast(t2.CalendarDate as datetime)
and v5.EmployeeId = v3.EmployeeId
) v1
group by v1.EmployeeId,
v1.CalendarDate
Pay attention, you need to create a Calendar with work dates (in my case it's CTE2).
Result:
+------------+--------------+------------------+---------------+
| EmployeeId | CalendarDate | PastDueTimeInMin | WorkTimeInMin |
+------------+--------------+------------------+---------------+
| 1 | 01.01.2019 | 0.000000 | 450.000000 |
| 2 | 01.01.2019 | 0.000000 | 450.000000 |
| 3 | 01.01.2019 | 339.183333 | 110.816666 |
| 4 | 01.01.2019 | 0.916666 | 449.083333 |
+------------+--------------+------------------+---------------+
I hope this will help you. Good luck!:)

Related

Using TIME_DIFF with multiple conditions in Google BigQuery

I am trying to calculate the worked hours for specific days in Google BigQuery (SQL).
The pay wage is $10 when you work on a day time but $15 when you work on a night time.
Day time is defined as 6am to 10pm whereas night time is defined as 10pm to 6am.
Employees can work flexibly as they are limousine drivers.
The following is an example of my table:
id
start_at
end_at
date
abc123
04:00:00
07:00:00
2020-01-05
abc123
09:00:00
15:32:00
2020-01-05
abc123
23:00:00
23:35:00
2020-01-05
abc123
23:40:00
23:59:00
2020-01-05
abc123
23:59:00
01:35:00
2020-01-05
abc123
02:02:00
04:35:00
2020-01-06
abc123
05:40:00
06:59:00
2020-01-06
So the actual work hours is calculated by taking the difference between start_at and end_at but the day time and night time conditions are becoming a hassle in my query..
*the date column is based on start_at. Even when you start at 11:59pm and end at the next day 12:05am, the date follows the date of the start_at instead of end_at.
Any ideas? Thanks in advance!
Consider below solution
create temp function night_day_split(start_at time, end_at time, date date) as (array(
select as struct
extract(date from time_point) day,
if(extract(hour from time_point) between 6 and 22, 'day', 'night') day_night,
count(1) minutes
from unnest(generate_timestamp_array(
timestamp(datetime(date, start_at)),
timestamp(datetime(if(start_at < end_at, date, date + 1), end_at)),
interval 1 minute
)) time_point
group by 1, 2
));
select id, day,
sum(if(day_night = 'day', minutes, null)) day_minutes,
sum(if(day_night = 'night', minutes, null)) night_minutes
from yourtable,
unnest(night_day_split(start_at, end_at, date)) v
group by id, day
if applied to sample data in your question - output is
You can try following code :-
with mytable as (
select 'abc123' id, cast( '04:00:00' as time) start_dt, cast( '07:00:00' as time) end_dt, date('2020-01-05' ) date union all
select 'abc123', cast( '09:00:00' as time), cast( '15:32:00' as time), date('2020-01-05') union all
select 'abc123', cast( '23:00:00' as time), cast( '23:35:00' as time), date('2020-01-05' ) union all
select 'abc123', cast('23:40:00' as time), cast( '23:59:00' as time), date('2020-01-05') union all
select 'abc123', cast ('23:59:00' as time), cast( '01:35:00' as time), date('2020-01-05') union all
select 'abc123', cast('02:02:00' as time), cast( '04:35:00' as time), date('2020-01-06') union all
select 'abc123', cast('05:40:00' as time), cast( '06:59:00' as time), date('2020-01-06')
)
select id, date, sum (value) as sal from(
select id, date,
case when start_dt > cast( '06:00:00' as time) and end_dt < cast( '22:00:00' as time) and start_dt < end_dt then (time_diff(end_dt, start_dt, Minute)/60) * 10
when start_dt < cast( '06:00:00' as time) and end_dt < cast( '06:00:00' as time) then (time_diff(end_dt, start_dt, Minute)/60) * 15
when start_dt < cast( '06:00:00' as time) and end_dt < cast( '22:00:00' as time) then (time_diff(cast( '06:00:00' as time), start_dt, Minute)/60) * 15 + (time_diff( end_dt,cast( '06:00:00' as time), Minute)/60) * 10
when start_dt > cast( '22:00:00' as time) and end_dt < cast( '06:00:00' as time) then (time_diff(cast( '23:59:00' as time), start_dt, Minute)/60) * 15 + (time_diff( end_dt,cast( '00:00:00' as time), Minute)/60) * 15
when start_dt > cast( '22:00:00' as time) and end_dt > cast( '22:00:00' as time) then (time_diff(end_dt, start_dt, Minute)/60) * 15
else 0
end as value
from mytable) group by id, date
Output :-
You can further group by on month for monthly salary.

how to convert decimal time to datetime so I can run dateadd calculation on it

I have a MSSQL table where the raw data is formatted as this:
date1 time1
2008-01-20 00:00:00 654
2008-01-20 00:00:00 659
2008-01-20 00:00:00 1759
and I need to join both of them together so I can query for example all date_time that happened in the last 15 hours. what I did was
in the select statement:
combined = CONVERT(VARCHAR(10), Date1, 103) +' ' + (left((replace((CONVERT(dec(7, 2), time1) / 100 ),'.',':')),4) + ':00') ,
This helped me with getting results for
date1 time1 combined1
2008-01-20 00:00:00 654 20/01/2008 6:54:00
2008-01-20 00:00:00 659 20/01/2008 6:59:00
2008-01-20 00:00:00 1759 20/01/2008 17:5:00
I cant change the table data & I cant get the right syntax to convert it fully (including taking in consideration the 24h hour format - 1759 for example)
And in the end I need to be able to do a where statement on the combined1 column to see only the rows that happened in the last 15 hours
DATEADD(hour, - 15, GETDATE())
Thanks in advance
Try This
select date1,time1, DATEADD(MINUTE, time1%100, DATEADD(HOUR, time1/100, convert(varchar(10),date1,101))) as Combined
from Table
Where DATEADD(MINUTE, time1%100, DATEADD(HOUR, time1/100, convert(varchar(10),date1,101)))>(DATEADD(hour,-15,GETDATE()))
Try like this:
DECLARE #date DATETIME = '2008-01-20 00:00:00'
, #Time INT = 654
SELECT DATEADD(MINUTE, #Time%100, DATEADD(HOUR, #Time/100, #date))
;WITH cte
AS (SELECT CAST('2008-01-20 00:00:00' AS DATETIME) AS date1, 654 AS Time1
UNION ALL
SELECT CAST('2008-01-20 00:00:00' AS DATETIME) AS date1, 659 AS Time1
UNION ALL
SELECT CAST('2008-01-20 00:00:00' AS DATETIME) AS date1, 1759 AS Time1
)
SELECT
DATEADD(ms, DATEDIFF(ms, '00:00:00', CAST(FORMAT(Time1, '##:##') AS TIME)), date1) AS [CombinedDateTime]
FROM cte;
--Results to:
CombinedDateTime
2008-01-20 06:54:00.000
2008-01-20 06:59:00.000
2008-01-20 17:59:00.000

know in which interval of dates of 15 minutes is a date SQL SERVER

sql fiddle example
I have this table structure :
CREATE TABLE TIMETABLE
([ID] int, [Name] varchar(50), [StartDate] datetime, [EndDate] datetime)
;
INSERT INTO TIMETABLE
([ID], [Name], [StartDate], [EndDate])
VALUES
(1, 'John', '2017-01-29 16:00:00.000', '2017-01-29 16:12:00.000'),
(2, 'Mario', '2017-01-29 16:17:00.000', '2017-01-29 16:29:00.000'),
(3, 'Kate', '2017-01-15 10:35:00.000', '2017-01-15 10:40:00.000'),
(4, 'Maria', '2017-01-15 10:17:00.000', '2017-01-15 10:27:00.000'),
(5, 'Oliver', '2017-01-15 13:46:00.000', '2017-01-29 14:00:00.000')
;
And The result for this :
select * from TIMETABLE
ID Name StartDate EndDate
1 John 2017-01-29T16:00:00Z 2017-01-29T16:12:00Z
2 Mario 2017-01-29T16:17:00Z 2017-01-29T16:29:00Z
3 Kate 2017-01-15T10:35:00Z 2017-01-15T10:40:00Z
4 Maria 2017-01-15T10:17:00Z 2017-01-15T10:27:00Z
5 Oliver 2017-01-15T13:46:00Z 2017-01-29T14:00:00Z
I want to know with a range from 15 mins in wich range is the date, for example:
ID Name StartDate EndDate HourRangeTime
1 John 2017-01-29T16:00:00Z 2017-01-29T16:12:00Z 16:00
In the example the startdate and the enddate is in the range between 16:00 and 16:12 is in the range 16:00
The result it should be like this:
ID Name StartDate EndDate HourRangeTime
1 John 2017-01-29T16:00:00Z 2017-01-29T16:12:00Z 16:00
2 Mario 2017-01-29T16:17:00Z 2017-01-29T16:29:00Z 16:15
3 Kate 2017-01-15T10:35:00Z 2017-01-15T10:40:00Z 10:30
4 Maria 2017-01-15T10:17:00Z 2017-01-15T10:27:00Z 10:15
5 Oliver 2017-01-15T13:46:00Z 2017-01-29T14:00:00Z 13:45
How can I fill the column HourRangeTime, take dates and see what range does it belong to?
Your seem focused on the StartDate.
A relatively general way to do this is to convert this to minutes and then truncate the minutes to the nearest 15 minutes. Here is code:
select cast(dateadd(minute,
15 * (datediff(minute, 0,
cast(StartDate as time)
) / 15
), 0
) as time)
This returns the result as a time.
You can get difference and process future.
SELECT StartTime, EndTime, DATEDIFF(MINUTE, StartTime , EndTime) AS MinuteDiff
FROM TIMETABLE
You can try this for your desired output:
SELECT
CONCAT(DATEPART(hh,StartDate), ':',
CASE
WHEN DATEPART(MINUTE,StartDate) BETWEEN 0 AND 14 THEN '00'
WHEN DATEPART(MINUTE,StartDate) BETWEEN 15 AND 29 THEN '15'
WHEN DATEPART(MINUTE,StartDate) BETWEEN 30 AND 44 THEN '30'
WHEN DATEPART(MINUTE,StartDate) BETWEEN 45 AND 59 THEN '45'
ELSE '00'
END) AS HourRangeTime
FROM TIMETABLE
OUTPUT:
HourRangeTime
-------------
16:00
16:15
10:30
10:15
13:45
You can use this.
SELECT *,
CONVERT(VARCHAR,DATEPART(HOUR, [StartDate]))
+ ':'
+ RIGHT(CONVERT(VARCHAR,(DATEPART(MINUTE, [StartDate]) / 15) * 15)+'0',2) HourRangeTime FROM TIMETABLE

Grouping incident counts into 5 minute time segments

Hoping someone can assist with how to modify the following SQL to achieve the result shown in desired output. I am not fluent in TSQL but know enough to get this far.
My objectrive is to count my incident data and group into 15 minutes time segments starting at midnight but also include zero (0) where there is no incident data in a particular time segment.
Curent Query#
;With cte As
(SELECT CONVERT (varchar(5),DATEADD(minute, 15 *
(DATEDIFF(minute, '20000101', I.CreateTimestamp) / 15), '20000101'),108)
AS CreationTime, I.IncidentShortReference AS Ref
FROM Incident I
WHERE i.CreateTimestamp between DATEADD(d,-1,GETDATE()) and GETDATE()
)
SELECT CTE.CreationTime, count(CTE.Ref) As Count
FROM cte CTE
GROUP BY CTE.CreationTime
ORDER BY CTE.CreationTime
My result
CreationTime count
00:15 2
01:00 1
01:15 1
01:30 1
01:45 2
02:00 1
02:15 1
02:30 4
(Truncated)
Desired Output
CreationTime count
00:15 2
00:30 0
00:45 0
01:00 1
01:15 1
01:30 1
01:45 2
02:00 1
02:15 1
02:30 4
02:45 0
03:00 0
(Truncated)
This uses a cte creating a record for every timestamp between midnight yesterday and now, with a count for the number of incidents in each range with sample data:
declare #incident table (CreateTimestamp datetime, IncidentShortReference varchar(5))
insert into #incident values ('4/10/2017 11:11:00', 'test')
insert into #incident values ('4/10/2017 11:12:00', 'test')
insert into #incident values ('4/10/2017 11:21:00', 'test')
insert into #incident values ('4/10/2017 11:31:00', 'test')
insert into #incident values ('4/10/2017 13:31:00', 'test')
DECLARE #dt datetime
SELECT #dt = dateadd(d, datediff(d, 0, getdate()), 0) - 1 -- yesterday at midnight
;with cte as
(
select #dt dt
union all
select DATEADD(minute, 15, dt) as Next15
FROM cte
WHERE DATEADD(minute, 15, dt) < GETDATE()
)
select convert(varchar(5), dt, 108) as CreationTime, (select count(*) FROM #incident WHERE CreateTimestamp >= dt and CreateTimestamp < dateadd(mi, 15, dt)) as count
from cte
Sample output from a random interval:
You could create a time interval CTE table like this
WITH TIME_CTE
AS(
SELECT
CAST('20170411 00:15:00' AS DATETIME) AS TimePeriod
UNION ALL
SELECT
DATEADD(MINUTE, 15, TimePeriod)
FROM TIME_CTE
WHERE
DATEADD(MINUTE, 15, TimePeriod) < CAST('20170411 23:59:00' AS DATETIME)
)
SELECT
LEFT(CONVERT(VARCHAR(10),TimePeriod,108), 5)
FROM TIME_CTE
Then join it with your original query
WITH TIME_CTE
AS(
SELECT
CAST('20170411 00:15:00' AS DATETIME) AS TimePeriod
UNION ALL
SELECT
DATEADD(MINUTE, 15, TimePeriod)
FROM TIME_CTE
WHERE
DATEADD(MINUTE, 15, TimePeriod) < CAST('20170411 23:59:00' AS DATETIME)
),
CTE
AS (
SELECT CONVERT (varchar(5),DATEADD(minute, 15 *
(DATEDIFF(minute, '20000101', I.CreateTimestamp) / 15), '20000101'),108)
AS CreationTime, I.IncidentShortReference AS Ref
FROM Incident I
WHERE i.CreateTimestamp between DATEADD(d,-1,GETDATE()) and GETDATE()
)
SELECT TIME_CTE.TimePeriod, SUM(IIF(CTE.Ref IS NULL, 0, 1)) As Count
FROM TIME_CTE
LEFT JOIN CTE ON CTE.CreationTime = TIME_CTE.TimePeriod
GROUP BY TIME_CTE.TimePeriod
ORDER BY TIME_CTE.TimePeriod

Sum of data between 7AM to 7PM and 7PM to 7AM (next day)

I have following table:
Date Reading1 Reading2
2017-02-15 07:00:00.0000000 33 30
2017-02-15 07:15:00.0000000 32 31
2017-02-15 07:30:00.0000000 32 31
2017-02-15 07:45:00.0000000 33 30
2017-02-15 08:00:00.0000000 33 28
2017-02-15 08:15:00.0000000 32 29
2017-02-15 08:30:00.0000000 32 31
2017-02-15 08:45:00.0000000 34 31
2017-02-15 09:00:00.0000000 34 31
2017-02-15 09:15:00.0000000 34 30
2017-02-15 09:30:00.0000000 31 30
2017-02-15 09:45:00.0000000 32 32
........
2017-02-16 06:15:00.0000000 32 31
2017-02-16 06:30:00.0000000 35 32
2017-02-16 06:45:00.0000000 34 30
2017-02-16 07:00:00.0000000 34 31
I can sum the Reading1 and Reading2 column based on hour or date, but my problem is that I want to sum-up the column between 7AM to 7PM and then 7PM to 7AM of the next day. Any help will be highly appreciable.
'For sum on hour I am using following query'
--Sum on hour
select datepart(hour,Date), SUM(Reading1), SUM(Reading2)
from #LocalTempTable
group by
datepart(hour,Date),
dateadd(d, 0, datediff(d, 0,Date))
For any given day, I would approach this with a couple of BETWEEN subqueries. Something like:
declare #refDate datetime
declare #midPeriod datetime
declare #endPeriod datetime
set #refDate = '2017-02-15 07:00'
set #midPeriod = dateadd(hh, 12, #refDate)
set #endPeriod = dateadd(hh, 24, #refDate)
select #refDate PeriodStart,
(select sum(Reading1) from #LocalTempTable where Date between #refDate and #midPeriod) EarlyReading1,
(select sum(Reading1) from #LocalTempTable where Date between #midPeriod and #endPeriod) LateReading1,
(select sum(Reading2) from #LocalTempTable where Date between #refDate and #midPeriod) EarlyReading2,
(select sum(Reading2) from #LocalTempTable where Date between #midPeriod and #endPeriod) LateReading2
You can use a case statement to group on the hours to get the desired results:
select year(Date) as DateYear,
datepart(dy, Date) as DayOfYear,
case when datepart(hour,Date) >= 7 and datepart(hour, Date) < 19 then '7AM - 7PM' else '7PM - 7AM' end as HourGroup,
SUM(Reading1),
SUM(Reading2)
from #LocalTempTable
group by year(Date) as DateYear,
datepart(dy, Date) as DayOfYear,
case when datepart(hour,Date) >= 7 and datepart(hour, Date) < 19 then '7AM - 7PM' else '7PM - 7AM' end
Here's a full script with some sample data:
CREATE TABLE #Readings ([Date] DateTime, Reading1 int, Reading2 int)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-15 06:45:00', 1, 1)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-15 07:00:00', 2, 2)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-15 07:15:00', 3, 3)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-15 18:45:00', 4, 4)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-15 19:15:00', 5, 5)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-16 06:45:00', 6, 6)
INSERT INTO #Readings ([Date], Reading1, Reading2) VALUES ('2017-02-16 07:00:00', 7, 7)
SELECT
DATEADD(hh, (Half * 12) + 7, ModifiedDay) AS StartPeriod,
SUM(Reading1) AS SumOfReading1,
SUM(Reading2) AS SumOfReading2
FROM (
SELECT
[Date],
CAST(CAST(ModifiedDate AS DATE) AS DATETIME) AS [ModifiedDay],
DATEPART(hh, ModifiedDate) AS [DatePart],
CASE WHEN DATEPART(hh, ModifiedDate) < 12 THEN 0 ELSE 1 END AS Half,
Reading1,
Reading2
FROM (
SELECT
[Date],
DATEADD(hh, -7, [Date]) AS [ModifiedDate],
Reading1,
Reading2
FROM #Readings
) t
) t
GROUP BY DATEADD(hh, (Half * 12) + 7, ModifiedDay), Half
ORDER BY 1, 2
DROP TABLE #Readings
StartPeriod SumOfReading1 SumOfReading2
2017-02-14 19:00:00.000 1 1
2017-02-15 07:00:00.000 9 9
2017-02-15 19:00:00.000 11 11
2017-02-16 07:00:00.000 7 7
Without using a calendar table or cte:
test setup: http://rextester.com/ZENTG4450
select
FromDate = convert(varchar(10)
,min(dateadd(day,(datediff(hour,0,date)-7)/24,0))
,120)
, ThruDate = convert(varchar(10)
,max(dateadd(day,(datediff(hour,0,date)+5)/24,0))
,120)
, Hours = case ((datediff(hour,0,date)+5)/12)%2
when 1
then '7 AM to 7 PM'
else '7 PM to 7 AM'
end
, SumReading1=sum(Reading1)
, SumReading2=sum(Reading2)
from t
group by (datediff(hour,0,date)+5)/12
returns:
+------------+------------+--------------+-------------+-------------+
| FromDate | ThruDate | Hours | SumReading1 | SumReading2 |
+------------+------------+--------------+-------------+-------------+
| 2017-02-15 | 2017-02-15 | 7 AM to 7 PM | 392 | 364 |
| 2017-02-15 | 2017-02-16 | 7 PM to 7 AM | 101 | 93 |
| 2017-02-16 | 2017-02-16 | 7 AM to 7 PM | 34 | 31 |
+------------+------------+--------------+-------------+-------------+
using a calendar table or cte:
test setup: http://rextester.com/QOC88855
declare #fromdate date = '20170201'
declare #thrudate date = '20170228'
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top ((datediff(day, #fromdate, #thrudate)+1)*2)
[FromDate]=dateadd(hour,7+12*((row_number() over (order by (select 1)) -1)%2)
,convert(datetime2(2)
,dateadd(day, (row_number() over (order by (select 1)) -1)/2, #fromdate))
)
, [ThruDate]=dateadd(hour,19+12*((row_number() over (order by (select 1)) -1)%2)
,convert(datetime2(2)
,dateadd(day, (row_number() over (order by (select 1)) -1)/2, #fromdate))
)
from n as deka
cross join n as hecto /* 100 days */
--cross join n as kilo /* 2.73 years */
--cross join n as [tenK] /* 27.3 years */
order by 1
)
select
FromDate=convert(varchar(20),FromDate,120)
, ThruDate=convert(varchar(20),ThruDate,120)
, SumReading1=sum(Reading1)
, SumReading2=sum(Reading2)
from dates d
inner join t
on t.date >= d.fromdate
and t.date < d.thrudate
group by d.FromDate, d.ThruDate
order by d.FromDate, d.ThruDate
returns:
+---------------------+---------------------+-------------+-------------+
| FromDate | ThruDate | SumReading1 | SumReading2 |
+---------------------+---------------------+-------------+-------------+
| 2017-02-15 07:00:00 | 2017-02-15 19:00:00 | 392 | 364 |
| 2017-02-15 19:00:00 | 2017-02-16 07:00:00 | 101 | 93 |
| 2017-02-16 07:00:00 | 2017-02-16 19:00:00 | 34 | 31 |
+---------------------+---------------------+-------------+-------------+
Assuming your [Date] column is a DATETIME column, you can do this:(Basically what it does is to group the time range from 7AM-7PM as one and 7PM-7AM as another.
select FORMAT(dateadd(hour,-7,[date]), 'yyyy-MM-dd') + case when DATEPART(hour,dateadd(hour,-7,[date])) between 0 and 11 then ' 7AM-7PM' ELSE ' 7PM-7AM' END as [TimeRange], SUM(Reading1), SUM(Reading2)
from #LocalTempTable
group by FORMAT(dateadd(hour,-7,[date]), 'yyyy-MM-dd') + case when DATEPART(hour,dateadd(hour,-7,[date])) between 0 and 11 then ' 7AM-7PM' ELSE ' 7PM-7AM' END
Assuming:
We need to SUM data for each day (not calculate total sum for all days)
We consider accuracy to minute, so 7AM = 420 minutes (from 0:00 AM) and 7PM = 1140 minutes
We split day to 2 group: group 1 > 7AM today and < 7PM today, group 2 >= 7PM today and <= 7AM tomorrow (E.G 20170228 will have 2 group:
20170228_1 and 20170228_2)
Then you could use this:
SELECT
CASE WHEN DATEPART(hh, date)*60 + DATEPART(mi, date) <= 420
THEN CONVERT(char(8), date - 1, 112) + '_2'
WHEN DATEPART(hh, date)*60 + DATEPART(mi, date) >= 1140
THEN CONVERT(char(8), date, 112) + '_2'
ELSE CONVERT(char(8), date, 112) + '_1'
END AS date_group,
SUM(reading1),
SUM(reading2)
FROM table_name
GROUP BY
CASE WHEN DATEPART(hh, date)*60 + DATEPART(mi, date) <= 420
THEN CONVERT(char(8), date - 1, 112) + '_2'
WHEN DATEPART(hh, date)*60 + DATEPART(mi, date) >= 1140
THEN CONVERT(char(8), date, 112) + '_2'
ELSE CONVERT(char(8), date, 112) + '_1'
END;