Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 
        
Home       Members    Calendar    Who's On


Add to briefcase 12»»

Determine time elapased by hour Expand / Collapse
Author
Message
Posted Wednesday, October 30, 2013 4:24 AM
SSC Journeyman

SSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC Journeyman

Group: General Forum Members
Last Login: Tuesday, April 22, 2014 4:55 PM
Points: 92, Visits: 265
Hi,

Wondering if anyone has an elegant solution to finding out how much time has elapsed for a specific hour.


declare @t as table (ptr int identity, START time, ENDD time)

insert into @t (START, ENDD)
SELECT '09:30', '10:30'
union all
SELECT '10:00', '11:00'
union all
SELECT '10:30', '11:30'

Taking the example table above, I am looking to output the amount of time elapsed for each hour block. So:

09 - 0.5
10 - 2
11 - 0.5

I can think of complicated and ugly ways using cursors and many lines of code, but I thought someone might have a more user friendly solution!

Many thanks for your time.

Matt

Post #1509705
Posted Wednesday, October 30, 2013 8:06 AM


SSChampion

SSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampion

Group: General Forum Members
Last Login: Today @ 2:05 PM
Points: 13,327, Visits: 12,820
m.dunster (10/30/2013)
Hi,

Wondering if anyone has an elegant solution to finding out how much time has elapsed for a specific hour.


declare @t as table (ptr int identity, START time, ENDD time)

insert into @t (START, ENDD)
SELECT '09:30', '10:30'
union all
SELECT '10:00', '11:00'
union all
SELECT '10:30', '11:30'

Taking the example table above, I am looking to output the amount of time elapsed for each hour block. So:

09 - 0.5
10 - 2
11 - 0.5

I can think of complicated and ugly ways using cursors and many lines of code, but I thought someone might have a more user friendly solution!

Many thanks for your time.

Matt



Thank you for providing ddl and sample data. I am confused by your expected output. Can you try to explain what you want? The business rules for the output is lost on me.


_______________________________________________________________

Need help? Help us help you.

Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

Need to split a string? Try Jeff Moden's splitter.

Cross Tabs and Pivots, Part 1 – Converting Rows to Columns
Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs
Understanding and Using APPLY (Part 1)
Understanding and Using APPLY (Part 2)
Post #1509795
Posted Wednesday, October 30, 2013 8:14 AM
SSC Journeyman

SSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC Journeyman

Group: General Forum Members
Last Login: Tuesday, April 22, 2014 4:55 PM
Points: 92, Visits: 265
Hi,

To explain further. Let us assume that table @t represents light bulbs being switched on and off. So the first light bulb is switched on at 9.30am and off at 10.30am, the second on at 10.00am and off at 11.00am, the third on at 10.30 am and off at 11.30am.

I need to know how long, within hour blocks, the total time expended for the sum of all light bulbs being on.

So in our example:

Between 9am to 10am, light bulb 1 was on for 0.5 hours, light bulb 2&3 were both off, so a total of half an hour (0.5)
Between 10am to 11am, light bulb 1 was on for 0.5 hours, light bulb 2 for an hour, and light bulb 3 on for 0.5 hours, so a total of 2 hours (2.0)
Between 11.00am to 12.00pm, light bulb 1 was off, light bulb 2 was off, hour, and light bulb 3 on for 0.5 hours, so a total of half an hour (0.5)
Post #1509799
Posted Wednesday, October 30, 2013 8:22 AM


SSCommitted

SSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommitted

Group: General Forum Members
Last Login: Today @ 2:58 PM
Points: 1,683, Visits: 19,607
Not quite a complete solution, it doesn't handle spanning midnight

WITH Hours(hrStart,hrEnd) AS (
SELECT CAST(hrStart AS TIME),CAST(hrEnd AS TIME)
FROM (
VALUES ('00:00','01:00'),('01:00','02:00'),('02:00','03:00'),('03:00','04:00'),
('04:00','05:00'),('05:00','06:00'),('06:00','07:00'),('07:00','08:00'),
('08:00','09:00'),('09:00','10:00'),('10:00','11:00'),('11:00','12:00'),
('12:00','13:00'),('13:00','14:00'),('14:00','15:00'),('15:00','16:00'),
('16:00','17:00'),('17:00','18:00'),('18:00','19:00'),('19:00','20:00'),
('20:00','21:00'),('21:00','22:00'),('22:00','23:00'),('23:00','00:00')) h(hrStart,hrEnd))
SELECT h.hrStart AS [Hour],
SUM(DATEDIFF(minute,CASE WHEN t.START < h.hrStart THEN h.hrStart ELSE t.START END,
CASE WHEN t.ENDD > h.hrEnd THEN h.hrEnd ELSE t.ENDD END)) AS minutes

FROM Hours h
INNER JOIN @t t ON t.ENDD > h.hrStart AND t.START < h.hrEnd
GROUP BY h.hrStart
ORDER BY h.hrStart;



____________________________________________________

How to get the best help on a forum

http://www.sqlservercentral.com/articles/Best+Practices/61537

Never approach a goat from the front, a horse from the rear, or a fool from any direction.
Post #1509801
Posted Wednesday, October 30, 2013 10:55 AM
SSC Journeyman

SSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC Journeyman

Group: General Forum Members
Last Login: Tuesday, April 22, 2014 4:55 PM
Points: 92, Visits: 265
Thanks Mark. I'll give it a go. The midnight issue shouldn't be a problem.
Post #1509876
Posted Wednesday, October 30, 2013 6:41 PM


Hall of Fame

Hall of FameHall of FameHall of FameHall of FameHall of FameHall of FameHall of FameHall of FameHall of Fame

Group: General Forum Members
Last Login: Yesterday @ 9:53 PM
Points: 3,438, Visits: 5,390
Another take (also not handling crossing midnight), with some sample data added.

declare @t as table (ptr int identity, START time, ENDD time)

insert into @t (START, ENDD)
SELECT '09:30', '10:30' union all
SELECT '10:00', '11:00' union all
SELECT '10:30', '11:30' union all
SELECT '09:30', '11:30' union all
SELECT '11:00', '11:30';

WITH Tally (n) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES (0),(0),(0),(0),(0),(0)) a(a)
CROSS JOIN (VALUES (0),(0),(0),(0)) b(b)
)
SELECT Hour=RIGHT(100+d.n-1,2) + ':00'
,LightBulbsShining=SUM(
CASE WHEN StartHr=c.n AND EndHr=c.n THEN DATEDIFF(minute, START, ENDD)/60.
WHEN StartHr=c.n AND EndHr<>c.n THEN DATEDIFF(minute, START, DATEADD(hour, 1, hr))/60.
ELSE DATEDIFF(minute, hr, ENDD)/60. END)
FROM @t a
CROSS APPLY
(
SELECT StartHr=DATEPART(hour, START), EndHr=DATEPART(hour, ENDD)
) b
CROSS APPLY
(
SELECT n, hr=RIGHT(100+n,2) + ':00'
FROM Tally
WHERE n BETWEEN StartHr AND EndHr
) c
RIGHT JOIN Tally d ON d.n-1 = c.n
GROUP BY RIGHT(100+d.n-1,2) + ':00';






My mantra: No loops! No CURSORs! No RBAR! Hoo-uh!

My thought question: Have you ever been told that your query runs too fast?

My advice:
INDEXing a poor-performing query is like putting sugar on cat food. Yeah, it probably tastes better but are you sure you want to eat it?
The path of least resistance can be a slippery slope. Take care that fixing your fixes of fixes doesn't snowball and end up costing you more than fixing the root cause would have in the first place.


Need to UNPIVOT? Why not CROSS APPLY VALUES instead?
Since random numbers are too important to be left to chance, let's generate some!
Learn to understand recursive CTEs by example.
Splitting strings based on patterns can be fast!
Post #1509992
Posted Thursday, October 31, 2013 7:04 PM
SSC Journeyman

SSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC JourneymanSSC Journeyman

Group: General Forum Members
Last Login: Tuesday, April 22, 2014 4:55 PM
Points: 92, Visits: 265
Hi Mark,

Your solution is good, but it has a limitation in that if a period start and end more than one hour apart, it doesn't get calculated.

Dwain: Yours, I can't really understand (my limitation), but there is something wrong, if you alter the first end time to 13:30, then it counts all the extra hours in the 10:00 slot.

Thanks for your efforts and help. I'll have to keep going until I beat it.

Matt
Post #1510471
Posted Friday, November 1, 2013 7:04 PM
Ten Centuries

Ten CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen Centuries

Group: General Forum Members
Last Login: Yesterday @ 10:50 PM
Points: 1,069, Visits: 1,232
Here is my shot at this using Mark-101232 derived table for times.

--use any database which has this numbers table.

declare @t as table (ptr int identity, START time, ENDD time)

insert into @t (START, ENDD)
SELECT '09:30', '10:00'
union all
SELECT '10:00', '11:00'
union all
SELECT '10:30', '11:30'
union all
SELECT '13:30', '15:30'
union all
SELECT '18:30', '19:00'
union all
SELECT '10:00', '10:30'
union all
SELECT '03:00', '08:30'
union all
SELECT '19:00', '19:45'

--uses number table
--http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/you-require-a-numbers-table.aspx

/**************************************
Most of this code is because of expanding the
times in the start and end. If times span more than
1 hour this will generate a new record for this.

*******************************************/
IF OBJECT_ID('tempdb..#LightHour','u') IS NOT NULL
DROP TABLE #LightHour

;WITH ExpandHours
AS
(
SELECT t.ptr
,e.RowNum
,StartTime = CAST(e.StartTime AS TIME)
,EndTime = CAST(e.EndTime AS TIME)
,OriginalStartTime = t.START
,OriginalEndTime = t.ENDD
FROM @t t
CROSS APPLY (
SELECT RowNum = ROW_NUMBER() OVER (PARTITION BY t.ptr ORDER BY prev.Number)
,DATEADD(hh, prev.Number, dateadd(hour, datediff(hour, 0, t.START), 0)) AS StartTime
,DATEADD(hh, curr.Number, dateadd(hour, datediff(hour, 0, t.START), 0)) AS EndTime
FROM dbo.Numbers curr
JOIN dbo.Numbers prev
ON curr.Number = prev.Number + 1
WHERE curr.Number <= DATEDIFF(hh, t.START, t.ENDD)
) e
),
Final
AS
(
SELECT ptr
,StartTime = CASE WHEN StartTime < OriginalStartTime
THEN OriginalStartTime
ELSE StartTime
END
,EndTime = CASE WHEN OriginalEndTime < EndTime
THEN OriginalEndTime
ELSE EndTime
END
FROM ExpandHours
UNION
SELECT ptr
,StartTime = (SELECT MAX(c.EndTime) FROM ExpandHours c WHERE i.ptr = c.ptr)
,EndTime = OriginalEndTime
FROM ExpandHours i
)
SELECT ptr
,Start = StartTime
,Endd = EndTime
INTO #LightHour
FROM Final
WHERE StartTime <> EndTime
UNION
--if under 1 hour then include here
SELECT *
FROM @t
WHERE DATEDIFF(mi, START, ENDD) < 60


IF OBJECT_ID('tempdb..#Final','u') IS NOT NULL
DROP TABLE #Final


SELECT LighHour =hrStart
,TotalTimeOn_MI = SUM(CASE WHEN t.Start >= hrStart AND t.Start < hrEnd
THEN DATEDIFF(mi, t.Start, t.Endd)
ELSE 0
END)
--,ptr
INTO #Final
FROM #LightHour t
CROSS APPLY
(
SELECT hrStart = CAST(hrStart AS TIME)
,hrEnd = CAST(hrEnd AS TIME)
FROM (
VALUES ('00:00','01:00'),('01:00','02:00'),('02:00','03:00'),('03:00','04:00'),
('04:00','05:00'),('05:00','06:00'),('06:00','07:00'),('07:00','08:00'),
('08:00','09:00'),('09:00','10:00'),('10:00','11:00'),('11:00','12:00'),
('12:00','13:00'),('13:00','14:00'),('14:00','15:00'),('15:00','16:00'),
('16:00','17:00'),('17:00','18:00'),('18:00','19:00'),('19:00','20:00'),
('20:00','21:00'),('21:00','22:00'),('22:00','23:00'),('23:00','24:00')) h(hrStart,hrEnd)
) h
GROUP BY h.hrStart
-- ,ptr

SELECT *
FROM #Final
WHERE TotalTimeOn_MI > 0




Post #1510833
Posted Friday, November 1, 2013 7:37 PM


SSCommitted

SSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommitted

Group: General Forum Members
Last Login: Today @ 6:17 AM
Points: 1,816, Visits: 5,913
Borrowing from Dwain's sample data and Tally, here is my submission:


--== Dwain's setup ==--

declare @t as table (ptr int identity, START time, ENDD time)

insert into @t (START, ENDD)
SELECT '09:30', '10:30' union all
SELECT '10:00', '11:00' union all
SELECT '10:30', '11:30' union all
SELECT '09:30', '11:30' union all
SELECT '11:00', '11:30';

WITH Tally (n) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES (0),(0),(0),(0),(0),(0)) a(a)
CROSS JOIN (VALUES (0),(0),(0),(0)) b(b)
)

--== And MM's solution ==--

select Tally.n as [Hour],SUM(calc.UpToENDD - calc.UpToSTART) as [TotalMinutes]
from Tally
left outer join @t as source
on Tally.n between datepart(hour,convert(datetime,source.START)) and datepart(hour,convert(datetime,source.ENDD))
outer apply (
select case when source.START is null then 0
when dateadd(hour,Tally.n,0)<convert(datetime,source.START) then datediff(minute,dateadd(hour,Tally.n,0),convert(datetime,source.START))
else 0
end as UpToSTART,
case when source.ENDD is null then 0
when Tally.n=datepart(hour,convert(datetime,source.ENDD)) then datediff(minute,dateadd(hour,Tally.n,0),convert(datetime,source.ENDD))
else 60
end as UpToENDD
) calc
group by Tally.n
order by Tally.n;




MM


  • MMGrid Addin
  • MMNose Addin


  • Forum Etiquette: How to post Reporting Services problems
  • Forum Etiquette: How to post data/code on a forum to get the best help - by Jeff Moden
  • How to Post Performance Problems - by Gail Shaw

  • Post #1510834
    Posted Friday, November 1, 2013 7:54 PM
    Ten Centuries

    Ten CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen Centuries

    Group: General Forum Members
    Last Login: Yesterday @ 10:50 PM
    Points: 1,069, Visits: 1,232
    Monster Maghoul thank you for sharing! This is awesome now I just need to understand what is going on here. This guy will perform well under load too!
    Post #1510835
    « Prev Topic | Next Topic »

    Add to briefcase 12»»

    Permissions Expand / Collapse