Calculate number of minutes for specific day from a date range - sql

I have a table that holds vehicle activity data. There is a start date and end date columns in the table. I wish to construct a query that will show the amount of time in minutes that each vehicle is active or working on a particular day. My problem is that the time between the start date and end date may span a number of days.
Example:
dwVehicleIDFK StartDate EndDate Minutes HrsMins
731 18/09/2019 08:00 18/09/2019 13:00 300 05:00
797 18/09/2019 08:00 18/09/2019 12:00 240 04:00
687 17/09/2019 16:00 17/09/2019 21:00 300 05:00
826 17/09/2019 16:00 17/09/2019 21:00 300 05:00
734 18/09/2019 10:00 18/09/2019 15:30 330 05:30
843 18/09/2019 14:00 18/09/2019 18:00 240 04:00
662 18/09/2019 09:00 18/09/2019 14:00 300 05:00
662 17/09/2019 09:00 17/09/2019 14:00 300 05:00
662 16/09/2019 09:00 16/09/2019 14:00 300 05:00
817 18/09/2019 14:00 19/09/2019 08:00 1080 18:00
In the data above the vehicle with the ID of 817 extends over two days. How would I get the query to return the time period for the 18/09 only or up until midnight on the 18/09?
The query to return the data above:
Select
dwVehicleIDFK,
StartDate,
EndDate,
DATEDIFF(MINUTE, Convert(DateTime, StartDate, 103),
Convert(DateTime, EndDate, 103)) as Minutes,
CONVERT(varchar(5), DATEADD(minute, DATEDIFF(minute,
Convert(DateTime, StartDate, 103),
Convert(DateTime, EndDate, 103)), 0), 114) as HrsMins
from
VehHistory

Assume you loaded the data for the specified date #Day and converted the start/end date to datetime in temp table #temp:
Select
dwVehicleIDFK,
StartDate,
EndDate,
DATEDIFF(MINUTE, CASE WHEN StartDate<#Day THEN #Day ELSE StartDate END,
CASE WHEN EndDate>DATEADD(DAY,1,#Day) THEN DATEADD(DAY,1,#Day) ELSE EndDate END) AS UsedMinutes
from #temp;

Related

Exclude overlapped time period

I wish to write a query for below problem.
The problem is, I want to eliminate all overlapping periods, so that I get the total amount of time which is not taken in any other row.
Example:
NAME
Start Date Time
End Date time
Load shed
21-03-2020 12:30
21-03-2020 13:30
Shutdown
21-03-2020 13:00
21-03-2020 14:00
breakdown
21-03-2020 13:10
21-03-2020 14:10
Load shed
24-03-2020 12:30
24-03-2020 13:30
Shutdown
24-03-2020 11:00
24-03-2020 19:00
breakdown
24-03-2020 13:10
24-03-2020 14:10
Now what we have to do is:
Return time period between start date time and end date time but exclude overlapped time.
Expected result will be:
NAME
Start Date Time
End Date time
Time_interval
Load shed
21-03-2020 12:30
21-03-2020 13:30
01:00
Shutdown
21-03-2020 13:30
21-03-2020 14:00
00:30
breakdown
21-03-2020 14:00
21-03-2020 14:10
00:10
Shutdown
24-03-2020 11:00
24-03-2020 19:00
08:00
Now we can see in result,
First row: As it is because it has the lowest start date time in all
overlapped rows.
Second row: 30 minutes already used in first row so
we exclude 30 minutes here and write left time interval.
Third row:
we exclude till time 14:00 because its already used in row 2 so now time
interval has 10 minutes only.
Fourth row: We exclude all rows from
given table because they all overlapped and they are within start date
time 24-3-2020 11:00 and 24:03:2020 19:00 .
Hope you understand the problem.
Thanks in advance.
You can calculate the previous enddt before each row. Then, if that is larger than the start date, use that for the row. And, if the duration of the row is negative, then filter out the row.
The code looks like:
select name, imputed_startdt, enddt, prev_enddt,
convert(time, dateadd(minute, datediff(minute, imputed_startdt, enddt), 0)) as duration
from (select t.*, max(enddt) over (order by startdt rows between unbounded preceding and 1 preceding) as prev_enddt
from t
) t cross apply
(values (case when prev_enddt > startdt then prev_enddt else startdt end)
) v(imputed_startdt)
where prev_enddt < enddt or prev_enddt is null;
Here is a db<>fiddle.

Generate rows with time intervals between 2 dates in Oracle

I have table in which Sunday to Saturdy "Doctor Start" and "End Time" is given.
I want to create time slots of 15 minutes.
On the basis of that, the patient clicks on calendar datetime interval which shows slots that have already been booked.
The following example shows how to split time into slices of 15 minutes. It uses hierarchical query. A little bit of explanation:
line 2: trunc function, applied to a date value, returns "beginning" of that day (at midnight). Adding 15 / (24*60) adds 15 minutes (as there are 24 hours in a day and 60 minutes in an hour). Multiplying 15 by level works as a "loop", i.e. adds 15-by-15-by-15 ... minutes to previous value.
line 4: similar to line 2, but it makes sure that a day (24 hours * 60 minutes) is divided to 15-minutes parts
line 6: start time is trivial
line 7: end time just adds 15 minutes to start_time
line 9: return only time between 10 and 16 hours (you don't have patients at 02:15 AM, right?)
SQL> with fifteen as
2 (select trunc(sysdate) + (level * 15)/(24*60) c_time
3 from dual
4 connect by level <= (24*60) / 15
5 )
6 select to_char(c_time, 'hh24:mi') start_time,
7 to_char(c_time + 15 / (24 * 60), 'hh24:mi') end_time
8 from fifteen
9 where extract(hour from cast (c_time as timestamp)) between 10 and 15;
START_TIME END_TIME
---------- ----------
10:00 10:15
10:15 10:30
10:30 10:45
10:45 11:00
11:00 11:15
11:15 11:30
11:30 11:45
11:45 12:00
12:00 12:15
12:15 12:30
12:30 12:45
12:45 13:00
13:00 13:15
13:15 13:30
13:30 13:45
13:45 14:00
14:00 14:15
14:15 14:30
14:30 14:45
14:45 15:00
15:00 15:15
15:15 15:30
15:30 15:45
15:45 16:00
24 rows selected.
SQL>

SQL Server SUM(Values)

I have the following query that works perfectly well. The query sums the values in a given day.
SELECT
SUM(fldValue) AS 'kWh',
DAY(fldDateTime) AS 'Day',
MONTH(fldDateTime) AS 'Month',
YEAR(fldDateTime) AS 'Year'
FROM
[Data.tblData]
WHERE
tblData_Id IN (SELECT DISTINCT tblData_Id
FROM [Data.tblData])
GROUP BY
YEAR(fldDateTime), MONTH(fldDateTime), DAY(fldDateTime),
tblData_Id,fldDateTime
ORDER BY
YEAR(fldDateTime), MONTH(fldDateTime), DAY(fldDateTime)
The problem I have is that it sums from midnight to midnight, I need it to sum the values after midnight ( >= Midnight) then up to midnight of the next day. The reason for this is the data that comes in for a day, is always after midnight. For example the first logged data will be '2016-01-01 00:01:00', the final logged data will be '2016-01-02 00:00:00'. This is how the hardware works that sends me the data.
I would like to know how to encapsulate >= midnight to midnight in the query.
Dataset:
DateTime Value
20/03/2016 00:30 69.00
20/03/2016 01:00 69.00
20/03/2016 01:30 69.00
20/03/2016 02:00 69.00
20/03/2016 02:30 69.00
20/03/2016 03:00 69.00
20/03/2016 03:30 11.88
20/03/2016 04:00 0.52
20/03/2016 04:30 1.51
20/03/2016 05:00 2.22
20/03/2016 05:30 2.11
20/03/2016 06:00 0.05
20/03/2016 06:30 6.78
20/03/2016 07:00 14.79
20/03/2016 07:30 1.57
20/03/2016 08:00 1.51
20/03/2016 08:30 4.81
20/03/2016 09:00 0.11
20/03/2016 09:30 8.99
20/03/2016 10:00 10.06
20/03/2016 10:30 15.28
20/03/2016 11:00 3.22
20/03/2016 11:30 1.73
20/03/2016 12:00 19.10
20/03/2016 12:30 2.08
20/03/2016 13:00 2.61
20/03/2016 13:30 0.84
20/03/2016 14:00 8.65
20/03/2016 14:30 2.37
20/03/2016 15:00 16.34
20/03/2016 15:30 12.66
20/03/2016 16:00 2.64
20/03/2016 16:30 0.19
20/03/2016 17:00 3.91
20/03/2016 17:30 2.39
20/03/2016 18:00 0.57
20/03/2016 18:30 1.30
20/03/2016 19:00 5.06
20/03/2016 19:30 17.45
20/03/2016 20:00 13.04
20/03/2016 20:30 5.00
20/03/2016 21:00 7.47
20/03/2016 21:30 5.09
20/03/2016 22:00 0.33
20/03/2016 22:30 5.29
20/03/2016 23:00 15.33
20/03/2016 23:30 5.39
21/03/2016 00:00 6.74
Thank you in advance.
The expected sum output value for 20/03/2016 is: 662.98
The output table will look like:
SumValue Day Month Year Meter Id
659.18 20 3 2016 6
251.37 21 3 2016 6
279.03 22 3 2016 6
280.03 23 3 2016 6
284.22 24 3 2016 6
310.12 25 3 2016 6
320.84 26 3 2016 6
269.29 27 3 2016 6
276.11 28 3 2016 6
279.11 29 3 2016 6
The value column is the sum of the values for that day, made up of lots of individual times.
Use the below query for summing up the midnight value with previous day.
SELECT
SUM(fldValue) AS 'kWh',
CASE WHEN CONVERT(VARCHAR(8), fldDateTime, 108)='00:00:00' THEN DAY(fldDateTime)-1 ELSE DAY(fldDateTime) END AS 'Day',
MONTH(fldDateTime) AS 'Month',
YEAR(fldDateTime) AS 'Year'
FROM
Data.[tblData]
GROUP BY
YEAR(fldDateTime), MONTH(fldDateTime),CASE WHEN CONVERT(VARCHAR(8), fldDateTime, 108)='00:00:00' THEN DAY(fldDateTime)-1 ELSE DAY(fldDateTime) END
ORDER BY
YEAR(fldDateTime), MONTH(fldDateTime), CASE WHEN CONVERT(VARCHAR(8), fldDateTime, 108)='00:00:00' THEN DAY(fldDateTime)-1 ELSE DAY(fldDateTime) END
Sample output :
First, I have no idea what the WHERE clause is doing, so I'm going to remove it.
Second, don't use single quotes for column names.
Third, your GROUP BY clause is too complicated. You only need to include the unaggregated columns in the SELECT.
Finally, the key idea is to subtract one hour from the values everywhere they are used. Here is a simple method:
SELECT SUM(fldValue) AS kWh,
DAY(newdt) AS [Day],
MONTH(newdt) AS [Month],
YEAR(newdt) AS [Year]
FROM (SELECT d.*, DATEADD(hour, -1, fldDateTime) as newdt
FROM Data.tblData d
) d
GROUP BY YEAR(newdt), MONTH(newdt), DAY(newdt)
ORDER BY YEAR(newdt), MONTH(newdt), DAY(newdt)
Same answer as #Gordon but you can subtract one minute instead of one hour.
SELECT SUM(fldValue) AS kWh,
DAY(newdt) AS [Day],
MONTH(newdt) AS [Month],
YEAR(newdt) AS [Year]
FROM (SELECT d.*, DATEADD(minute, -1, fldDateTime) as newdt
FROM Data.tblData d
) d
GROUP BY YEAR(newdt), MONTH(newdt), DAY(newdt)
ORDER BY YEAR(newdt), MONTH(newdt), DAY(newdt)
declare #tempTable table ([DateTime] datetime, Value Float)
insert into #tempTable ([DateTime], [Value])
select convert(datetime,'20/03/2016 00:30',103), 69.00 union all
select convert(datetime,'20/03/2016 01:00',103), 69.00 union all
select convert(datetime,'21/03/2016 00:00',103), 6.74
select * from #tempTable
select [sum] = SUM(value), [year] = year(DT), [month] = month(DT), [day] = day(DT)
from (select Value, DT = dateadd(second, -1, [DateTime]) from #tempTable) x
group by year(DT), month(DT), day(DT)

How to generate hours between two hours in SQL Server?

I want to generate a list of hours between to hours with an interval of 30 minutes.
For example an employee enters work at 09:00 and leaves at 18:00, so I want to generate this:
Hours
-----
09:00
09:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
How can I generate this? Thanks.
Well using recursive CTE, you can achieve this result.
Try below query -
DECLARE #timeFrom TIME = '09:00'
DECLARE #timeTo TIME = '18:00'
;with SourceHrs
as
(
select #timeFrom as [Hours]
UNION ALL
SELECT DATEADD(MINUTE, 30, [Hours]) from SourceHrs WHERE [Hours] < #timeTo
)
SELECT CONVERT(VARCHAR(5),Hours,108) FROM SourceHrs
Result
Hours
-------
09:00
09:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
This will give you what you need, using a tally is faster than recursive:
DECLARE #from time = '09:00'
DECLARE #to time = '09:00'
IF #from <= #to
WITH N(N)AS
(SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1))M(N)),
tally(N)AS(SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a)
SELECT top (datediff(minute, #from, #to)/ 30 + 1 )
LEFT(dateadd(minute, (N - 1 )*30, #from), 5)
FROM tally
You can also try while loop
DECLARE #strattime TIME = '09:00' DECLARE #endtime TIME = '18:00'
CREATE TABLE #tmp_hours( [BetweenHours] VARCHAR(5) )
WHILE #strattime <= #endtime BEGIN INSERT INTO #tmp_hours values(#strattime) SET #strattime = DATEADD(minute,30,#strattime) END
SELECT * FROM #tmp_hours DROP TABLE #tmp_hours

how to write Recursive CTE in SQL server 2012 for hour and min

I would like to generate a list of half an hour interval. Any suggestion would be very helpful. I tried this and did not work. Thank you
WITH cte
AS (select convert(varchar, DATEADD(Day, 0, DATEDIFF(Day, 0, GetDate())), 108) AS Today
UNION ALL
SELECT dateadd(MINUTE, 30, Today) AS Today
FROM cte
WHERE dateadd(MINUTE, 30,Today) < (select convert(varchar, DATEADD(Day, 1, DATEDIFF(Day, 0, GetDate())), 108))
)
SELECT*
FROM cte
To get:
0:00
0:30
1:00
1:30
2:00
2:30
3:00
3:30
4:00
4:30
5:00
5:30
6:00
6:30
7:00
7:30
8:00
8:30
9:00
9:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
18:30
19:00
19:30
20:00
20:30
21:00
21:30
22:00
22:30
23:00
23:30
0:00
You can't convert the time to a VARCHAR and then use DATETIME functions on it, you need to keep it as a DATETIME throughout the recursive portion:
;WITH cte AS (SELECT DATEADD(day, 0, DATEDIFF(day, 0, GETDATE())) dt
UNION ALL
SELECT DATEADD(MINUTE, 30, dt) AS dt
FROM cte
WHERE dt < DATEADD(day,1,GETDATE())
)
SELECT CONVERT(VARCHAR(12),dt, 108)
FROM cte