Need to calculate sum of overtime - sql

I have a table TEST
AccountName AccountIndex AccountID StartTime EndTime checkouttime
ABC 3 7 07:00:00.00 16:00:00.00 2016-07-22 17:03:00
ABC 3 7 07:00:00.00 16:00:00.00 2016-07-23 16:00:00
ABC 3 7 07:00:00.00 16:00:00.00 2016-07-25 17:04:00
I have to calculate the sum of overtime.
I am trying this
select name,accountid,case when (cast(CheckOutTime as time) < EndTime) then '-' else '' end +
convert(varchar(8),
dateadd(minute,
abs(
DATEDIFF(minute,
cast(CheckOutTime as time)
, EndTime)
)
,0)
,108) as Overtime
from test
And i am getting the o/p as
name accountid Overtime
ABC 7 01:03:00
ABC 7 00:00:00
ABC 7 01:04:00
I want to have the o/p like
name accountid Overtime
ABC 7 02:07:00
sum of overtime how to achieve that

select accountid,name,cast((totalseconds/3600) as varchar) + ':' + cast(((totalseconds%3600)/60) as varchar) as overtime
from
(
select accountid,name,
sum(Datediff(s,Endtime,cast(checkouttime as time))) as totalseconds
group by accountid,name
) t
Use the above query to calculate overtime

Declare #YourTable table (AccountName varchar(50),AccountIndex int,AccountID int,StartTime varchar(25),EndTime varchar(25), checkouttime datetime )
Insert into #YourTable values
('ABC',3,7,'07:00:00.00','16:00:00.00','2016-07-22 17:03:00'),
('ABC',3,7,'07:00:00.00','16:00:00.00','2016-07-23 16:00:00'),
('ABC',3,7,'07:00:00.00','16:00:00.00','2016-07-25 17:04:00')
Select AccountName
,AccountID
,OverTime = cast(DateAdd(MINUTE,sum(DateDiff(MINUTE,cast(EndTime as time),case when cast(EndTime as time)>cast(checkouttime as time) then cast(EndTime as time) else cast(checkouttime as time) end)),'00:00:00') as time)
From #YourTable
Group By AccountName,AccountID
Returns
AccountName AccountID OverTime
ABC 7 02:07:00.0000000
For a more readable one, you could use a CTE.
;with cteBase as (
Select AccountName
,AccountID
,EndTime =cast(EndTime as time)
,checkouttime=cast(checkouttime as time)
From #YourTable
)
Select AccountName
,AccountID
,OverTime = cast(DateAdd(MINUTE,sum(DateDiff(MINUTE,EndTime,case when EndTime>checkouttime then EndTime else checkouttime end)),'00:00:00') as time)
From cteBase
Group By AccountName,AccountID

SUM does not work against time data type.
you can group the data by name, accountID and use
SUM(DATEDIFF(ss, '00:00:00.000', [your overtime value]))
to find the total number of seconds, and finally convert the number of seconds back to time with DATEADD() function.
UPDATE:
select
acc_id,
name,
CONVERT(VARCHAR(20), DATEADD(ss, SUM(DATEDIFF(ss, '00:00:00.000', [your overtime value])), '00:00:00'), 114) as overtime
from [your table]
group by acc_id, name
If you expect the overtime may be more than 24 hours for one acc_id, then you can add another column to show the number of days (number of seconds / (3600 * 24))

It will also fulfill your requirement so please try with this as well: (It only works on 2012)
SELECT AccountName, AccountID,
CONCAT(t.tot/60, ':', t.tot%60,':00') orverTime
FROM (
SELECT AccountName, AccountID,
SUM(DATEDIFF(mi, CONCAT(SUBSTRING(
CAST(checkouttime AS VARCHAR(50)), 1, 11), ' ', EndTime),
checkouttime)) tot
FROM #tmp
GROUP BY AccountName, AccountID ) t

Related

distribute accumulated working hours through days

I have Date time when engine has started working and how long was it working. but sometimes it can work more than 24 Hours.
if it worked for 28 Hours on the starting date i will have record
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 08/02/2018 17:30 28
I need to to have record that will show that engine has worked for 10:30 in 07 and 17:30 in 08.
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 07/02/2018 00:00 10:30
obj-00123 07/02/2018 13:30 08/02/2018 17:30 17:30
or something like that. I don't have any idea how can i get this done. can you give me some clues. i dont ask for writing code if its not too easy.
thank you
This might do the trick for you
--Using CTE to show sample data
;WITH cteX( Name,started_working,Finished_working)
AS
(
SELECT
'obj-00123','07/02/2018 13:30','08/02/2018 17:30' UNION ALL
SELECT 'obj-00155','07/02/2018 15:00','07/02/2018 22:30'
)
SELECT
X.Name
, X.started_working
, X.Finished_working
, HoursWorked = CONVERT(VARCHAR(12), DATEADD(minute, DATEDIFF(minute, X.started_working, X.Finished_working), 0), 114)
FROM
(
SELECT
T1.Name
,T1.started_working
,Finished_working = DATEADD(SECOND,0,DATEADD(DAY, DATEDIFF(DAY,-1,T1.started_working),0)) -- Dummy finish time # Midnight
FROM
cteX T1
WHERE
DATEDIFF(DAY,T1.started_working,T1.Finished_working) <> 0 --Create a dummy finish time #Midnight when start and end not on same day
UNION ALL
SELECT
T2.Name
,started_working = CASE WHEN DATEDIFF(DAY,T2.started_working,T2.Finished_working) <> 0
THEN DATEADD(DAY, DATEDIFF(DAY, 0, T2.Finished_working), 0) --Start # Midnight
ELSE T2.started_working
END
,T2.Finished_working
FROM
cteX T2
) X
ORDER BY
X.Name, X.started_working
OUTPUT
Name started_working Finished_working HoursWorked
obj-00123 2018-07-02 13:30:00.000 2018-07-03 00:00:00.000 10:30:00:000
obj-00123 2018-08-02 00:00:00.000 2018-08-02 17:30:00.000 17:30:00:000
obj-00155 2018-07-02 15:00:00.000 2018-07-02 22:30:00.000 07:30:00:000
According to your sample data working hours may be more than several days. In this case you need to use tally table or recursive CTE. I have used recursive CTE since it's easier to handle result fields. Also there are two columns in result named started_working and started_working2. started_working is from your expected output, but I believe you need started_working2 column
declare #T as table (
Name varchar(100)
, started_working datetime
, finished_working datetime
--, hours_worked int
)
insert into #T
values
('obj-00123', '20180207 13:30', '20180208 17:30')
, ('obj-00123', '20180208 19:00', '20180209 05:00')
, ('obj-00123', '20180209 19:00', '20180209 22:00')
, ('obj-00123', '20180210 19:00', '20180213 22:00')
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name, started_working, finished_working
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name, started_working, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name, started_working
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
I'd approach the solution something like this:
WITH dynamic_twelths_of_hr_table(datetime2_value) AS
(
SELECT '2017-01-01'
UNION ALL
SELECT DATEADD(MINUTE, 5, datetime2_value)
FROM dynamic_twelths_of_hr_table
WHERE DATEADD(MINUTE, 5, datetime2_value) <= '2019-01-01'
)
,twelths_hr_table AS
(
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, datetime2_value), 0) AS date_value
,datetime2_value
FROM dynamic_twelths_of_hr_table
)
,modified_source_table AS
(
SELECT
name
,objectid
,engine_start
,ISNULL(engine_stop, GETDATE()) AS engine_stop
,IIF(engine_start IS NULL OR engine_stop IS NULL, 1, 0) AS is_still_running
FROM [YOUR_SOURCE_TABLE]
)
SELECT
name
,objectid
,is_still_running
,date_value
,(COUNT(datetime2_value)/12.0) AS hours_run_on_this_day
FROM
modified_source_table
LEFT JOIN
twelths_hr_table AS tht
ON (tht.datetime2_value BETWEEN engine_start AND engine_stop)
GROUP BY
name, objectid, is_still_running, date_value
ORDER BY
name, objectid, is_still_running, date_value
Note I haven't tested this code so please excuse any small syntax errors.
I've also baked in an assumption about the range of dates to be considered (these can be widened, or made dynamic based on when the query runs), and it has a 5 minute resolution (based on the fact that, at a glance, I could only see one value in the engine_stop column that didn't fall on a 5-minute threshold - so I assume sub-5-minute precision is not required).
Basically what it does is expand each engine row out into 5-minute windows (twelths of an hour), and then simply groups these by day and counts the number of windows per day during which the engine was running.
For currently-running engines, it will calculate how long it has run so far. I trust you can tweak the code to your exact requirements.
thank you to all. this worked perfectly. it needed slight polishing and recursion needed to be set to 0.
But creating view is a trouble with CTE.
create view mroobjectenginerowkinghoursdeclare as
declare #T as table (
Name nvarchar(100)
, OBJECTID varchar(50)
, started_working datetime
,STOPFROM datetime
,STARTDATE datetime
,STOPDATE datetime
,MODIFIEDDATETIME datetime
,START_STOP int
,STARTDESCRIPTION nvarchar(300)
,STOPDESCRIPTION nvarchar(300)
,wattage nvarchar (50)
,purpose nvarchar(300)
,location nvarchar(300)
,finished_working datetime
,oldDiff int
)
insert into #T
select
NAME
,OBJECTID
,STOPTO
,STOPFROM
,STARTDATE
,STOPDATE
,MODIFIEDDATETIME
,START_STOP
,STARTDESCRIPTION
,STOPDESCRIPTION
,wattage
,purpose
,location
,next_stopfrom
,diff
FROM [MicrosoftDynamicsAX].[dbo].[mroobjectengineworkinghours]
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location, finished_working,oldDiff
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
OPTION (MAXRECURSION 0);

SQL Server : DATEDIFF exclude certain time within the two dates

I'm trying to get the difference between two dates in minutes, is it possible for me to exclude lunch times from this calculation aka 13:30-14:00 purely within my SQL statement. What I'm currently doing is below:
Example row
SetupStart StartTime SetupTime
----------------------------------------------------------------
2017-01-23 12:56:42.000 2017-01-23 14:41:06.000 105
My current statement:
DATEDIFF(MINUTE, UserData.Orders.SetupStart, UserData.Orders.StartTime) AS[SetupTime]
EDIT:--------------
Here is my current Select statement
SELECT DISTINCT
TOP (100) PERCENT UserData.Resources.Name AS Resource, UserData.Orders.OrderNo, UserData.Orders.StringAttribute4 AS Customer, UserData.Orders.Product,
CEILING(UserData.Orders.OpNo10Quantity) AS OpNo10Quantity, UserData.Orders.NumericalAttribute12 AS Speed, UserData.Orders.SetupStart, UserData.Orders.StartTime,
UserData.Orders.EndTime, CASE ToggleAttribute1 WHEN 1 THEN 'Yes' ELSE 'No' END AS Packed, UserData.Orders.StringAttribute5 AS OrderInstructions, UserData.Orders.NextResource,
UserData.Orders.DatasetId, UserData.Orders_Dataset.name AS ScheduleName, UserData.Orders.ShowOnReport, dbo.tblPreactorExportFullv10.Length AS L,
dbo.tblPreactorExportFullv10.Thickness AS T, dbo.tblPreactorExportFullv10.fWidth AS W,
DATEDIFF(MINUTE, UserData.Orders.SetupStart, UserData.Orders.StartTime) AS [SAM SetupTime]
FROM UserData.Orders INNER JOIN
UserData.Resources ON UserData.Orders.Resource = UserData.Resources.ResourcesId AND UserData.Orders.Resource = UserData.Resources.ResourcesId INNER JOIN
UserData.Orders_Dataset ON UserData.Orders.DatasetId = UserData.Orders_Dataset.DatasetId AND UserData.Orders.DatasetId = UserData.Orders_Dataset.DatasetId INNER JOIN
dbo.tblPreactorExportFullv10 ON UserData.Orders.PartNo = dbo.tblPreactorExportFullv10.ProductCode
WHERE (UserData.Orders.DatasetId = 15) AND (UserData.Resources.Name = 'Moulder 6') AND (UserData.Orders.ShowOnReport = 1)
AND (UserData.Orders.OperationProgress <> 5)
ORDER BY UserData.Orders.SetupStart
I'm sure Gordon's answer will be faster, but another option is as follows:
(Edit: Just for fun, ran this on 20,000 records and returned in 238 milliseconds)
Declare #YourTable table (SetupStart datetime, StartTime datetime)
Insert Into #YourTable values
('2017-01-23 12:56:42.000','2017-01-23 14:41:06.000'),
('2017-01-23 15:00:00.000','2017-01-23 18:30:00.000'), -- No Lunch
('2017-01-23 23:51:00.000','2017-01-23 23:53:46.000'), -- Anomoly mentioned
('2017-01-23 13:15:00.000','2017-01-23 13:45:00.000') -- Started After Lunch
Select A.*
,B.*
From ( ... your complex query here ... ) A
Cross Apply (
Select SetupTime = count(*)
From (Select Top (DateDiff(MINUTE,A.SetupStart,A.StartTime)) T=cast(DateAdd(MINUTE,Row_Number() Over (Order By Number)-1,A.SetupStart) as time)
From master..spt_values ) S
Where (cast(A.SetupStart as time)<'13:30' and cast(A.StartTime as time)>'14:00' and T not between '13:30' and '14:00')
or (cast(A.SetupStart as time) > '13:30' )
or (cast(A.StartTime as time) < '14:00' )
) B
Returns
SetupStart StartTime SetupTime
2017-01-23 12:56:42.000 2017-01-23 14:41:06.000 75
2017-01-23 15:00:00.000 2017-01-23 18:30:00.000 210
2017-01-23 23:51:00.000 2017-01-23 23:53:46.000 2
2017-01-23 13:15:00.000 2017-01-23 13:45:00.000 30
declare
#ldt_from time = '13:00'
,#ldt_to time = '14:00';
-- dummy data prepare
;with [my_setups] as
(
select [SetupStart] = cast('2017-01-23 13:56:42.000' as datetime), [StartTime] = cast('2017-01-23 14:41:06.000' as datetime)
union all
select [SetupStart] = cast('2017-01-23 12:45:00.000' as datetime), [StartTime] = cast('2017-01-23 12:46:00.000' as datetime)
)
-- end dummy data prepare
select [minutes] =
datediff(mi, [SetupStart], [StartTime])
- datediff(day, [SetupStart], [StartTime]) * (datediff(mi, #ldt_from, #ldt_to))
+ case when cast([SetupStart] as time) between #ldt_from and #ldt_to or cast([StartTime] as time) between #ldt_from and #ldt_to then
datediff
(
mi
,case when cast([SetupStart] as time) > #ldt_from then cast([SetupStart] as time) else #ldt_from end
,case when cast([StartTime] as time) < #ldt_to then cast([StartTime] as time) else #ldt_to end
)
else 0 end
from
[my_setups]
This is a pain in SQL Server, but you can do it:
(CASE WHEN CAST(o.SetupStart as time) > '14:00:00' OR
CAST(o.StartTime as time) < '13:30:00'
THEN DATEDIFF(MINUTE, o.SetupStart, o.StartTime)
WHEN CAST(o.SetupStart as time) >= '13:30:00' AND
CAST(o.StartTime as time) <= '14:00:00'
THEN 0
WHEN CAST(o.SetupStart as time) >= '13:30:00'
THEN DATEDIFF(MINUTE,
CAST(CAST(o.SetupStart as DATE) as DATETIME) + CAST('14:00:00' as TIME),
o.StartTime
)
WHEN CAST(o.StartTime as Time) <= '14:00:00'
THEN DATEDIFF(MINUTE,
o.SetStart,
CAST(CAST(o.StartTime as DATE) as DATETIME) + CAST('13:30:00' as TIME)
)
ELSE DATEDIFF(MINUTE, o.SetupStart, o.StartTime) - 30
END) AS [SetupTime]
This handles the following cases:
Times are both before or after lunch.
Times are both during lunch.
SetupStart is during lunch.
StartTime is during lunch.
SetupStart is before lunch and StartTime is after lunch
The easiest way is what #Shakeer Hussain recommended
DATEDIFF(MINUTE, UserData.Orders.SetupStart, UserData.Orders.StartTime) AS[SetupTime]
BECOMES
DATEDIFF(MINUTE, UserData.Orders.SetupStart, DATEADD(MINUTE,-30,(UserData.Orders.StartTime))) AS[SetupTime]
Additionally, you could do two DATEDIFF functions and add them together:
DATEDIFF(MINUTE, UserData.Orders.SetupStart, UserData.Orders.LunchStartTime)
+ DATEDIFF(MINUTE, UserData.Orders.LunchEndTime, UserData.Orders.StartTime) AS[SetupTime]
If these data points don't exist, you can create a temp table.

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

How to find the total playing time per day for all the users in my sql server database

I have a table which contains following columns
userid,
game,
gameStarttime datetime,
gameEndtime datetime,
startdate datetime,
currentdate datetime
I can retrieve all the playing times but I want to count the total playing time per DAY and 0 or null if game not played on a specific day.
Take a look at DATEDIFF to do the time calculations. Your requirements are not very clear, but it should work for whatever you're looking to do.
Your end result would probably look something like this:
SELECT
userid,
game,
DATEDIFF(SS, gameStarttime, gameEndtime) AS [TotalSeconds]
FROM [source]
GROUP BY
userid,
game
In the example query above, the SS counts the seconds between the 2 dates (assuming both are not null). If you need just minutes, then MI will provide the total minutes. However, I imagine total seconds is best so that you can convert to whatever unit of measure you need accurate, such as hours that might be "1.23" or something like that.
Again, most of this is speculation based on assumptions and what you seem to be looking for. Hope that helps.
MSDN Docs for DATEDIFF: https://msdn.microsoft.com/en-us/library/ms189794.aspx
You may also look up DATEPART if you want the minutes and seconds separately.
UPDATED BASED ON FEEDBACK
The query below breaks out the hour breakdowns by day, splits time across multiple days, and shows "0" for days where no games are played. Also, for your output, I have to assume you have a separate table of users (so you can show users who have no time in your date range).
-- Define start date
DECLARE #BeginDate DATE = '4/21/2015'
-- Create sample data
DECLARE #Usage TABLE (
userid int,
game nvarchar(50),
gameStartTime datetime,
gameEndTime datetime
)
DECLARE #Users TABLE (
userid int
)
INSERT #Users VALUES (1)
INSERT #Usage VALUES
(1, 'sample', '4/25/2015 10pm', '4/26/2015 2:30am'),
(1, 'sample', '4/22/2015 4pm', '4/22/2015 4:30pm')
-- Generate list of days in range
DECLARE #DayCount INT = DATEDIFF(DD, #BeginDate, GETDATE()) + 1
;WITH CTE AS (
SELECT TOP (225) [object_id] FROM sys.all_objects
), [Days] AS (
SELECT TOP (#DayCount)
DATEADD(DD, ROW_NUMBER() OVER (ORDER BY x.[object_id]) - 1, #BeginDate) AS [Day]
FROM CTE x
CROSS JOIN CTE y
ORDER BY
[Day]
)
SELECT
[Days].[Day],
Users.userid,
SUM(COALESCE(CONVERT(MONEY, DATEDIFF(SS, CASE WHEN CONVERT(DATE, Usage.gameStartTime) < [Day] THEN [Day] ELSE Usage.gameStartTime END,
CASE WHEN CONVERT(DATE, Usage.gameEndTime) > [Day] THEN DATEADD(DD, 1, [Days].[Day]) ELSE Usage.gameEndTime END)) / 3600, 0)) AS [Hours]
FROM [Days]
CROSS JOIN #Users Users
LEFT OUTER JOIN #Usage Usage
ON Usage.userid = Users.userid
AND [Days].[Day] BETWEEN CONVERT(DATE, Usage.gameStartTime) AND CONVERT(DATE, Usage.gameEndTime)
GROUP BY
[Days].[Day],
Users.userid
The query above yields the output below for the sample data:
Day userid Hours
---------- ----------- ---------------------
2015-04-21 1 0.00
2015-04-22 1 0.50
2015-04-23 1 0.00
2015-04-24 1 0.00
2015-04-25 1 2.00
2015-04-26 1 2.50
2015-04-27 1 0.00
I've edited my sql on sql fiddle and I think this might get you what you asked for. to me it looks a little more simple then the answer you've accepted.
DECLARE #FromDate datetime, #ToDate datetime
SELECT #Fromdate = MIN(StartDate), #ToDate = MAX(currentDate)
FROM Games
-- This recursive CTE will get you all dates
-- between the first StartDate and the last CurrentDate on your table
;WITH AllDates AS(
SELECT #Fromdate As TheDate
UNION ALL
SELECT TheDate + 1
FROM AllDates
WHERE TheDate + 1 <= #ToDate
)
SELECT UserId,
TheDate,
COALESCE(
SUM(
-- When the game starts and ends in the same date
CASE WHEN DATEDIFF(DAY, GameStartTime, GameEndTime) = 0 THEN
DATEDIFF(HOUR, GameStartTime, GameEndTime)
ELSE
-- when the game starts in the current date
CASE WHEN DATEDIFF(DAY, GameStartTime, TheDate) = 0 THEN
DATEDIFF(HOUR, GameStartTime, DATEADD(Day, 1, TheDate))
ELSE -- meaning the game ends in the current date
DATEDIFF(HOUR, TheDate, GameEndTime)
END
END
),
0) As HoursPerDay
FROM (
SELECT DISTINCT UserId,
TheDate,
CASE
WHEN CAST(GameStartTime as Date) = TheDate
THEN GameStartTime
ELSE NULL
END As GameStartTime, -- return null if no game started that day
CASE
WHEN CAST(GameEndTime as Date) = TheDate
THEN GameEndTime
ELSE NULL
END As GameEndTime -- return null if no game ended that day
FROM Games CROSS APPLY AllDates -- This is where the magic happens :-)
) InnerSelect
GROUP BY UserId, TheDate
ORDER BY UserId, TheDate
OPTION (MAXRECURSION 0)
Play with it your self on sql fiddle.

DateDiff to output hours and minutes

my code gives TOTAL HOURS in hours, but i am trying to output something like
TotalHours
8:36
where 8 represents hour part and 36 represents minutes part mean totalHours a person has worked in a single day at office.
with times as (
SELECT t1.EmplID
, t3.EmplName
, min(t1.RecTime) AS InTime
, max(t2.RecTime) AS [TimeOut]
, t1.RecDate AS [DateVisited]
FROM AtdRecord t1
INNER JOIN
AtdRecord t2
ON t1.EmplID = t2.EmplID
AND t1.RecDate = t2.RecDate
AND t1.RecTime < t2.RecTime
inner join
HrEmployee t3
ON t3.EmplID = t1.EmplID
group by
t1.EmplID
, t3.EmplName
, t1.RecDate
)
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, DATEDIFF(Hour,InTime, [TimeOut]) TotalHours
from times
Order By EmplID, DateVisited
Very simply:
CONVERT(TIME,Date2 - Date1)
For example:
Declare #Date2 DATETIME = '2016-01-01 10:01:10.022'
Declare #Date1 DATETIME = '2016-01-01 10:00:00.000'
Select CONVERT(TIME,#Date2 - #Date1) as ElapsedTime
Yelds:
ElapsedTime
----------------
00:01:10.0233333
(1 row(s) affected)
Try this query
select
*,
Days = datediff(dd,0,DateDif),
Hours = datepart(hour,DateDif),
Minutes = datepart(minute,DateDif),
Seconds = datepart(second,DateDif),
MS = datepart(ms,DateDif)
from
(select
DateDif = EndDate-StartDate,
aa.*
from
( -- Test Data
Select
StartDate = convert(datetime,'20090213 02:44:37.923'),
EndDate = convert(datetime,'20090715 13:24:45.837')) aa
) a
Output
DateDif StartDate EndDate Days Hours Minutes Seconds MS
----------------------- ----------------------- ----------------------- ---- ----- ------- ------- ---
1900-06-02 10:40:07.913 2009-02-13 02:44:37.923 2009-07-15 13:24:45.837 152 10 40 7 913
(1 row(s) affected)
Small change like this can be done
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, CASE WHEN minpart=0
THEN CAST(hourpart as nvarchar(200))+':00'
ELSE CAST((hourpart-1) as nvarchar(200))+':'+ CAST(minpart as nvarchar(200))END as 'total time'
FROM
(
SELECT EmplID, EmplName, InTime, [TimeOut], [DateVisited],
DATEDIFF(Hour,InTime, [TimeOut]) as hourpart,
DATEDIFF(minute,InTime, [TimeOut])%60 as minpart
from times) source
I would make your final select as:
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, CONVERT(varchar(3),DATEDIFF(minute,InTime, TimeOut)/60) + ':' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(minute,InTime,TimeOut)%60),2)
as TotalHours
from times
Order By EmplID, DateVisited
Any solution trying to use DATEDIFF(hour,... is bound to be complicated (if it's correct) because DATEDIFF counts transitions - DATEDIFF(hour,...09:59',...10:01') will return 1 because of the transition of the hour from 9 to 10. So I'm just using DATEDIFF on minutes.
The above can still be subtly wrong if seconds are involved (it can slightly overcount because its counting minute transitions) so if you need second or millisecond accuracy you need to adjust the DATEDIFF to use those units and then apply suitable division constants (as per the hours one above) to just return hours and minutes.
Just change the
DATEDIFF(Hour,InTime, [TimeOut]) TotalHours
part to
CONCAT((DATEDIFF(Minute,InTime,[TimeOut])/60),':',
(DATEDIFF(Minute,InTime,[TimeOut])%60)) TotalHours
The /60 gives you hours, the %60 gives you the remaining minutes, and CONCAT lets you put a colon between them.
I know it's an old question, but I came across it and thought it might help if someone else comes across it.
Divide the Datediff in MS by the number of ms in a day, cast to Datetime, and then to time:
Declare #D1 datetime = '2015-10-21 14:06:22.780', #D2 datetime = '2015-10-21 14:16:16.893'
Select Convert(time,Convert(Datetime, Datediff(ms,#d1, #d2) / 86400000.0))
If you want 08:30 ( HH:MM) format then try this,
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, RIGHT('0' + CONVERT(varchar(3),DATEDIFF(minute,InTime, TimeOut)/60),2) + ':' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(minute,InTime,TimeOut)%60),2)
as TotalHours from times Order By EmplID, DateVisited
Please put your related value and try this :
declare #x int, #y varchar(200),
#dt1 smalldatetime = '2014-01-21 10:00:00',
#dt2 smalldatetime = getdate()
set #x = datediff (HOUR, #dt1, #dt2)
set #y = #x * 60 - DATEDIFF(minute,#dt1, #dt2)
set #y = cast(#x as varchar(200)) + ':' + #y
Select #y
Difference Two Time in [hh:mm:ss]
select FORMAT((CONVERT(datetime,'2021-12-01 19:24:40') - CONVERT(datetime,'2021-12-01 17:00:00')),'hh:mm:ss')DffTime
this would hep you
DECLARE #DATE1 datetime = '2014-01-22 9:07:58.923'
DECLARE #DATE2 datetime = '2014-01-22 10:20:58.923'
SELECT DATEDIFF(HOUR, #DATE1,#DATE2) ,
DATEDIFF(MINUTE, #DATE1,#DATE2) - (DATEDIFF(HOUR,#DATE1,#DATE2)*60)
SELECT CAST(DATEDIFF(HOUR, #DATE1,#DATE2) AS nvarchar(200)) +
':'+ CAST(DATEDIFF(MINUTE, #DATE1,#DATE2) -
(DATEDIFF(HOUR,#DATE1,#DATE2)*60) AS nvarchar(200))
As TotalHours
Since any DateTime can be cast to a float, and the decimal part of the number represent the time itself:
DECLARE #date DATETIME = GETDATE()
SELECT CAST(CAST(#date AS FLOAT) - FLOOR(CAST(#date AS FLOAT)) AS DATETIME
This will result a datetime like '1900-01-01 hour of the day' you can cast it as time, timestamp or even use convert to get the formatted time.
I guess this works in any version of SQL since cast a datetime to float is compatible since version 2005.
Hope it helps.
In case someone is still searching for a query to display the difference in hr min and sec format:
(This will display the difference in this format: 2 hr 20 min 22 secs)
SELECT
CAST(DATEDIFF(minute, StartDateTime, EndDateTime)/ 60 as nvarchar(20)) + ' hrs ' + CAST(DATEDIFF(second, StartDateTime, EndDateTime)/60 as nvarchar(20)) + ' mins' + CAST(DATEDIFF(second, StartDateTime, EndDateTime)% 60 as nvarchar(20)) + ' secs'
OR can be in the format as in the question:
CAST(DATEDIFF(minute, StartDateTime, EndDateTime)/ 60 as nvarchar(20)) + ':' + CAST(DATEDIFF(second, StartDateTime, EndDateTime)/60 as nvarchar(20))
Sharing a variant that works for more than 24 hours.
DECLARE #sd DATETIME = CONVERT(DATETIME, '12/07/2022 11:10:00', 103)
DECLARE #ed DATETIME = CONVERT(DATETIME, '15/07/2022 13:20:05', 103)
Select Concat (
DATEDIFF(DAY, #sd, #ed), 'd ',
DATEPART(Hour, CONVERT(Time,#ed - #sd)), 'h ',
DATEPART(Minute, CONVERT(Time,#ed - #sd)), 'm ',
DATEPART(Second, CONVERT(Time,#ed - #sd)), 's'
)
Output:
3d 2h 10m 5s
For people that has MySql version < 5.6 as me they don't have TIMESTAMPDIFF so, I wrote MYTSDIFF a function that accepts %s (%m or %i) for minutes %h flags to get the difference in seconds, minutes and hours between 2 timestamps.
Enjoy!
DROP FUNCTION IF EXISTS MYTSDIFF;
DELIMITER $$
CREATE FUNCTION `MYTSDIFF`( date1 timestamp, date2 timestamp, fmt varchar(20))
returns varchar(20) DETERMINISTIC
BEGIN
declare secs smallint(2);
declare mins smallint(2);
declare hours int;
declare total real default 0;
declare str_total varchar(20);
select cast( time_format( timediff(date1, date2), '%s') as signed) into secs;
select cast( time_format( timediff(date1, date2), '%i') as signed) into mins;
select cast( time_format( timediff(date1, date2), '%H') as signed) into hours;
set total = hours * 3600 + mins * 60 + secs;
set fmt = LOWER( fmt);
if fmt = '%m' or fmt = '%i' then
set total = total / 60;
elseif fmt = '%h' then
set total = total / 3600;
else
/* Do nothing, %s is the default: */
set total = total + 0;
end if;
select cast( total as char(20)) into str_total;
return str_total;
END$$
DELIMITER ;
No need to jump through hoops. Subtracting Start from End essentially gives you the timespan
(combining Vignesh Kumar's and Carl Nitzsche's answers) :
SELECT *,
--as a time object
TotalHours = CONVERT(time, EndDate - StartDate),
--as a formatted string
TotalHoursText = CONVERT(varchar(20), EndDate - StartDate, 114)
FROM (
--some test values (across days, but OP only cares about the time, not date)
SELECT
StartDate = CONVERT(datetime,'20090213 02:44:37.923'),
EndDate = CONVERT(datetime,'20090715 13:24:45.837')
) t
Ouput
StartDate EndDate TotalHours TotalHoursText
----------------------- ----------------------- ---------------- --------------------
2009-02-13 02:44:37.923 2009-07-15 13:24:45.837 10:40:07.9130000 10:40:07:913
See the full cast and convert options here:
https://msdn.microsoft.com/en-us/library/ms187928.aspx