Counting minutes between two times for a daterange - sql

Using SQL Server 2008 I would like to count the amount of minutes that land between 16:30 and 18:00 for a given date range.
So inputs are for example #fromdate = '2017-04-17 17:00:00' and #todate = '2017-04-19 17:00:00'
So for the above example using lamens maths.
Day 1 (17th) : 60
Day 2 (18th) : 90
Day 3 (19th) : 30
Total 180
Day 1 is 60 because there is 60 minutes between the start date 17:00 and 18:00 Day 2 is 90 because the middle date must be the full 90 minutes between 16:30 and 18:00 Day 3 is 30 because there is 30 minutes between 16:30 and 17:00 (the time of the end date) So I am searching for the ammount of minutes between 16:30 and 18:00 for each day if that makes sense.
I would like the total to be the output.

This will work in sql server. We populate our start and end datetimes in a cte. After that, it is as simple as summing the date difference in minutes:
declare #fromdate datetime = convert(datetime,'2017-04-17 17:00:00',20)
declare #todate datetime = convert(datetime,'2017-04-19 17:00:00',20)
declare #startRangeTime time = '16:30';
declare #endRangeTime time = '18:00';
with dates as (
select top (datediff(day, #fromdate, #toDate) + 1)
case when dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) = cast(#fromdate as date)
then #fromdate
else cast(dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) as datetime) + cast(#startRangeTime as datetime)
end as startDt,
case when dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) = cast(#todate as date)
then #todate
else cast(dateadd(day,row_number() over (order by a.object_id) - 1, cast(#fromdate as date)) as datetime) + cast(#endRangeTime as datetime)
end as endDt
from sys.all_objects a
cross join sys.all_objects b
)
select sum(datediff(mi, startDt, endDt)) from dates
returns:
180
generating dates without a loop taken from this series on generating a sequence without loops

You cannot subtract 2 strings so do it this way. The sample code to get the time in minutes between 2 dates.
ALTER SESSION SET NLS_LANGUAGE="American"
WITH source as (
SELECT '30-OCT-2016 21:08:34' as A, '30/10/2016 21:06:34' as B
FROM Dual
)
SELECT TO_DATE(A, 'DD-MON-YYYY HH24:MI:SS') as new_a,
TO_DATE(B, 'DD/MM/YYYY HH24:MI:SS') as new_b,
( TO_DATE(A, 'DD-MON-YYYY HH24:MI:SS')
- TO_DATE(B, 'DD/MM/YYYY HH24:MI:SS')
) * 24 * 60 as result
FROM source
Hope this helps...

Related

How to get records in specific time range when the time range is between two days

I'm trying to query a specific range of time:
i.e. 1/1/2021 - 1/31/2021
between 5:55AM - 5:00AM (next day) for all days in above date
You need to extract time portion from your timestamps (datetime type for T-SQL) and filter by them in addition to dates filter.
db<>fiddle here
declare
#dt_from datetime
, #dt_to datetime
, #tm_from time
, #tm_to time
, #dt datetime
;
select
#dt = convert(datetime, '2021-02-10 11:48:36', 120)
, #dt_from = convert(date, '2021-02-01', 23)
, #dt_to = convert(date, '2021-02-28', 23)
, #tm_from = convert(time, '05:55:00', 120)
, #tm_to = convert(time, '05:00:00', 120)
;
with a as (
select #dt as dt union all
select dateadd(hour, 20, #dt) union all
select dateadd(hour, -15, #dt) union all
select dateadd(hour, -6, #dt) union all
select dateadd(day, -30, #dt) union all
select convert(datetime, '2021-02-01 00:01:02', 120) union all
select convert(datetime, '2021-02-28 20:01:02', 120)
)
select *
from dt
/*Restrict dates*/
where dt >= #dt_from
and dt < dateadd(day, 1, #dt_to)
and (
/*Exclude time intervals between 05:00 and 05:55*/
cast(dt as time) <= #tm_to
or cast(dt as time) >= #tm_from
)
Or if you need only the cases when entire time frame falls into your dates (e.g. exclude times from 00:00 till 05:00 for start date and from 05:55 till 23:59:59 of end date):
select *
from dt
where dt between #dt_from and #dt_to
and (
/*Exclude time intervals between 05:00 and 05:55*/
cast(dt as time) <= #tm_to
or cast(dt as time) >= #tm_from
)
/*Exclude time intervals at boundary dates*/
and not(
(cast(dt as date) = #dt_from and cast(dt as time) <= #tm_to)
or (cast(dt as date) = #dt_to and cast(dt as time) >= #tm_from)
)

How to get Last day day and Today Data based on time limit

I have sample data here:
ID Val dt
1 Mohan 2017-10-13 13:02:49.493
2 Manasa 2017-10-12 20:02:49.493
3 maren 2017-10-13 18:02:49.493
When I run the statement today at 2 PM, it should give the result set of the last day 6 PM after Data, and today data up to this time and when I run the statement today at 7 PM, it should give today's data after 6 PM.
If I ran it at Today at 2 PM, the result will be like this
ID Val dt
1 Mohan 2017-10-13 13:02:49.493
2 Manasa 2017-10-12 20:02:49.493
If I ran at Today at 7 PM:
ID Val dt
3 maren 2017-10-13 18:02:49.493
Basic thing, if I ran before 6 PM it should give last day data before 6 PM and if i ran after 6 PM it should give today Data after 6 PM. I have tried with DATE DIFF conditions but am not able to justify the result - can any one please suggest?
Table Script
Declare #tab table
(ID INT,Val Varchar(10),dt datetime)
Insert into #tab (ID,val,dt)
values (1,'Mohan','2017-10-13 13:02:49.493'),
(2,'Manasa','2017-10-12 20:02:49.493'),
(3,'maren','2017-10-13 18:02:49.493')
You can achieve this by creating two dates that simulate your day "window", i.e. 6pm yesterday and 6pm today, adjusting the concept of yesterday/today depending on whether the current time is before or after 6pm currently. You then simply select your data where dt is between those two dates (or use <= and > or whatever you need to include or exclude the relevant rows) e.g....
DECLARE #t table (ID int,Val varchar(30),dt datetime2(3))
INSERT #t(ID,Val,dt)
VALUES(1,'Mohan' ,'2017-10-13T19:02:49.493'),
(2,'Manasa','2017-10-12T20:02:49.493'),
(3,'maren' ,'2017-10-13T07:02:49.493');
DECLARE #now datetime2 = dateadd(HOUR,12,sysdatetime());
SELECT *,
#now,
x.Today6pm,
x.Yesterday6pm
FROM #t AS t
CROSS APPLY (VALUES(DATEADD(day, DATEDIFF(day,'19000101',cast(#now AS date))-(CASE WHEN cast(#now AS time) < timefromparts(18,00,00,0,0) THEN 1 ELSE 0 END), CAST(timefromparts(18,00,00,0,0) AS DATETIME2(7))),
DATEADD(day, DATEDIFF(day,'19000101',cast(#now AS date))+(CASE WHEN cast(#now AS time) > timefromparts(18,00,00,0,0) THEN 1 ELSE 0 END), CAST(timefromparts(18,00,00,0,0) AS DATETIME2(7))))) x(Yesterday6pm,Today6pm)
WHERE dt BETWEEN x.Yesterday6pm AND x.Today6pm
The CROSS APPLY here is to simplfy the code otherwise you could put those functions directly in the WHERE predicate.
the variable #now is simply used for testing to ensure the adjustment works rather than waiting until 6:01pm this evening. Obviously in your code just replace #now with sysdatetime().
Note: if your dates are datetime then adding a date and time together "works" but it does not work for datetime2. The above approach will work for both datetime and datetime2 so is more resilient.
DECLARE #T TABLE
(
ID INT,
Val VARCHAR(50) ,
dt DATETIME
)
INSERT INTO #T
VALUES
(1, 'Mohan ', '2017-10-13 13:02:49.493'),
(2, 'Manasa ', '2017-10-12 20:02:49.493'),
(3, 'maren ', '2017-10-13 18:02:49.493')
DECLARE #CURRENT datetime = '13 oct 2017 18:00'
SELECT
*
FROM #T
WHERE
(
CAST(#CURRENT as time) BETWEEN '00:00' AND '18:00' AND
dt BETWEEN DATEADD(day,-1,DATEADD(hh,18,CAST(CAST(#CURRENT as date) as datetime))) AND DATEADD(hh,18,CAST(CAST(#CURRENT as date) as datetime))
) OR
(
CAST(#CURRENT as time) NOT BETWEEN '00:00' AND '18:00' AND
dt >=DATEADD(hh,18,CAST(CAST(#CURRENT as date) as datetime))
)
You can try this script.
SELECT * FROM MyTable WHERE
( (CAST(GETDATE() AS TIME) < '18:00')
AND dt < DATEADD(HOUR,18, CAST(CAST(GETDATE() AS DATE) AS DATETIME) ) )
OR
(( CAST(GETDATE() AS TIME) >= '18:00' )
AND dt >= DATEADD(HOUR,18, CAST(CAST(GETDATE() AS DATE) AS DATETIME) ) )
Try this:
DECLARE #Now DATETIME, #FromDate DATETIME
SET #Now = GETDATE()
SET #FromDate = DATEADD( HOUR, 18, DATEADD( DAY, DATEDIFF( DAY, 0, #Now )
- ( CASE WHEN DATEPART( HOUR, #Now ) < 18 THEN 1 ELSE 0 END ), 0 ))
SELECT #Now, #FromDate
SELECT *
FROM #tab
WHERE #FromDate < dt AND dt <= #Now
Result:
Now FromDate
----------------------- -----------------------
2017-10-13 19:36:40.963 2017-10-13 18:00:00.000
ID Val dt
----------- ---------- -----------------------
3 maren 2017-10-13 18:02:49.493
Explanation:
#FromDate is calculated as follows:
( CASE WHEN DATEPART( HOUR, #Now ) < 18 THEN 1 ELSE 0 END ) - if hour is less than 18 (6PM) then return 1
DATEADD( HOUR, 18, DATEADD( DAY, DATEDIFF( DAY, 0, #Now ) - ... , 0 )) - return "6PM date", which is either yesterday or today depending on the result of the CASE expression above
You could try this:
SELECT * FROM [yourTable] WHERE
(dt between CONCAT(CAST(GETDATE() AS DATE), ' ', '18:00:00.000') AND
CONCAT(CAST(GETDATE() AS DATE), ' ', '23:59:59.999')
AND HOUR(GETDATE()) > 18)
OR
(dt between CONCAT(CAST(dt AS DATE) - INTERVAL 1 DAY, ' ', '18:00:00.000')
AND GETDATE()
AND HOUR(GETDATE()) < 18)

SQL calculate points between a time range

I’ve a table which contains different time ranges:
Id Start Time End Time Points
1 0:00 3:00 10
2 3:01 6:00 20
3 6:01 23:59 30
Now I need to calculate the points achieved between two date ranges with respect to time specified.
Start date = 11/9/2016 18:17:00 and
End date = 11/10/2016 01:20:00
I need to calculate the sum of points gained between these two dates.
The time of start date that is 18:17 falls under Id 3, whose point is 30. So the calculation will be,
18:17 to 23:59 -> 6 hrs -> 6 * 30 = 180 points
The end time 01:20 falls under Id 1
0:00 to 1:20 -> 2 hrs
(if minute is greater than zero, it is rounded to next hour, ie; 2) -> 2 * 10 = 20 points
So the total points gained will be 200 points.
Taking the time difference, does not help me, if the start and end date difference is greater than one day.
Table Structure:
Id - int,
StartTime - time(7),
EndTime - time(7),
Points - int
How to write a query for this using SQL?
This question was good.
You can as the below:
DECLARE #Tbl TABLE (Id INT, StartTime TIME, EndTime TIME, Points INT)
INSERT INTO #Tbl
VALUES
(1, '0:00', '3:00' , 10),
(2, '3:01', '6:00' , 20),
(3, '6:01', '23:59', 30)
DECLARE #StartDate DATETIME = '2016.11.09 18:17:00'
DECLARE #EndDate DATETIME = '2016.11.10 01:20:00'
;WITH CTE
AS
(
SELECT 1 AS RowId, #StartDate CurrentDate, 0 Point, #StartDate DateVal UNION ALL
SELECT
A.RowId ,
IIF((A.CurrentDate + A.EndTime) > #EndDate, #EndDate, DATEADD(MINUTE, 1, (A.CurrentDate + A.EndTime))) AS CurrentDate,
A.Points,
IIF((A.CurrentDate + A.EndTime) > #EndDate, #EndDate, (A.CurrentDate + A.EndTime)) DateVal
FROM
(
SELECT
C.RowId + 1 AS RowId,
CAST(CAST(CurrentDate AS DATE) AS DATETIME) CurrentDate,
CAST((SELECT T.EndTime FROM #Tbl T WHERE CAST(CurrentDate AS TIME) BETWEEN T.StartTime AND T.EndTime) AS DATETIME) AS EndTime,
(SELECT T.Points FROM #Tbl T WHERE CAST(CurrentDate AS TIME) BETWEEN T.StartTime AND T.EndTime) AS Points,
C.CurrentDate AS TempDate
FROM CTE C
) A
WHERE
A.TempDate <> IIF((A.CurrentDate + A.EndTime) > #EndDate, #EndDate, DATEADD(MINUTE, 1, (A.CurrentDate + A.EndTime)))
), CTE2
AS
(
SELECT
C.RowId ,
C.CurrentDate ,
C.Point ,
C.DateVal,
DATEDIFF(MINUTE, LAG(C.DateVal) OVER (ORDER BY C.RowId), C.DateVal) MinuteOfDateDiff
FROM
CTE C
)
SELECT
SUM(CEILING(C.MinuteOfDateDiff * 1.0 / 60.0) * C.Point)
FROM
CTE2 C
Result: 200

Query to select a count group by time period

I have a typical log table with many details and a datetime of when the record was created.
I'm trying to analyse how many times does an event occurs per a time period (each 30 mins). I need to be able to analyze for a period bigger than one day. Basically, my desired output would be something like:
Period | Total
Day 1 00:00 - 00:30 | 23
Day 1 00:30 - 01:00 | 0
Day 1 01:00 - 01:30 | 534
...
Day 2 23:00 - 23:30 | 23
I'm flexible on the date column layout - I just need to be "readable"!
Here's my try but it's not working very well
declare #startdatetime datetime = '2016-02-03 00:00:00'
declare #enddatetime datetime = '2016-02-19 23:59:59'
declare #apiserviceid int = 21
select DATEPART(MINUTE, usr.STARTDATETIME) % 30, COUNT(*)
from TABLE 1 usr
where usr.APIREQUESTID = #apiserviceid
and usr.STARTDATETIME >= #startdatetime and usr.STARTDATETIME <= #enddatetime
group by
(DATEPART(MINUTE, usr.STARTDATETIME) % 30)
order by 1;
Thanks :)
DATEPART(MINUTE,...) are just the minutes within one hour, not the minutes within one day. Consider the hours as well
select (60 * DATEPART(HOUR, usr.STARTDATETIME) +
DATEPART(MINUTE, usr.STARTDATETIME)) % 30, COUNT(*)
...
I would recommend using a recursive CTE to generate all of your 30 min time segments and then JOIN on your table to find the occurrences.
DECLARE #startdate DATETIME = '2016-02-23'--your_starting_date
DECLARE #enddate DATETIME = '2016-03-01' --your_ending_date
;WITH cte AS (
SELECT
#startdate AS start_time
, DATEADD(MINUTE, 30, #startdate) AS end_time
UNION ALL
SELECT DATEADD(MINUTE, 30, start_time) AS start_time
, DATEADD(MINUTE, 30, end_time) AS end_TIME
FROM cte
WHERE end_time <= #enddate
)
SELECT *
INTO #time_table
FROM CTE
OPTION (MAXRECURSION 32727)
GO
SELECT
start_time
, end_time
, SUM(CASE WHEN your_time_column BETWEEN start_time AND end_time THEN 1 ELSE 0 END) AS total_count
FROM #time_table
INNER JOIN your_table --left join if you want all time slots with 0 occurrences
ON your_time_column BETWEEN start_time AND end_time
GROUP BY
start_time
, end_time
This will group by on the hour (not 30 min)
declare #startdatetime datetime = '2016-02-03 00:00:00'
declare #enddatetime datetime = '2016-02-19 23:59:59'
declare #apiserviceid int = 21
select min(usr.STARTDATETIME), max(usr.STARTDATETIME), count(0)
from TABLE 1 usr
where usr.APIREQUESTID = #apiserviceid
and usr.STARTDATETIME >= #startdatetime and usr.STARTDATETIME <= #enddatetime
group by convert(char(13),usr.STARTDATETIME,121)
order by 1;
select cast(year(usr.STARTDATETIME) as varchar)+''
+cast(month(usr.STARTDATETIME) as varchar)+
'-'+cast(day(usr.STARTDATETIME) as varchar)+' '+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':'+
CASE WHEN DATEPART(MINUTE, usr.STARTDATETIME)>30 THEN '31 - '+
CAST((DATEPART(HOUR, usr.STARTDATETIME)+1) AS VARCHAR) +':00' ELSE '00 -
'+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':30' END , COUNT(*)
from TABLE 1 usr
where usr.APIREQUESTID = #apiserviceid
and usr.STARTDATETIME >= #startdatetime and usr.STARTDATETIME <= #enddatetime
group by
cast(year(usr.STARTDATETIME) as varchar)+'-'+
cast(month(usr.STARTDATETIME) as varchar)+'-'+cast(day(usr.STARTDATETIME) as
varchar)+' '+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':'+
CASE WHEN DATEPART(MINUTE, usr.STARTDATETIME)>30 THEN '31 - '+
CAST((DATEPART(HOUR, usr.STARTDATETIME)+1) AS VARCHAR) +':00' ELSE '00 -
'+CAST(DATEPART(HOUR, usr.STARTDATETIME) AS VARCHAR)+':30' END
order by 1;

SQL Insert Dates every 7 days for 10 years

I am trying to script a query that inserts a date into a table every 7 days for the next 10 years. This will prevent me from having to have to type these dates by hand.
Is there a way to specify a start date and add 7 days to that date on each insert until the end date is reached?
Attached is my query. not sure where to being on this one. Any help is most appreciated.
declare #startDate date
declare #endDate date
set #startDate='2015-01-03'
set #endDate='2015-01-04'
INSERT INTO TimePeriod (YearsA)
VALUES ('2015-01-03'),
('2015-01010'),
(etc.)
('2025-01-04)
The below query will give you weekend dates for till 2042-05-17 years.
SELECT DISTINCT DATEADD(DAY, - DATEPART(WEEKDAY, DayNumber), CAST(DayNumber AS DATE))
FROM(
SELECT TOP (10000)
DATEADD(DAY
,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1
, CAST(YEAR(GETDATE()) AS VARCHAR(4)) + '0101' ) DayNumber
From master..spt_values x Cross Join master..spt_values y
)x
ORDER BY DATEADD(DAY, - DATEPART(WEEKDAY, DayNumber), CAST(DayNumber AS DATE))
Result
2015-01-03
2015-01-10
2015-01-17
2015-01-24
2015-01-31
2015-02-07
2015-02-14
2015-02-21
2015-02-28
You can use recursive CTE to get all the dates:
try;
declare #startDate date
declare #endDate date
set #startDate='2015-03-01' -- YYYY-MM-DD format
set #endDate='2015-04-01'
;with all_date as (
select #startDate Dates
union all
select DATEADD(day, 7, Dates)
from all_date
where Dates < #endDate
)
INSERT INTO TimePeriod (YearsA)
select Dates from all_date