Select part of duration that occurs in specific hour of day - sql-server

In SQL Server, I have a table of processes with a starttime and an endtime, from which I can calculate a duration using DATEDIFF.
Name StartTime EndTime
------------------------------------------------
process1 2016-10-10 11:10 2016-10-10 11:20
process2 2016-10-10 11:40 2016-10-10 12:30
How can I select the timespan of the process duration that occured in specific hours of the day (11 and 12) in seconds?
So process1 would be 10 minutes in hour 11.
process2 would be 20 minutes in hour 11, 30 minutes in hour 12.

Here's a sample that will generate hourly records for each source record. It uses a recursive CTE to move from the StartTime of each record through the EndTime. It may need some slight modifications to make it work in your case, but hopefully you can get the idea of how this method works.
Note that, as shown in the sample below, this will work properly even if the time span crosses a date boundary.
--==================================================================================
-- Do some quick setup to get a temporary table populated with data to use:
--==================================================================================
IF OBJECT_ID('tempdb..#ProcessHistory', 'U') IS NOT NULL DROP TABLE #ProcessHistory;
CREATE TABLE #ProcessHistory (
Name VARCHAR(20),
StartTime DATETIME,
EndTime DATETIME
)
INSERT INTO #ProcessHistory
VALUES ('process1', '2016-10-10 11:10', '2016-10-10 11:20'),
('process2', '2016-10-10 11:40', '2016-10-10 12:30'),
('process3', '2016-10-10 22:21', '2016-10-11 02:36');
--==================================================================================
-- Use a recursive CTE to generate hourly data for each record:
--==================================================================================
WITH HourlyData AS (
-- Anchor:
SELECT
ph.Name [ProcessName],
ph.StartTime [StartTime],
ph.EndTime [EndTime],
-- Get the current hour with date:
DATEADD(MINUTE, -DATEPART(MINUTE, ph.StartTime), ph.StartTime) [CurrentHour],
-- Calculate the next hour for use later:
DATEADD(MINUTE, 60 - DATEPART(MINUTE, ph.StartTime), ph.StartTime) [NextHour],
-- Determine how many minutes the process was active this hour:
CASE
WHEN DATEDIFF(MINUTE, ph.StartTime, ph.EndTime) > 60 - DATEPART(MINUTE, ph.StartTime)
THEN 60 - DATEPART(MINUTE, ph.StartTime)
ELSE DATEDIFF(MINUTE, ph.StartTime, ph.EndTime)
END [Minutes]
FROM #ProcessHistory ph
UNION ALL
-- Recurse:
SELECT
hd.ProcessName,
hd.StartTime,
hd.EndTime,
hd.NextHour [CurrentHour],
DATEADD(HOUR, 1, hd.NextHour) [NextHour],
-- Determine how many minutes the process was active this hour:
CASE
WHEN DATEDIFF(MINUTE, hd.NextHour, hd.EndTime) < 60
THEN DATEDIFF(MINUTE, hd.NextHour, hd.EndTime)
ELSE 60
END
FROM HourlyData hd
WHERE hd.NextHour < hd.EndTime
)
SELECT
hd.ProcessName,
hd.CurrentHour [HourWithDate],
CONVERT(DATE, hd.CurrentHour) [Date],
DATEPART(HOUR, hd.CurrentHour) [Hour],
hd.Minutes
FROM HourlyData hd
ORDER BY
hd.ProcessName,
hd.CurrentHour;
The output from the above sample would look like this:
ProcessName HourWithDate Date Hour Minutes
process1 2016-10-10 11:00:00.000 2016-10-10 11 10
process2 2016-10-10 11:00:00.000 2016-10-10 11 20
process2 2016-10-10 12:00:00.000 2016-10-10 12 30
process3 2016-10-10 22:00:00.000 2016-10-10 22 39
process3 2016-10-10 23:00:00.000 2016-10-10 23 60
process3 2016-10-11 00:00:00.000 2016-10-11 0 60
process3 2016-10-11 01:00:00.000 2016-10-11 1 60
process3 2016-10-11 02:00:00.000 2016-10-11 2 36

To handle general cases, you could try something like
--drop table #processes
CREATE TABLE #processes
(
name varchar(50),
StartTime Datetime,
EndTime DateTime
);
insert #processes VALUES('proc1','20161010 11:10','20161010 11:20');
insert #processes VALUES('proc2','20161010 11:40','20161010 12:20');
insert #processes VALUES('proc3','20161010 10:40','20161010 12:20');
;WITH HRS AS (SELECT 0 HR
UNION ALL
SELECT HR + 1 FROM HRS WHERE HR < 23),
MINS AS (SELECT 0 MN
UNION ALL
SELECT MN + 1 FROM MINS WHERE MN < 59),
TIMES AS (SELECT HR,MN FROM HRS CROSS JOIN MINS)
SELECT name,starttime,endtime,Count(0) AS mins FROM #processes
JOIN TIMES
ON (HR > datepart(hh,Starttime)
OR HR = datepart(hh,Starttime) AND MN >= datepart(n,STARTtIME))
AND
(HR < datepart(hh, EndTime)
OR HR = datepart(hh, EndTime) AND MN < datepart(n,EndTime))
WHERE HR = 11 --hour is 11
GROUP BY name,
starttime,
endtime
drop table #processes;

create table #temp (Name varchar(5), starttime datetime, EndTime datetime)
insert into #temp values(1,'2016-10-10 11:10','2016-10-10 11:20' )
insert into #temp values(2,'2016-10-10 11:40','2016-10-10 12:30' )
insert into #temp values(2,'2016-10-10 10:40','2016-10-10 11:30' )
insert into #temp values(2,'2016-10-10 10:40','2016-10-10 12:30' )
DECLARE #firstTime time ,#secondTime time
set #firstTime ='11:00'
set #secondTime ='12:00'
select
CASE WHEN CONVERT(time(0), starttime) < #firstTime AND CONVERT(time(0), EndTime) > #secondTime THEN DATEDIFF(ss,#firstTime,#secondTime)
WHEN CONVERT(time(0), EndTime) > #secondTime THEN DATEDIFF(ss,CONVERT(time(0), starttime),#secondTime)
WHEN CONVERT(time(0), starttime) < #firstTime THEN DATEDIFF(ss,CONVERT(time(0), EndTime),#secondTime)
ELSE DATEDIFF(ss,starttime,EndTime)
END
from #temp

SELECT CAST(DATEADD(MINUTE,DATEDIFF(MINUTE,'2016-10-10 11:10','2016-10-10 12:20'),'2011-01-01 00:00:00.000') AS TIME)
as timeDifference
With timeDifference -
SELECT CAST(DATEADD(MINUTE,DATEDIFF(MINUTE,StartTime,EndTime),'2011-01-01 00:00:00.000') AS TIME)
as timeDifference
from #YourTableName
With Days and TimeDiffernece
declare #start_time as varchar(150);
declare #end_time as varchar(150);
set #start_time='2016-10-10 10:10';
set #end_time='2016-10-12 12:10'
SELECT datediff(day,#start_time,#end_time) as dayDifference,
CAST(DATEADD(MINUTE,DATEDIFF(MINUTE,#start_time,#end_time),'2011-01-01 00:00:00') AS TIME(0))
as timeDifference

I think this does the trick, but it's pretty ugly. Maybe someone can do it more elegantly?
SELECT
case
when HOUR(starttime) < 11 AND HOUR(endtime) = 11 then minute(endtime)
when HOUR(starttime) < 11 AND HOUR(endtime) > 11 then 60
when HOUR(starttime) = 11 AND HOUR(endtime) = 11 then minute(endtime) - minute(starttime)
when HOUR(starttime) = 11 AND HOUR(endtime) > 11 then 60 - minute(starttime)
else 0
end AS ProcessTimeHour_11,
case
when HOUR(starttime) < 12 AND HOUR(endtime) = 12 then minute(endtime)
when HOUR(starttime) < 12 AND HOUR(endtime) > 12 then 60
when HOUR(starttime) = 12 AND HOUR(endtime) = 12 then minute(endtime) - minute(starttime)
when HOUR(starttime) = 12 AND HOUR(endtime) > 12 then 60 - minute(starttime)
else 0
end AS ProcessTimeHour_12
from StuffAndThings

Related

Calculate time between startdate and enddate and subtracting days that have no worktime

My goal is to check if an email is answered within 24 hours during workdays. de definition of a workday is if there is time registered in another table. this because we sometimes work on a Saturday or a Sunday or to exclude holidays. I made a view from that table that gives a 1 if the date has worktime or a 0 if there is no worktime registered.
DateWorked
HasWorked
2021-04-01 00:00:00.000
1
2021-04-02 00:00:00.000
1
2021-04-03 00:00:00.000
1
2021-04-04 00:00:00.000
0
2021-04-05 00:00:00.000
1
So for example a few situations:
1. MailIncoming: 2021-04-01 16:30:00, MailAnswering: 2021-04-02 14:00:00
This one is easy, I don't have to subtract anything and the mail is answered within 24 hours.
2. MailIncoming: 2021-04-01 09:30:00, MailAnswering: 2021-04-03 14:00:00
This one is also easy, I don't have to subtract anything and the mail is not answered within 24 hours.
3. MailIncoming: 2021-04-03 12:30:00, MailAnswering: 2021-04-05 10:00:00
There is 1 day where no one has worked, so I need to subtract 1 whole day from the total time, and in that case the email is answered within 24 hours during workdays.
4. MailIncoming: 2021-04-04 11:00:00, MailAnswering: 2021-04-05 18:00:00
The remaining 13 hours from 04 do not count toward the '24 hours during workdays' so the email is answered within 24 during workdays.
Also, there can be multiple dates with zero after each other.
So the outcome I'm looking for is:
MailIncoming
MailAnswering
TotalTime
TotalTimeWithoutDaysNotWorked
2021-04-04 11:00:00.000
2021-04-05 18:00:00.000
31
18
How can I calculate this last column? Or am I approaching this in the wrong way?
The query needs a way to generate calculated dates between MailIncoming and MailAnswering so there can be a LEFT JOIN (or INNER JOIN) to the WorkingDay table. In this case the query uses dbo.fnTally which is known to be a fast and efficient way to generate rows.
tables
drop table if exists #WorkingDay;
go
create table #WorkingDay(
DateWorked Date,
HasNotWorked int);
drop table if exists #MailIncoming;
go
create table #MailIncoming(
MailIncoming DateTime,
MailAnswering DateTime);
insert into #WorkingDay values
('2021-04-01', 0),
('2021-04-02', 0),
('2021-04-03', 0),
('2021-04-04', 1),
('2021-04-05', 0),
('2021-04-06', 0);
insert into #MailIncoming values
('2021-04-01 16:30:00', '2021-04-02 14:00:00'),
('2021-04-01 09:30:00', '2021-04-03 14:00:00'),
('2021-04-03 12:30:00', '2021-04-05 10:00:00'),
('2021-04-04 11:00:00', '2021-04-05 18:00:00');
dbo.fnTally
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Jeff Moden Script on SSC: https://www.sqlservercentral.com/scripts/create-a-tally-function-fntally
**********************************************************************************************************************/
(#ZeroOrOne BIT, #MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
H2(N) AS ( SELECT 1
FROM (VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)V(N)) --16^2 or 256 rows
, H4(N) AS (SELECT 1 FROM H2 a, H2 b) --16^4 or 65,536 rows
, H8(N) AS (SELECT 1 FROM H4 a, H4 b) --16^8 or 4,294,967,296 rows
SELECT N = 0 WHERE #ZeroOrOne = 0 UNION ALL
SELECT TOP(#MaxN)
N = ROW_NUMBER() OVER (ORDER BY N)
FROM H8
;
query
select mi.MailIncoming, mi.MailAnswering,
avg(datediff(hour, MailIncoming, MailAnswering)) hrs_to_ans,
sum(case when w.HasNotWorked=1 and
v.calc_dt > mi_dt.inc_dt and
v.calc_dt < mi_dt.ans_dt
then -24
when w.HasNotWorked=1
then datediff(hour, dateadd(day, 1, mi_dt.inc_dt), mi.MailIncoming)
else 0 end) hrs_to_sub
from #MailIncoming mi
cross apply (values (cast(MailIncoming as date),
cast(MailAnswering as date))) mi_dt(inc_dt, ans_dt)
cross apply dbo.fnTally(0, datediff(day, mi.MailIncoming, mi.MailAnswering)) fn
cross apply (values (dateadd(day, fn.n, mi_dt.inc_dt))) v(calc_dt)
left join #WorkingDay w on v.calc_dt=w.DateWorked
group by mi.MailIncoming, mi.MailAnswering
order by mi.MailIncoming;
MailIncoming MailAnswering hrs_to_ans hrs_to_sub
2021-04-01 09:30:00.000 2021-04-03 14:00:00.000 53 0
2021-04-01 16:30:00.000 2021-04-02 14:00:00.000 22 0
2021-04-03 12:30:00.000 2021-04-05 10:00:00.000 46 -24
2021-04-04 11:00:00.000 2021-04-05 18:00:00.000 31 -13
I suggest you to use a column HasNotWorked, so the tables are
create table WorkingDay(DateWorked Date, HasNotWorked int);
create table MailIncoming(MailIncoming DateTime, MailAnswering DateTime);
and the rows
insert into WorkingDay values('2021-04-01', 0);
insert into WorkingDay values('2021-04-02', 0);
insert into WorkingDay values('2021-04-03', 0);
insert into WorkingDay values('2021-04-04', 1);
insert into WorkingDay values('2021-04-05', 0);
insert into WorkingDay values('2021-04-06', 0);
insert into MailIncoming values('2021-04-04 11:00:00.000', '2021-04-06 18:00:00.000');
I want calculate the start date. If is in working day, we must consider the hour of the mail, else the first working day with
case when
(select HasNotWorked from WorkingDay where DateWorked = convert(date, MailIncoming)) = 1 then
(select min(DateWorked) from WorkingDay where DateWorked > MailIncoming and HasNotWorked = 0)
else MailIncoming end as startDate
and discard the day that are not working day
((select sum(HasNotWorked) from WorkingDay where DateWorked between convert(date, startDate)
and convert(date, MailAnswering)
) * 24) as numNotWorkingDay
so the query could be
select startDate, MailAnswering, MailIncoming, hour, numNotWorkingDay, hour - numNotWorkingDay hourWitoutWorkingDay
from (
select
MailAnswering, startDate, MailIncoming,
DateDiff("hh", startDate, MailAnswering) hour,
((select sum(HasNotWorked) from WorkingDay where DateWorked between convert(date, startDate)
and convert(date, MailAnswering)
) * 24) as numNotWorkingDay
from (
select *,
case when
(select HasNotWorked from WorkingDay where DateWorked = convert(date, MailIncoming)) = 1 then
(select min(DateWorked) from WorkingDay where DateWorked > MailIncoming and HasNotWorked = 0)
else MailIncoming end as startDate
from MailIncoming) as startCalc
) as calcTable;
sqlfiddle

ms sql group by 2 hours

i have this ms-sql code below, running on sql server 2017:
DECLARE #report_int TABLE
(
atimestamp DATETIME,
adate DATE,
ahour INT,
intvalue INT,
attribute CHAR(254),
total INT
)
INSERT INTO #report_int
SELECT
el.servertimestamp,
cast(el.servertimestamp as date) adate,
(DATEPART(HH,el.servertimestamp)) ahour,
el.intvalue,
el.attributes,
0
FROM
eventlog el
where
el.servertimestamp BETWEEN '6/29/2017'
AND DATEADD(dd, +1, '6/29/2019')
and (attributes LIKE '%N<=>OlympieioIn%' OR attributes LIKE '%N<=>OlympieioOut%')
order by
el.servertimestamp
DECLARE #report TABLE
(
adate datetime,
ahour int,
came_in bigint,
gone_out bigint
)
--fill report with dates and hours
INSERT INTO #report (adate, ahour) SELECT DISTINCT adate, ahour FROM #report_int
--reset
UPDATE #report set came_in = 0, gone_out = 0
--update in totals, min and max times
UPDATE #report SET came_in = i.total from
#report r,
(
select adate, ahour, COUNT(*) total
FROM #report_int
WHERE attribute LIKE 'N<=>OlympieioIn'
GROUP BY adate, ahour
) i
WHERE
i.adate = r.adate
and i.ahour = r.ahour
--update out totals
UPDATE #report SET gone_out = i.total from
#report r,
(
SELECT adate, ahour, COUNT(*) total
FROM #report_int
WHERE attribute LIKE 'N<=>OlympieioOut'
GROUP BY adate, ahour
) i
where
i.adate = r.adate
and i.ahour = r.ahour
select
adate,
ahour,
RIGHT('00'+cast(ahour as varchar),2)+':00 - '+RIGHT('00'+cast(ahour+1 as varchar),2)+':00' timeframe,
came_in,
gone_out
from
#report
This code shows how many people came in or gone out of a building every day. As you can see, the amount of people who come in and go out, are displayed in an hourly grouping.
Below is a screenshot of the executed code.
What i want to do is group these data in a 2-hour grouping.For example, the grouping of the 3rd and 4th hour of the executed code above, will be:
DATE(adate): 2018-05-12
Hour(ahour): 15
Timeframe: 15:00-17:00
came_in: 0
gone_out: 2
Your help will be much appreciated,
thank you.
One way to get a datetime to the last 2 hour slot would be to use:
DATEADD(HOUR, DATEDIFF(HOUR, 0, YourDateColumn) - (DATEDIFF(HOUR, 0, YourDateColumn) % 2), 0)
If we then take some (random) sample data, you get the following:
WITH VTE AS (
SELECT CONVERT(datetime2(0),V.DT) AS DT
FROM (VALUES('2018-07-04T00:11:32'),('2018-07-04T01:17:12'),('2018-07-04T02:00:01'),('2018-07-04T07:49:59'),('2018-07-04T07:59:58'),('2018-07-04T08:00:00')) V(DT))
SELECT DT, DATEADD(HOUR, DATEDIFF(HOUR, 0, DT) - (DATEDIFF(HOUR, 0, DT) % 2), 0) AS DT2h
FROM VTE;
Which returns:
DT DT2h
--------------------------- -----------------------
2018-07-04 00:11:32 2018-07-04 00:00:00.000
2018-07-04 01:17:12 2018-07-04 00:00:00.000
2018-07-04 02:00:01 2018-07-04 02:00:00.000
2018-07-04 07:49:59 2018-07-04 06:00:00.000
2018-07-04 07:59:58 2018-07-04 06:00:00.000
2018-07-04 08:00:00 2018-07-04 08:00:00.000
If you wanted a COUNT by 2 hour slot:
WITH VTE AS (
SELECT CONVERT(datetime2(0),V.DT) AS DT
FROM (VALUES('2018-07-04T00:11:32'),('2018-07-04T01:17:12'),('2018-07-04T02:00:01'),('2018-07-04T07:49:59'),('2018-07-04T07:59:58'),('2018-07-04T08:00:00')) V(DT))
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, DT) - (DATEDIFF(HOUR, 0, DT) % 2), 0) AS DT2h, COUNT(DT) AS C
FROM VTE
GROUP BY DATEADD(HOUR, DATEDIFF(HOUR, 0, DT) - (DATEDIFF(HOUR, 0, DT) % 2), 0);
Which returns:
DT2h C
----------------------- -----------
2018-07-04 00:00:00.000 2
2018-07-04 02:00:00.000 1
2018-07-04 06:00:00.000 2
2018-07-04 08:00:00.000 1
I would simply manually create a timetable and join it.
The timetable would have 24 rows (one for each hour) and could look like this:
hour hourtimeframe twohourtimeframe
13 13:00 - 14:00 13:00 - 15:00

SQL query to join tables - where one table's datetime does not match the other ones

Joined just to ask this because it is killing me :) Great forum with a lot of great minds!
I simply need to join these two tables, i can only join them on date and time no other columns are available.
Table 1 has for example following columns
Film -----------------Datetime……………………………Duration(minutes)
TITANIC------------2016-01-01 01:00:00-----------------60
Armageddon---------2016-01-01 02:00:00-----------------60
Table 2 has following columns
Date----------------Time
2016-01-01……….01:00:00
2016-01-01……….01:01:00
2016-01-01……….01:02:00
2016-01-01……….01:03:00
…and so on
Table 2 contains info for every minute but table one only one specific time and date per event. so i need to match for every minute in table two with what i got from table one.
Any ideas? i'll take anything that works :) btw sorry for the formatting!
Edit:
Desired result would be something like
TITANIC------------2016-01-01 01:00:00-----------------60
TITANIC------------2016-01-01 01:01:00-----------------60
TITANIC------------2016-01-01 01:02:00-----------------60
Armageddon---------2016-01-01 02:00:00-----------------60
Armageddon---------2016-01-01 02:01:00-----------------60
Armageddon---------2016-01-01 02:02:00-----------------60
And so on...
convert the table 2 date and time to a datetime and see if it is between table 1 datetime and datetime + duration.
SELECT *
FROM table1 t1
JOIN table2 t2 ON CAST(t2.Date AS DATETIME)
+ CAST(t2.Time AS DATETIME) >= t1.Datetime
AND CAST(t2.Date AS DATETIME)
+ CAST(t2.Time AS DATETIME) < DATEADD(MIN,
t1.Duration,
t1.Datetime)
I'm going to make some assumptions as you haven't outlined the table structure.
Your film table is of the following structure:
CREATE TABLE Films ([Film] NVARCHAR(128), [DateTime] DATETIME, Duration INT)
GO
Your Date/Time values table is of the following structure:
CREATE TABLE DateTimeValues ([Date] DATE, [Time] TIME)
GO
Lets insert your values:
--Insert Values for Films
INSERT INTO Films
VALUES ('TITANIC', '2016-01-01T01:00:00', 60),
('Armageddon', '2016-01-01 02:00:00', 60)
GO
--Insert Values for every minute of 2016-01-01
DECLARE #DATETIMEBEGIN DATETIME
SET #DATETIMEBEGIN = '2016-01-01'
DECLARE #DATETIMEEND DATETIME
SELECT #DATETIMEEND = '2016-01-02'
;WITH CTE AS (SELECT DATEADD(day, 0, DATEDIFF(day, 0, #DATETIMEBEGIN)) DateTimeValues
UNION ALL
SELECT DATEADD(MINUTE, 1, DateTimeValues) AS DateTimeValues
FROM CTE
WHERE DateTimeValues < #DATETIMEEND
)
INSERT INTO DateTimeValues
SELECT CONVERT(DATE, DateTimeValues) "Date",
CONVERT(TIME, DateTimeValues) "Time"
FROM CTE
OPTION (MAXRECURSION 0)
GO
Lets make it simple and lets caluclate the start time/end time for each film according to the duration. The query you will need for your desired output is:
;WITH CTEFilms AS
(
SELECT Film,
CONVERT(DATE, [DateTime]) "Date",
CONVERT(TIME, [DateTime]) "StartTime",
CONVERT(TIME, DATEADD(MINUTE,Duration,[DateTime])) "EndTime",
Duration
FROM Films
)
SELECT f.Film,
CAST(dtv."Date" AS DATETIME) + dtv."Time" "DateTime",
Duration
FROM CTEFilms f
INNER JOIN DateTimeValues dtv
ON f.[Date] = dtv.[Date]
AND dtv.[Time] >= f.StartTime
AND dtv.[Time] < f.EndTime
ORDER BY Film, Time
Your Results:
Film DateTime Duration
Armageddon 2016-01-01 02:00:00.000 60
Armageddon 2016-01-01 02:01:00.000 60
Armageddon 2016-01-01 02:02:00.000 60
Armageddon 2016-01-01 02:03:00.000 60
Armageddon 2016-01-01 02:04:00.000 60
...
Armageddon 2016-01-01 02:55:00.000 60
Armageddon 2016-01-01 02:56:00.000 60
Armageddon 2016-01-01 02:57:00.000 60
Armageddon 2016-01-01 02:58:00.000 60
Armageddon 2016-01-01 02:59:00.000 60
TITANIC 2016-01-01 01:00:00.000 60
TITANIC 2016-01-01 01:01:00.000 60
TITANIC 2016-01-01 01:02:00.000 60
TITANIC 2016-01-01 01:03:00.000 60
TITANIC 2016-01-01 01:04:00.000 60
...
TITANIC 2016-01-01 01:55:00.000 60
TITANIC 2016-01-01 01:56:00.000 60
TITANIC 2016-01-01 01:57:00.000 60
TITANIC 2016-01-01 01:58:00.000 60
TITANIC 2016-01-01 01:59:00.000 60
#Eddie B, you did not give out your table structure, i.e. column data types, so I just come up with the following assumed code/data
use tempdb
drop table dbo.t1, dbo.t2;
create table dbo.T1 (film varchar(10), dt datetime, duration int)
create table dbo.T2 (dt varchar(10), tm varchar(10));
go
-- populate the tables with SOME sample data
insert into dbo.T1(film, dt, duration)
values ('Titanic', '2016-01-01 01:00:00', 5), ('Amageddon', '2016-01-01 02:00:00', 4)
insert into dbo.T2 (dt, tm)
values
('2016-01-01', '01:00:00')
, ('2016-01-01', '01:01:00')
, ('2016-01-01', '01:02:00')
, ('2016-01-01', '01:03:00')
, ('2016-01-01', '01:04:00')
, ('2016-01-01', '01:05:00')
, ('2016-01-01', '02:00:00')
, ('2016-01-01', '02:01:00')
, ('2016-01-01', '02:02:00')
, ('2016-01-01', '02:03:00')
, ('2016-01-01', '02:04:00')
, ('2016-01-01', '02:05:00')
, ('2016-01-01', '03:00:00');
go
-- here is the result
select t1.film, [DateTime]= convert(datetime, t2.dt + ' ' + t2.tm), t1.duration
from dbo.t1
inner join dbo.t2
on convert(datetime, t2.dt + ' ' + t2.tm) >= t1.dt
and convert(datetime, t2.dt + ' ' + t2.tm) <= dateadd(minute, t1.duration, t1.dt)
go
Here is the result:

T-SQL to create one data point for each hour over past 24 hours

Please how may we do this:
1) Generate 24 rows one for each hour from current time back 24 hours
2) Aggregate data from another table over the past 24 hours into these 24 data points.
I have seen solutions suggesting number tables from 0-23, but these might make it difficult if we need this to start from NOW, then run back 24 hours Get every hour for a time range
e.g [5:00am, 4:00am, 3:00am ... 12:am, 11pm ... 7:00am,6:00am]
Source Table:
select d,h,count(1)cnt from msgs
where dt>= DateAdd(hh, -24, sysdatetime())
group by d,h order by 1 desc,2 desc
Sample Data
d h cnt
2015-06-05 16 11
2015-06-05 13 44
2015-06-05 12 16
2015-06-05 11 31
2015-06-05 10 10
2015-06-05 9 12
2015-06-05 8 1
2015-06-04 21 1
2015-06-04 20 2
2015-06-04 18 5
2015-06-04 16 2
I have missing hours, i would need a query that fills out the missing hours with 0
As an alternative solution, you could use this query to provide all 24 hour ranges. Then simply aggregate and sum these values against your original query to return only 24 rows.
;WITH hrs AS
(
SELECT h = 1
UNION ALL
SELECT h + 1
FROM hrs
WHERE h + 1 <= 24
)
SELECT
d = left(convert(varchar(50),DateAdd(hour, -1 * h, getdate()), 21),10),
h = DatePart(hour, DateAdd(hour, -1 * h, getdate())),
cnt = 0
FROM hrs
You could try joining to this function:
CREATE FUNCTION ufn_Last24Hrs
(
#start DateTime2(7)
)
RETURNS #Result TABLE (d char(10), h int)
AS
BEGIN
DECLARE #current DateTime2(7) = #start
WHILE (#current > DateAdd(hour, -24, #start))
BEGIN
INSERT INTO #Result
VALUES
(
REPLACE(CONVERT(char(10), #current, 102) , '.', '-'),
DATEPART(hour, #current)
)
SET #current = DateAdd(hour, -1, #current)
END
RETURN;
END;
GO
SELECT * FROM ufn_Last24Hrs(SYSDATETIME());
SELECT
d,h,COUNT(1)cnt
FROM
ufn_Last24Hrs(SYSDATETIME()) hrs
left join msgs
ON msgs.d = hrs.d
and msgs.h = hrs.h
WHERE dt>= DateAdd(hour, -24, SYSDATETIME())
GROUP BY d,h
ORDER BY 1 DESC, 2 DES

SQL query for finding difference of two time and sum

I have this table in SQL Server:
UserId Date Start Time End Time
------------------------------------------------------------------------------------
20 2012-04-02 00:00:00.000 NULL 2012-04-02 09:17:57.000
20 2012-04-02 00:00:00.000 2012-04-02 09:17:57.000 2012-04-02 09:57:55.000
20 2012-04-02 00:00:00.000 2012-04-02 09:57:55.000 2012-04-02 10:04:58.000
20 2012-04-02 00:00:00.000 2012-04-02 10:04:58.000 2012-04-02 10:21:40.000
20 2012-04-02 00:00:00.000 2012-04-02 10:22:15.000 2012-04-02 10:22:20.000
20 2012-04-02 00:00:00.000 2012-04-02 10:22:56.000 2012-04-02 10:23:33.000
I want to find the difference between start time and end time and sum up the difference hours based on date
Output required is:
UserID Time_Duration
-----------------------
20 1:20:20 (this is example not actuals)
Can anyone help me write a SQL query..
I have tried with the below query,
select sum(sub1.TotalSeconds / 3600) as [Hours], sum((sub1.TotalSeconds % 3600) / 60)
as [Minutes],
sum((sub1.TotalSeconds % 3600) % 60) as [Seconds],sub1.Date
from
(
SELECT
sub.UserID,
sub.Date,
sum(datepart(hour, sub.end_time-sub.start_time) * 3600) + sum(datepart(minute, sub.end_time-sub.start_time) * 60) +
sum(datepart(second, sub.end_time-sub.start_time)) as TotalSeconds
from.......
) AS sub
group by sub.UserID,sub.Date,sub.start_time,sub.end_time)
as sub1 group by sub1.Date;
I get the following result
Hours Minutes Seconds Date
3 347 515 2012-04-02 00:00:00.000
But i want to add the minutes if greater then 60 it has to hours as 3+1 hr and so on. Can any 1 help me where Im making mistake
Hi take a look at this:
SQLFIDDLE
It is NOT the most efficient and I believe it's ugly for a code. However put it across for you to see the steps. You may get an idea out of it. Mainly using arimethic operators to get the reusults. There are extra fields as I was using it mainly to show you the data flow. I am sure other approaches are much much efficient in terms of performance. You can try.
Query:
select q.userid, t.date,
q.h + round((q.m + (q.s/60))/60,0) as hh,
round((q.m + (q.s/60)) mod 60,0) as mm,
q.s mod 60 as ss
from
(select t.userid,t.date,
sum(t.hours) as h,
sum(t.minutes) as m,
sum(t.seconds) as s
from (select userid,date,
TIMEDIFF(EndTime, StartTime) as duration,
TIMESTAMPDIFF(hour,starttime,endtime) as hours,
TIMESTAMPDIFF(minute,starttime,endtime) mod 60 as minutes,
TIMESTAMPDIFF(second,starttime,endtime) mod 60 seconds
from datestimes) as t
) as q
;
Resutls
USERID DATE HH MM SS
20 April, 02 2012 00:00:00+0000 13 4 25
Updated Query only with one nested query
Infact you may achieve this with one nested query. Apology as this whole answer is MYSQL based. So hopefully you may take the logic out to implement that using SQL Server syntax :)
select t.userid, t.date,
(sum(t.hours) + round((sum(t.minutes) +
sum(t.seconds)/60)/60,0)) as h,
(round((sum(t.minutes) +
sum(t.seconds)/60) mod 60,0)) as m,
sum(t.seconds) mod 60 as s
from
(select userid, date,
TIMEDIFF(EndTime, StartTime) as duration,
TIMESTAMPDIFF(hour,starttime,endtime) as hours,
TIMESTAMPDIFF(minute,starttime,endtime) mod 60 as minutes,
TIMESTAMPDIFF(second,starttime,endtime) mod 60 seconds
from datestimes) as t
;
You can do this matematically:
Select
CAST(DATEDIFF(SECOND, StartDate, EndDate) / 3600 as VARCHAR) + ':' +
CAST((DATEDIFF(SECOND, StartDate, EndDate) % 3600) / 60 as VARCHAR) + ':' +
CAST(DATEDIFF(SECOND, StartDate, EndDate) % 60 as VARCHAR) as DateDifference
From YourTable
DATEDIFF function gives you difference in seconds between start and end time as integer value. Simple dividing that value with 3600, or 60, you've got hours, or minutes, and with % you've got remain values.
Declare #test table(start datetime,ende datetime)
insert into #test Values ('20120101 10:00','20120101 18:00')
insert into #test Values ('20120101 10:00','20120101 18:13')
insert into #test Values ('20120101 10:00','20120102 18:25')
insert into #test Values ('20120101 10:00','20120101 18:00')
insert into #test Values ('20120101 10:00','20120101 18:00')
Select
FLOOR(Cast(Dummy as Float)) as Days
,DATEPART(Hour,dummy) as Hours
,DATEPART(MINUTE,dummy) as Minutes
,DATEPART(Second,dummy) as Seconds
from
(
Select
DateAdd(second, SUM(DATEDIFF(SECOND,start,ende)),Cast(0 as Datetime)) as Dummy
from #test
) a

Resources