SQL Sum duration by hour and day - sql

I am trying to write a query that will convert a duration field into seconds and then sum the durations based on what time and day it is.
The duration is the amount of time that the "event" was running during the hour of the end date.
I have this:
Description | Start Date | End Date | Duration
---------------------------------------------------------------------------
ABC | 2015-08-17 10:30:30.000 | 2015-08-17 11:59:59.000 | 0 00:59:59.0
ABC | 2015-08-18 11:00:00.000 | 2015-08-18 11:30:00.000 | 0 00:30:00.0
DEF | 2015-08-17 08:25:00.000 | 2015-08-17 10:30:00.000 | 0 00:30::00.0
ABC | 2015-08-18 11:30:00.000 | 2015-08-18 11:59:59.000 | 0 00:29:59.0
And I'm trying to get something like this:
Description | Date | Hour | Duration
-------------------------------------------
ABC | 2015-08-17 | 11 | 3575
ABC | 2015-08-18 | 11 | 3575
DEF | 2015-08-17 | 10 | 1800
This is the query I have wrote:
SELECT Description,
DATEPART(HOUR, EndDT), SUM(DATEPART(SECOND, CONVERT(TIME, RIGHT(Duration, LEN(Duration) -2))) +
60 * DATEPART(MINUTE, CONVERT(TIME, RIGHT(Duration, LEN(Duration) - 2))) +
3600 * DATEPART(HOUR, CONVERT(TIME, RIGHT(Duration, LEN(Duration) - 2))))
FROM table
GROUP BY Description,
DATEPART(HOUR, EndDT),
DATEADD(d, 0, DATEDIFF(d, 0, EndDT));
This query doesn't seem to be taking days into consideration like I thought it would and I have no idea how fix it.
I am getting something like this:
Description | Hour | Duration
------------------------------
ABC | 11 | 7150
DEF | 10 | 1800
I also realise that I haven't got the date in the select statement at the moment, but that can be added later.

If you GROUP your data by EndDT date part only, you get what you need:
WITH T AS (
SELECT *
FROM (VALUES('ABC', CAST('2015-08-17 10:30:30.000' AS DATETIME), CAST('2015-08-17 11:59:59.000' AS DATETIME), '0 00:59:59.0'),
('ABC', CAST('2015-08-18 11:00:00.000' AS DATETIME), CAST('2015-08-18 11:30:00.000' AS DATETIME), '0 00:30:00.0'),
('DEF', CAST('2015-08-17 08:25:00.000' AS DATETIME), CAST('2015-08-17 10:30:00.000' AS DATETIME), '0 00:30:00.0'),
('ABC', CAST('2015-08-18 11:30:00.000' AS DATETIME), CAST('2015-08-18 11:59:59.000' AS DATETIME), '0 00:29:59.0'))
AS V(Description, StartDT, EndDT, Duration)
)
SELECT Description ,
CAST(EndDT AS DATE),
DATEPART(HOUR, EndDT) ,
SUM(DATEPART(SECOND, CONVERT(TIME, RIGHT(Duration, LEN(Duration) - 2)))
+ 60 * DATEPART(MINUTE,CONVERT(TIME, RIGHT(Duration, LEN(Duration) - 2)))
+ 3600 * DATEPART(HOUR, CONVERT(TIME, RIGHT(Duration, LEN(Duration) - 2))))
FROM T
GROUP BY
Description ,
CAST(EndDT AS DATE),
DATEPART(HOUR, EndDT)
For this i am getting:
ABC 2015-08-17 11 3599
ABC 2015-08-18 11 3599
DEF 2015-08-17 10 1800
You can ignore the CTE that holds data from the example above, just added it to provide a working example.

This gives you how many hours and seconds each event was running for each day.
SELECT Description,
DATEDIFF(s,MIN([Start Date]), MAX([End Date]))/60/60 AS Hour,
DATEDIFF(s,MIN([Start Date]), MAX([End Date])) AS Duration
FROM yourtable
GROUP BY Description, DAY([Start Date])
OUTPUT
Description Hour Duration
ABC 1 5369
DEF 2 7500
ABC 0 3599
SQL Fiddle: http://sqlfiddle.com/#!3/935c7/15/0

Related

Splitting up events that occur over the day boundary

I have a table of events with a start time and an end time, with some events that have a start time before midnight and an end time after midnight. I'd like to produce output that splits up these events at the midnight barrier so they can be counted toward their respective date.
| EVENT_ID | START_TIME | END_TIME |
|----------|-------------------------|-------------------------|
| 1001 | 2021-02-21 14:00:00.000 | 2021-02-21 18:00:00.000 |
| 1002 | 2021-02-21 17:00:00.000 | 2021-02-22 03:00:00.000 |
| 1003 | 2021-02-21 18:00:00.000 | 2021-02-21 22:00:00.000 |
| 1004 | 2021-02-21 22:00:00.000 | 2021-02-22 07:00:00.000 |
The above table could be produced by the query:
SELECT EVENT_ID,
START_TIME,
END_TIME
FROM EVENTS
WHERE START_TIME BETWEEN '2021-02-21 00:00:00.000' AND '2021-02-21 23:59:59.999'
;
My desired output will split up the events that span multiple days at midnight:
| EVENT_ID | START_TIME | END_TIME |
|----------|-------------------------|-------------------------|
| 1001 | 2021-02-21 14:00:00.000 | 2021-02-21 18:00:00.000 |
| 1002 | 2021-02-21 17:00:00.000 | 2021-02-21 23:59:59.999 |
| 1002 | 2021-02-22 00:00:00.000 | 2021-02-22 03:00:00.000 |
| 1003 | 2021-02-21 18:00:00.000 | 2021-02-21 22:00:00.000 |
| 1004 | 2021-02-21 22:00:00.000 | 2021-02-21 23:59:59.999 |
| 1004 | 2021-02-22 00:00:00.000 | 2021-02-22 07:00:00.000 |
Any help would be greatly appreciated. Ideally I'd like to produce this without functions or the creation of new tables.
Note that I'm using SQL Server 2016
Using table of numbers
with t0(n) as (
select n
from (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
) t(n)
),nmbs as(
select row_number() over(order by t1.n) - 1 n
from t0 t1 cross join t0 t2 cross join t0 t3
)
select event_id,
case when n = 0
then start_time
else dateadd(day, n, convert(date, start_time))
end start_time,
case when datediff(day, start_time, end_time) = n
then end_time
else dateadd(second, -1, dateadd(day, n + 1, convert(datetime, convert(date, start_time))))
end as end_time
from Events
cross apply (
select top (datediff(day, start_time, end_time) + 1) n
from nmbs) ns
You can use a recursive CTE for this:
with cte as (
select event_id, start_time,
(case when datediff(day, start_time, end_time) = 0 then end_time
else dateadd(day, 1, convert(date, start_time))
end) as end_time,
end_time as real_end_time
from t
union all
select event_id, end_time,
(case when dateadd(day, 1, convert(date, end_time)) > real_end_time
then real_end_time
else dateadd(day, 1, convert(date, end_time))
end),
real_end_time
from cte
where end_time < real_end_time
)
select *
from cte;
Here is a db<>fiddle.
The following method solves for the case of midnight between START_TIME and END_TIME. The "desired output" above indicates only a single midnight occurs between START_TIME and END_TIME.
IF OBJECT_ID('tempdb..#t') IS NOT NULL DROP TABLE #t
CREATE TABLE #t ( Event_ID INT, START_TIME DATETIME2, END_TIME DATETIME2)
INSERT INTO #t (Event_ID, START_TIME, END_TIME)
VALUES
( 1001, '2021-02-21 14:00:00.000', '2021-02-21 18:00:00.000' )
, ( 1002, '2021-02-21 17:00:00.000', '2021-02-22 03:00:00.000' )
, ( 1003, '2021-02-21 18:00:00.000', '2021-02-21 22:00:00.000' )
, ( 1004, '2021-02-21 22:00:00.000', '2021-02-22 07:00:00.000' )
-- get original data plus midnight after START_TIME
IF OBJECT_ID('tempdb..#stage') IS NOT NULL DROP TABLE #stage
SELECT *
, CONVERT(DATETIME2, CONVERT(DATE, DATEADD(DAY, 1, t.START_TIME))) d
INTO #stage
FROM #t t
-- get all rows
SELECT Event_ID, START_TIME
, CASE WHEN d > END_TIME THEN END_TIME ELSE d END END_TIME
FROM #stage
UNION ALL
-- get rows where midnight occurs between START_TIME and END_TIME
SELECT Event_ID
, CASE WHEN d > END_TIME THEN START_TIME ELSE d END START_TIME
, END_TIME
FROM #stage
WHERE d < END_TIME
ORDER BY Event_ID

Time difference between two times in Hours in SQL Server

I need the time difference between two times in Hours. I have the start time and end time as shown below:
Start time | End Time
-----------+----------
23:00:00 | 19:00:00
23:00:00 | 07:00:00
I need the output for first row as 20, for second row 8.
Try this:
Schema:
create table a(Starttime time,Endtime time)
INSERT INTO a VALUES ('23:00:00','19:00:00')
INSERT INTO a VALUES ('09:00:00','19:00:00')
INSERT INTO a VALUES ('23:00:00','07:00:00')
Query:
select Starttime,Endtime,
CASE WHEN datediff(HOUR,Starttime,Endtime)<0 THEN 24+datediff(HOUR,Starttime,Endtime)
ELSE datediff(HOUR,Starttime,Endtime) END Diff
FROM A
Output:
| Starttime | Endtime | Diff |
|------------------|------------------|------|
| 23:00:00.0000000 | 19:00:00.0000000 | 20 |
| 09:00:00.0000000 | 19:00:00.0000000 | 10 |
| 23:00:00.0000000 | 07:00:00.0000000 | 8 |
Use DATEDIFF:
SELECT
start_time,
end_time,
24 + DATEDIFF(HOUR, start_time, end_time) AS diff_in_hours
FROM yourTable;
Demo
Query as per your requirement, just put your table name at the place of "YourTable"
SELECT Starttime
,Endtime
,CASE
WHEN DATEDIFF(HOUR, Starttime, Endtime) < 0
THEN 24 + DATEDIFF(HOUR, Starttime, Endtime)
ELSE DATEDIFF(HOUR, Starttime, Endtime)
END Time_Difference
FROM YourTable
Use select case
select case when start_time > end_time
then datediff(hour, start_time , dateadd(hh, 24, end_Time))
else datediff(hh, start_time , end_Time) end

Group by data to get count between the datetime range

Let's say I've a table like below
start_time end_time user_name
2019-01-01 00:00:05 2019-01-01 00:05:05 user1
2019-01-01 00:01:35 2019-01-01 00:06:05 user2
2019-01-01 00:02:05 2019-01-01 00:07:05 user3
2019-01-01 00:03:05 2019-01-01 00:08:05 user1
2019-01-01 00:04:05 2019-01-01 00:09:05 user2
My objective is find out how many users were logged in for a MINUTE. Say like below
time active no of users
2019-01-01 00:00:00 1
2019-01-01 00:01:00 2
2019-01-01 00:02:00 3
2019-01-01 00:03:00 3
2019-01-01 00:04:00 3
Now I first tried to round of time for a new column dateadd(mi, datediff(mi, 0, dateadd(s, 30, start_time)), 0). So, I will receive like above table time column
Next I tried to find the count for rounded datetime like below
SELECT
dateadd(mi, datediff(mi, 0, dateadd(s, 30, start_time)), 0) as RoundedDateTime,
(
SELECT count(distinct(user_name))
FROM entrytable sh
WHERE (sh.end_time > dateadd(mi, datediff(mi, 0, dateadd(s, 30, t.start_time)), 0)
and sh.start_time <= dateadd(mi, datediff(mi, 0, dateadd(s, 30, t.start_time)), 0))
) as usercounter
FROM entrytable t
But, above SQL query is running for longer time and goes to not responding mode.
I could not fix the issue. Can someone help?
Thanks in advance!
The most trivial solution is this:
DECLARE #t TABLE (start_time datetime, end_time datetime, user_name varchar(10));
INSERT INTO #t VALUES
('2019-01-01 00:00:05', '2019-01-01 00:05:05', 'user1'),
('2019-01-01 00:01:35', '2019-01-01 00:06:05', 'user2'),
('2019-01-01 00:02:05', '2019-01-01 00:07:05', 'user3'),
('2019-01-01 00:03:05', '2019-01-01 00:08:05', 'user1'),
('2019-01-01 00:04:05', '2019-01-01 00:09:05', 'user2');
SELECT dt AS date_time, SUM(SUM(val)) OVER (ORDER BY dt) AS active_count
FROM (
SELECT start_time, +1 FROM #t UNION ALL
SELECT end_time, -1 FROM #t
) cte1(dt, val)
GROUP BY dt
This will give you the number of active users whenever there was a change (someone logged in or logged out). Result:
| date_time | active_count |
|-------------------------|--------------|
| 2019-01-01 00:00:05.000 | 1 |
| 2019-01-01 00:01:35.000 | 2 |
| 2019-01-01 00:02:05.000 | 3 |
| 2019-01-01 00:03:05.000 | 4 |
| 2019-01-01 00:04:05.000 | 5 |
| 2019-01-01 00:05:05.000 | 4 |
| 2019-01-01 00:06:05.000 | 3 |
| 2019-01-01 00:07:05.000 | 2 |
| 2019-01-01 00:08:05.000 | 1 |
| 2019-01-01 00:09:05.000 | 0 |
Be advised that the result does not contain the "in-between" dates.
This question was originally tagged for SQL Server 2012, so this answer is for SQL Server.
One method is to generate a list of minutes and then:
with minutes as (
select cast('2019-01-01 00:00:00' as datetime) as mm
union all
select dateadd(minute, 1, minute)
from cte
where mm < '2019-01-01 00:00:05'
)
select m.*,
(select count(*)
from entrytable et
where et.start_time <= m.mm and
et.end_time > m.mm
) as num_actives
from minutes m;

how to ignore Minutes and date and time field and only take yyy/mm/dd/hh and count the records that shares the same date and time (hour only)

in the following table I'm trying to compare it to itself by using self join to be able to count TestOrderItemID that share the same createdate excluding minutes and seconds
for example it should count the createdate that shares the same Year/Month/Day/Hour while ignoring the minutes and seconds, the outcome should be like
for the first two record with TestOrderItemID 1 and 2 the count_Oftest_sameDate should equal 2 since we will ignore the seconds and minutes ,
also for the last 3 records 16 and 17 and 5 the count_Oftest_sameDate equals 3 since it ignored the minutes and only took hour into consideration
Construct date with the YYYY-MM-DD-HH portion and use it in partitioning.
SELECT Id
, COUNT(Id) OVER(PARTITION BY DATEADD(HOUR, DATEPART(HOUR, CreateDate), CAST(CAST([CreateDate] AS DATE) AS DATETIME)) ORDER BY DATEADD(HOUR, DATEPART(HOUR, CreateDate), CAST(CAST([CreateDate] AS DATE) AS DATETIME))) AS count_Oftest_sameDate
, CreateDate
FROM Test
ORDER BY CreateDate;
This will create the following output -
| Id | count_Oftest_sameDate | CreateDate |
|----|-----------------------|---------------------|
| 1 | 2 | 15.06.2015 09:42:45 |
| 2 | 2 | 15.06.2015 09:42:49 |
| 15 | 3 | 15.06.2015 11:13:40 |
| 13 | 3 | 15.06.2015 11:13:40 |
| 14 | 3 | 15.06.2015 11:13:40 |
| 16 | 3 | 15.06.2015 15:29:29 |
| 17 | 3 | 15.06.2015 15:29:29 |
| 5 | 3 | 15.06.2015 15:39:17 |
Update:
To show only the count for each individual hour the following query could be used
SELECT COUNT(*) AS count_Oftest_sameDate
, DATEADD(HOUR, DATEPART(HOUR, CreateDate), CAST(CAST([CreateDate] AS DATE) AS DATETIME)) AS [Hour] --, MIN(CreateDate)
FROM Test
GROUP BY DATEADD(HOUR, DATEPART(HOUR, CreateDate), CAST(CAST([CreateDate] AS DATE) AS DATETIME));
This query will produce the following output -
| count_Oftest_sameDate | Hour |
|-----------------------|---------------------|
| 2 | 15.06.2015 09:00:00 |
| 3 | 15.06.2015 11:00:00 |
| 3 | 15.06.2015 15:00:00 |
You can use a window funcction:
select t.*,
count(*) over (partition by cast(createdate as date)) as count_of_same_date
from t;
DATEADD(HOUR,DATEDIFF(HOUR,CAST(GETDATE() AS DATE),GETDATE()),CAST(CAST(GETDATE() AS DATE) AS DATETIME))
In the expression, replace getdate with the datefield you want to perform the calculation on. This will remove the minutes and seconds, but will keep the hours.
2018-01-23 06:55:47.720
becomes
2018-01-23 06:00:00.000
The easiest way to ignore part of date you can use the next trick to get data between 2021-08-01 03:06 and 2021-08-01 03:07
select * from log.barcode_parsing_log where log_date::text like '2021-08-01 03:06%' or log_date::text like '2021-08-01 03:07%' order by log_date asc;
Use convert function , select part of datetime string to select row count, here is sample output
DECLARE #mytable TABLE (Testorderitemid INT, Count_offtest_samedate INT, createddate DATETIME)
INSERT INTO #mytable
--
SELECT 1, 1, DATEADD(HH, - 4, getdate())
UNION
SELECT 2, 1, DATEADD(second, 10, DATEADD(HH, - 4, getdate()))
UNION
SELECT 15, 3, dateadd(MINUTE, - 35, DATEADD(HH, - 3, getdate()))
UNION
SELECT 13, 3, dateadd(MINUTE, - 35, DATEADD(HH, - 3, getdate()))
UNION
SELECT 14, 3, dateadd(MINUTE, - 35, DATEADD(HH, - 3, getdate()))
UNION
SELECT 16, 2, dateadd(MINUTE, - 35, DATEADD(HH, - 1, getdate()))
UNION
SELECT 16, 2, dateadd(MINUTE, - 35, DATEADD(HH, - 1, getdate()))
UNION
SELECT 17, 2, dateadd(MINUTE, - 35, DATEADD(HH, - 1, getdate()))
UNION
SELECT 5, 1, dateadd(MINUTE, - 15, DATEADD(HH, - 1, getdate()))
SELECT Testorderitemid, count(*) OVER (PARTITION BY convert(VARCHAR(13), createddate, 120)) AS Same_hour_count, createddate
FROM #mytable t;

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;