following code returns
CREATE TABLE #emp1
(
empid VARCHAR(50),
empname VARCHAR(50),
intime SMALLDATETIME,
outtime SMALLDATETIME
)
INSERT INTO #emp1
VALUES (2500,
'Arun',
'2014-01-01 09:00:00',
'2014-01-01 10:30:0'),
(2500,
'Arun',
'2014-01-01 11:00:00:00',
'2014-01-01 11:00:0'),
(2500,
'Arun',
'2014-01-01 11:30:00:00',
'2014-01-01 19:00:00')
SELECT empid,
Cast(intime AS DATE)
AS workingday,
empname,
RIGHT('0' + CONVERT(VARCHAR, Floor(Sum( Datediff( mi, intime, outtime)/
60.0))),
2)
+ ':'
+ RIGHT('0' + CONVERT(VARCHAR, Sum( Datediff( mi, intime, outtime))%60),
2) AS
[total work_hour(s)],
CONVERT(VARCHAR(5), Max(outtime) - Min(intime), 108)
AS Difference,
Cast(Dateadd(minute, Sum(Datediff(mi, intime, outtime)) - 45, 0) AS TIME)
AS
TotalTime,
Cast(Dateadd(minute, CASE
WHEN Sum(Datediff(mi, intime, outtime)) > 525 THEN
Sum(
Datediff(mi, intime, outtime)) - 525
ELSE 0
END, 0) AS TIME)
AS OverTime
FROM #emp1
GROUP BY empid,
empname,
Cast(intime AS DATE)
DROP TABLE #emp1
as
EmpId workingday EmpName total work_hour(s) Difference TotalTime OverTime
2500 2014-01-01 Arun 09:00 10:00 08:15:00.0000000 00:15:00.0000000
Here TotalTime is 08:15 and OverTime is 00:15. Everything is fine.
However, I need another field ActualTime
i.e. if TotalTime is greater than 8 hours then ActualTime is the difference of TotalTime and OverTime. In this example ActualTime should be 08:00.
If TotalTime is less than 8 hours then ActualTime is the same as TotalTime.
Finally, I also want to avoid excess zeros in TotalTime and OverTime
I imagine you want something like this: *EDIT nullcheck added
;with x as
(
SELECT empid,
Cast(intime AS DATE) workingday,
empname,
cast(dateadd(mi, sum(datediff(mi, 0, outtime - intime)), 0) as time) twh,
cast(dateadd(mi,datediff(mi,0,max(outtime)-min(intime)), 0) as time) Difference,
case when count(outtime)=count(*) then '' end nullcheck
FROM #emp1
GROUP BY empid, empname, Cast(intime AS DATE)
)
select empid, workingday, empname,
nullcheck+left(twh, 5) [total_hours],
nullcheck+left(Difference, 5) Difference,
nullcheck+left(cast(dateadd(mi, -45, twh)as time ),5) TotalTime,
nullcheck+left(case when twh > '08:45' then cast(dateadd(mi, -525, twh) as time) else '00:00' end, 5) OverTime,
nullcheck+left(case when twh > '08:00' then '08:00' else twh end, 5) actualtime
from x
Result:
empid workingday empname total_hours Difference TotalTime OverTime actualtime
2500 2014-01-01 Arun 09:00 10:00 08:15 00:15 08:00
First I'd wrap your entire current query in a Common Table Expression, so that the parts of it can be reused.
WITH CalculatedTime AS (SELECT empid,
Cast(intime AS DATE) AS workingday,
empname,
RIGHT('0' + CONVERT(VARCHAR, Floor(Sum( Datediff( mi, intime, outtime)/
60.0))),
2)
+ ':'
+ RIGHT('0' + CONVERT(VARCHAR, Sum( Datediff( mi, intime, outtime))%60),
2) AS [total work_hour(s)],
CONVERT(VARCHAR(5), Max(outtime) - Min(intime), 108)
AS Difference,
Cast(Dateadd(minute, Sum(Datediff(mi, intime, outtime)) - 45, 0) AS TIME)
TotalTime,
Cast(Dateadd(minute, CASE
WHEN Sum(Datediff(mi, intime, outtime)) > 525 THEN
Sum(
Datediff(mi, intime, outtime)) - 525
ELSE 0
END, 0) AS TIME)
OverTime
FROM #emp1
GROUP BY empid,
empname,
Cast(intime AS DATE) )
Now we can refer to CalculatedTime as a table in itself, and we don't need to repeat any of that nasty logic used for calculating the TotalTime column.
So the final step is to use a case statement to perform the calculation you are after:
SELECT *,
CASE WHEN DATEDIFF(minute,TotalTime,'08:00') > 0 THEN TotalTime
ELSE DATEADD(minute, DATEDIFF(minute,OverTime, 0), TotalTime)
END AS ActualTime
FROM CalculatedTime
The formatting part of the problem (remove the extra zeros) is a separate question that has been answered many times before on StackOverflow.
Related
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
I'm trying to sum up some time values, and when my sum exceeds 24 hours it subtracts 24 from the total.
Here's my SQL:
SELECT
UserID,
First_NM,
Last_NM,
MgrName,
CONVERT(varchar(5), DATEADD(minute, SUM(OnProd)/60, 0), 114) as OnProd,
CONVERT(varchar(5), DATEADD(minute, SUM(OffProd)/60, 0), 114) as OffProd,
CONVERT(varchar(5), DATEADD(minute, SUM(OnProd+OffProd)/60, 0), 114) as SumProd
FROM (
SELECT
UserID,
First_NM,
Last_NM,
MgrName,
CASE WHEN WorkUnitType LIKE '%On%' THEN DATEDIFF(s, DateStart, DateEnd) ELSE 0 END AS OnProd,
CASE WHEN WorkUnitType LIKE '%Off%' THEN DATEDIFF(s, DateStart, DateEnd) ELSE 0 END AS OffProd
FROM dbo.vw_mos_DPL_Reporting
) a
Group By
UserID,
First_NM,
Last_NM,
MgrName
And here's my current output:
UserID First_NM Last_NM OnProd OffProd SumProd
A05878 STEVEN LANTE 22:28 09:55 08:23
ARGLIN ARNIE FREEL 00:00 00:00 00:00
B96695 JENNIFER LARK 21:32 18:49 16:21
PATYOI PATTY TROTTER 04:29 00:03 04:32
How would I go about fixing this? Just to be clear, I want to show something like 38:24 or whatever the actual sum is. Total hours:Total minutes/60
It's a little inelegant, but you might try calculating the minutes and the hours separately and concatenating, eg, instead of
CONVERT(varchar(5), DATEADD(minute, SUM(OnProd)/60, 0), 114) as OnProd
try
SELECT CAST(SUM(OnProd)/3600 AS VARCHAR(6)) + ':' + RIGHT('00' + CAST((SUM(OnProd) % 3600) / 60 AS VARCHAR(6)),2) AS OnProd
You have to do the same thing for OffProd and SumProd as well.
In SQL Server, I have a start time column on a table such as:
2011-09-18 08:06:36.000
2011-09-19 05:42:16.000
2011-09-20 08:02:26.000
2011-09-21 08:37:24.000
2011-09-22 08:22:20.000
2011-09-23 11:58:27.000
2011-09-24 09:00:48.000
2011-09-25 06:51:34.000
2011-09-26 06:09:05.000
2011-09-27 08:25:26.000
...
My question is, how can I get the average hour and minute? I want to know that what is the average start time for this job. (for example 07:22)
I tried something like this but didn't work:
select CAST(AVG(CAST(DATEPART(HH, START_TIME)AS float)) AS datetime) FROM
Thanks.
declare #T table(StartTime datetime)
insert into #T values
('2011-09-18 08:06:36.000'),
('2011-09-19 05:42:16.000'),
('2011-09-20 08:02:26.000'),
('2011-09-21 08:37:24.000'),
('2011-09-22 08:22:20.000'),
('2011-09-23 11:58:27.000'),
('2011-09-24 09:00:48.000'),
('2011-09-25 06:51:34.000'),
('2011-09-26 06:09:05.000'),
('2011-09-27 08:25:26.000')
;with C(Sec) as
(
select dateadd(second, avg(datediff(second, dateadd(day, datediff(day, 0, StartTime), 0), StartTime)), 0)
from #T
)
select convert(char(5), dateadd(minute, case when datepart(second, C.Sec) >= 30 then 1 else 0 end, C.Sec), 108)
from C
-----
08:08
Try this :
select CAST((SUM(DATEPART(HH, START_TIME) * 60 + DATEPART(MI, START_TIME))/COUNT(*))/60 AS VARCHAR(10)) + ':' + CAST((SUM(DATEPART(HH, START_TIME) * 60 + DATEPART(MI, START_TIME))/COUNT(*))%60 AS VARCHAR(10))
FROM.....
Using SQL Server 2005
Table1
ID Intime Outtime
001 00.21.00 00.48.00
002 08.23.00 13.45.00
003 00.34.00 00.18.00
I need to display the time time like 30 minutes or 1 Hours, it should display a roundoff time
Expected Output
ID Intime Outtime
001 00.30.00 01.00.00
002 08.30.00 14.00.00
003 01.00.00 00.30.00
How to make a query for the roundoff time.
You can round the current date to 30 minutes like:
select dateadd(mi, datediff(mi,0,getdate())/30*30, 0)
Explanation: this takes the number of minutes since the 0-date:
datediff(mi,0,getdate())
Then it rounds that to a multiple of 30 by dividing and multiplying by 30:
datediff(mi,0,getdate())/30*30
The result is added back to the 0-date to find the last 30 minute block
dateadd(mi, datediff(mi,0,getdate())/30*30, 0)
This can be adjusted easily for 60 minutes. :)
By checking the range
select ID,
DateAdd(mi, DateDiff(mi, 0, Intime +
case when InMi >= 15 then 30 - InMi else - InMi end), 0) as Intime,
DateAdd(mi, DateDiff(mi, 0, Outtime +
case when OutMi >= 15 then 30 - OutMi else - OutMi end), 0) as Outtime
FROM
(
select ID, Intime, Outtime,
datepart(mi, InTime) % 30 InMi,
datepart(mi, Outtime) % 30 OutMi
from tbl
) X
or by using the classical trick equivalent to Int(x+0.5)..
select ID,
dateadd(mi, ((datediff(mi, 0, Intime)+15)/30)*30, 0) Intime,
dateadd(mi, ((datediff(mi, 0, Outtime)+15)/30)*30, 0) Outtime
from tbl
IF you want to ROUNDUP instead
(you have a value going from 00.34.00 to 01.00.00) Then you need this
select ID,
dateadd(mi, ((datediff(mi, 0, Intime)+29)/30)*30, 0) Intime,
dateadd(mi, ((datediff(mi, 0, Outtime)+29)/30)*30, 0) Outtime
from tbl
Take a look at the DATEDIFF, DATEADD and DATEPART. You should be able to do what you want with that.
http://msdn.microsoft.com/en-us/library/ms189794.aspx
http://msdn.microsoft.com/en-us/library/ms186819.aspx
http://msdn.microsoft.com/en-us/library/ms174420.aspx
Here is kind of a step-by-step routine. I'm sure you can do something shorter and even more efficient. It would also simplify a lot if you used a datetime data type instead of a string.
declare #T table (id char(3), intime char(8), outtime char(8))
insert into #T values ('001', '00.21.00', '00.48.00')
insert into #T values ('002', '08.23.00', '13.45.00')
insert into #T values ('003', '00.34.00', '00.18.00')
;with
cteTime(id, intime, outtime)
as
( -- Convert to datetime
select
id,
cast(replace(intime, '.', ':') as datetime),
cast(replace(outtime, '.', ':') as datetime)
from #T
),
cteMinute(id, intime, outtime)
as
( -- Get the minute part
select
id,
datepart(mi, intime),
datepart(mi, outtime)
from cteTime
),
cteMinuteDiff(id, intime, outtime)
as
( -- Calcualte the desired diff
select
id,
case when intime > 30 then (60 - intime) else (30 - intime) end,
case when outtime > 30 then (60 - outtime) else (30 - outtime) end
from cteMinute
),
cteRoundTime(id, intime, outtime)
as
( -- Get the rounded time
select
cteTime.id,
dateadd(mi, cteMinuteDiff.intime, cteTime.intime),
dateadd(mi, cteMinuteDiff.outtime, cteTime.outtime)
from cteMinuteDiff
inner join cteTime
on cteMinuteDiff.id = cteTime.id
),
cteRoundedTimeParts(id, inHour, inMinute, outHour, outMinute)
as
( -- Split the time into parts
select
id,
cast(datepart(hh, intime) as varchar(2)) as inHour,
cast(datepart(mi, intime) as varchar(2)) as inMinute,
cast(datepart(hh, outtime) as varchar(2)) as outHour,
cast(datepart(mi, outtime) as varchar(2)) as outMinute
from cteRoundTime
),
cteRoundedTime(id, intime, outtime)
as
( -- Build the time string representation
select
id,
right('00'+inHour, 2)+'.'+right('00'+inMinute, 2)+'.00',
right('00'+outHour, 2)+'.'+right('00'+outMinute, 2)+'.00'
from cteRoundedTimeParts
)
select *
from cteRoundedTime
Using SQL Server 2000:
SELECT PERSONID,
CARDEVENTDATE,
INTIME,
CASE
WHEN OUTTIME = INTIME THEN
'No PunchOut'
ELSE
OUTTIME
END AS OUTTIME,
CONVERT(char(8), CASE
WHEN DateAdd(Day, - DateDiff(Day, 0, OutTime), OutTime) > '18:00:00' THEN
Cast('18:00:00' AS datetime)
ELSE
DateAdd(Day, - DateDiff(Day, 0, OutTime), OutTime)
END - CASE
WHEN DateAdd(Day, - DateDiff(Day, 0, InTime), InTime) < '09:00:00' THEN
Cast('09:00:00' AS datetime)
ELSE
DateAdd(Day, - DateDiff(Day, 0, InTime), InTime)
END, 8) AS WorkTime
FROM (SELECT T_PERSON.PERSONID,
T_CARDEVENT.CARDEVENTDATE,
MIN(T_CARDEVENT.CARDEVENTTIME) AS INTIME,
MAX(T_CARDEVENT.CARDEVENTTIME) AS OUTTIME
FROM T_PERSON
INNER JOIN T_CARDEVENT ON T_PERSON.PERSONID = T_CARDEVENT.PERSONID
GROUP BY T_PERSON.PERSONID, T_CARDEVENT.CARDEVENTDATE) DERIVEDTBL
T_cardevent.cardeventtime column datatype is Varchar.
In table Cardeventtime values are 080002, 091235.... so on...,
When I executing the above query it showing Arithmetic Express Overflow error for converting expression to datatype Datetime.
So this "080002" stands for? 8 hours, 0 minutes, 2 seconds?
This is definitely not a valid DATETIME format out of the box - and it doesn't comply with any of the valid SQL Server CONVERT styles, either.
So you'll have to do some conversions yourself, manually. Is there any chance you could wrap the table with this column into a view which could handle the conversion?
You'd have to do something along the lines of:
CONVERT(DATETIME, SUBSTRING(CardEventTime, 1, 2) + ':' +
SUBSTRING(CardEventTime, 3, 2) + ':' +
SUBSTRING(CardEventTime, 5, 2), 8)
and this should turn your "080002" into "08:00:02" which can then be converted to a DATETIME (no separate time datatype until SQL Server 2008) using the style no. 8 (hh:mm:ss).
Marc
I've made a series of assumptions here, and worked without being able to test it, but here's a possible solution:
SELECT PERSONID,
CARDEVENTDATE,
INTIME,
CASE
WHEN OUTTIME = INTIME THEN
'No PunchOut'
ELSE
OUTTIME
END AS OUTTIME,
Stuff(Stuff(Right('000000' +
CONVERT(varchar(5), CASE
WHEN Convert(int,OutTime) > 180000 THEN
180000
ELSE
Convert(int,OutTime)
END - CASE
WHEN Convert(int,InTime) < 90000 THEN
90000
ELSE
Convert(int,InTime)
END), 6),5,0,':'),3,0,':')
AS WorkTime
FROM (SELECT T_PERSON.PERSONID,
T_CARDEVENT.CARDEVENTDATE,
MIN(T_CARDEVENT.CARDEVENTTIME) AS INTIME,
MAX(T_CARDEVENT.CARDEVENTTIME) AS OUTTIME
FROM T_PERSON
INNER JOIN T_CARDEVENT ON T_PERSON.PERSONID = T_CARDEVENT.PERSONID
GROUP BY T_PERSON.PERSONID, T_CARDEVENT.CARDEVENTDATE) DERIVEDTBL
I've not used Stuff very frequently, so the insertion points may be off.