Author |
Topic |
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-13 : 04:09:25
|
Hi,I am trying to calculate the hours worked by all employees. The data I have includes all the times the employee came in and out of the building. I need to to know the total hours that the employee was in the building. Here is the query I have to return the result below:Select Emp.Emp_Name + ' ' + Emp.Emp_Surname As FullName, Time.ClockedTimes, IO.InorOut FROM Employee Emp INNER JOIN Employee_Times Time ON Emp.Emp_ID = Times.Employee_ID INNER JOIN InOrOut IO ON Times.IO_ID = IO.IO_IDResult:FullName ClockedTimes InOrOutPerson1 2011-04-01 07:55:00 INPerson1 2011-04-01 10:00:00 OUTPerson1 2011-04-01 10:35:00 INPerson1 2011-04-01 17:05:00 OUTI want to be able to calculate the total hours, so in this case, it should be 2.05hrs + 6.5hrs = 8.55hrsCan alyone help me solve this. I am a newbie, so please give me as much explanation as possible. |
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2011-04-13 : 04:19:27
|
is the record always in pair of IN and OUT ?will there be cases of missing record ?What is the version of SQL Server are you using ? KH[spoiler]Time is always against us[/spoiler] |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-13 : 04:27:10
|
There will sometimes be missing values of In or Out. If the person clocked in for the day, but did not clock out, we can use a default value of 17:00:00 as the clock out time. If the person clocked out, but did not clock in for the day, we can use the defaukt of 8:00:00. I am using MS SQL Server 2005. |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-13 : 06:02:43
|
I have the following code, which seems to work, but only if the person ensured that they clocked OUT at the end of the day. If there are missing IN or OUT clocks, then the result is incorrect. How do I add default values if there are missing records?select Pers.Name + ' ' + Pers.surname As FullName , convert(varchar(10),RC.ClockTime,111),sum(case when Dir.Name='IN' then-1*(datepart(hh,RC.Clocktime)+datepart(mi,RC.Clocktime)/60.0)else datepart(hh,RC.Clocktime)+datepart(mi,RC.Clocktime)/60.0end)from Personnel Pers INNER JOIN RawClocks RC ON Pers.Personel_Id = RC.Person_IDINNER JOIN Direction Dir ON RC.Direction_Id = Dir.Direction_IDgroup by Pers.Name + ' ' + Pers.surname, convert(varchar(10),RC.ClockTime,111) |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-13 : 07:32:36
|
Anybody??? |
|
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2011-04-13 : 09:50:32
|
This gets you part way... but there are some problems:1) if a person forgets to clock twice in a row... ie. no clock OUT and no clock IN, then the range still looks valid:1 IN-2 OUT - forgot-3 IN - forgot 4 OUTSo range looks valid: 1-4 (In-Out).As far as assuming defaults... you can use IsNull() to put whatever you want in.Declare @t table ( id int, clockTime datetime, InOrOut varchar(10))/*Insert Into @t Select 1, '2011-04-01 07:55:00', 'IN'Insert Into @t Select 1, '2011-04-01 10:00:00', 'OUT'Insert Into @t Select 1, '2011-04-01 10:35:00', 'IN'Insert Into @t Select 1, '2011-04-01 17:05:00', 'OUT'*/Insert Into @t Select 2, '2011-04-01 07:55:00', 'IN'Insert Into @t Select 2, '2011-04-01 10:00:00', 'OUT'Insert Into @t Select 2, '2011-04-01 10:35:00', 'IN'--Insert Into @t Select 2, '2011-04-01 17:05:00', 'OUT'Insert Into @t Select 2, '2011-04-02 07:55:00', 'IN'--Insert Into @t Select 2, '2011-04-02 10:00:00', 'OUT'Insert Into @t Select 2, '2011-04-02 10:35:00', 'IN'Insert Into @t Select 2, '2011-04-02 17:05:00', 'OUT';with cte1(id, clockTime, InOrOut, r)As ( Select id, clockTime, InOrOut, r = Row_Number() Over(Partition By id Order By clockTime) From @t), cte2(id, InTime, OutTime)As ( Select id = isnull(A.id,B.id), InTime = A.ClockTime, OutTime = B.Clocktime From (Select * From cte1 Where InOrOut = 'IN') A Full Join (Select * From cte1 Where InOrOut = 'OUT') B On A.id = B.id and A.r = B.r-1)Select * From cte2Order By id, InTime CoreyI Has Returned!! |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 03:05:55
|
Thanks, but I tried to radd your code to my query. The result is not what I am looking for.I am looking for a result that looks like this:FullName Date Hours_WorkedPersonA 2011-04-01 08:45:23PersonB 2011-04-01 07:56:00PersonC 2011-04-01 16:15:10 |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2011-04-14 : 03:30:26
|
borrowing the @t from Corey; with cte as( select *, row_no = row_number() over (partition by id order by ClockTime) from @t),cte2 as( select c1.id, ClockIn = c1.ClockTime, ClockOut = case when c2.InOrOut = 'OUT' then c2.ClockTime else dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00' end from cte c1 left join cte c2 on c1.row_no = c2.row_no - 1 and c1.id = c2.id where c1.InOrOut = 'IN')select id, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, datediff(second, ClockIn, ClockOut), 0), 108)from cte2 KH[spoiler]Time is always against us[/spoiler] |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 04:13:57
|
Thanks,I get a much more legible result now, but can you explain what you did in this line:case when c2.Name = 'OUT' then c2.ClockTimeelse dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00' endI am trying to figure out how you got to the results you ended up with. My results dont seem to be correct. |
|
|
khtan
In (Som, Ni, Yak)
17689 Posts |
Posted - 2011-04-14 : 04:54:03
|
in the cte, row_no is a running number in the order of ClockTime. So by joining a row with the next row (row_no + 1) and checking the next row's state. If it is not OUT means, there is a OUT record missing, so default it to 17:00.quote: My results dont seem to be correct.
i have edited by query. Missed out a join condition on the ID. Try this. If it doesn't give the required result, post your sample data and the query that you used. KH[spoiler]Time is always against us[/spoiler] |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 05:51:05
|
Here is some sample data:FullName ClockIn DirectionPerson A 2011-04-01 07:40:56 INPerson A 2011-04-01 10:00:00 OUTPerson A 2011-04-01 10:25:56 INPerson A 2011-04-01 10:54:00 OUTHere is my query:; with cte as(select Emp.Emp_ID, Clock.ClockTime, Dir.Name, row_no = row_number() over (partition by Emp.Emp_ID order by ClockTime)from Employee Emp INNER JOIN ClockIns Clock ON Emp.Emp_Id = Clock.Emp_ID INNER JOIN Direction Dir ON Clock.Direction_ID = Dir.Direction_ID),cte2 as( select c1.Emp_ID, ClockIn = c1.ClockTime, ClockOut = case when c2.Name = 'OUT' then c2.ClockTimeelse dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00' endfrom cte c1 left join cte c2 on c1.row_no = c2.row_no - 1where c1.Name = 'IN')select Emp_ID, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, datediff(second, ClockIn, ClockOut), 0), 108)from cte2For the data displayed above, I am getting several lines of output with values of 6:34:04. |
|
|
sunitabeck
Master Smack Fu Yak Hacker
5155 Posts |
Posted - 2011-04-14 : 07:42:24
|
In your latest query, you did not follow khtan's advice about adding the employee Id in the join condition in the second CTE.cte2 as(select c1.Emp_ID, ClockIn = c1.ClockTime, ClockOut = case when c2.Name = 'OUT' then c2.ClockTimeelse dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00' endfrom cte c1 left join cte c2 on c1.row_no = c2.row_no - 1 and c1.Emp_ID = c2.Emp_IDwhere c1.Name = 'IN') |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 08:01:49
|
Thank you.The only problem now, is that if a person clocked in more than once for a day, for eg, he went out during lunch, there would be more than one total hours worked for the day. Is there a way to sum this and produce 1 result per person.Here is what my result looks like now:Emp_Id ClockTime Hours_Worked 1 2011-04-01 00:00:00.000 09:03:59 1 2011-04-01 00:00:00.000 00:30:06 2 2011-04-01 00:00:00.000 02:19:04 2 2011-04-01 00:00:00.000 00:28:04 3 2011-04-01 00:00:00.000 08:01:56 4 2011-04-01 00:00:00.000 10:18:57I need there to be only 1 record for Emp_Id 1, and Emp_Id 2. |
|
|
sunitabeck
Master Smack Fu Yak Hacker
5155 Posts |
Posted - 2011-04-14 : 08:09:05
|
Sure, that's easy. (Now that khtan and Corey have done all the hard work to help you, it is easy for me to come and add the final touches and take all the credit for having solved the problem [I am typing this and trying to hide an evil grin at the same time)Just change the last select to sum up the hours for each employee for each day like this:select id, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, ClockOut)), 0), 108)FROM cte2GROUP BY id, dateadd(day, datediff(day, 0, ClockIn), 0) |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 08:26:21
|
I have added that to make my complete query look like this:; with cte as (select Emp.Emp_ID, Clock.ClockTime, Dir.Name, row_no = row_number() over (partition by Emp.Emp_ID order by Clock.ClockTime) from Employee Emp INNER JOIN ClockIns Clock ON Emp.Emp_Id = Clock.Emp_ID INNER JOIN Direction Dir ON Clock.Direction_ID = Dir.Direction_ID), cte2 as (select c1.Emp_ID, ClockIn = c1.ClockTime, ClockOut = case when c2.Name = 'OUT' then c2.ClockTime else dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00' end from cte c1 left join cte c2 on c1.row_no = c2.row_no - 1 and c1.Emp_ID = c2.Emp_ID where c1.Name = 'IN') select Emp_ID, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, datediff(second, ClockIn, ClockOut), 0), 108) from cte2 GROUP BY Emp_ID, dateadd(day, datediff(day, 0, ClockIn), 0)I am now getting the following errors:"Column 'cte2.ClockIn' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.Column 'cte2.ClockOut' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause." |
|
|
sunitabeck
Master Smack Fu Yak Hacker
5155 Posts |
Posted - 2011-04-14 : 08:31:24
|
You left out the MOST vital part select id, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, ClockOut)), 0), 108)FROM cte2GROUP BY id, dateadd(day, datediff(day, 0, ClockIn), 0) The rule (for the most part) is that when you want to use an aggregate function such as SUM, all other columns in the select list that are outside of the aggregate function must be in the group by clause. So here I am summing the third item in your select list, and so the first two must be in the group by clause. |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 08:35:35
|
Thank you soooo much. Thanks to everyone who helped. :-)I just have one more question. With this data, I want to find out how many hours the person worked before 08:00:00AM and how many hours the person worked after 16:00:00 each day.How do I go about doing this? |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-14 : 09:46:43
|
Also, I want to change the query to allow for a default if the person only clocked out and not in for a day. What do I need to change in the query to accomodate this? So lets say if there is no IN clock for the day, we must assume that the person started at 08:00AM.I tried the following but it does not work:; with cte as (select Emp.Name + ' ' + Emmp.Surname As FullName, Clock.ClockTime, Dir.Name, row_no = row_number() over (partition by Emp.Name + ' ' + Emp.Surname order by C.ClockTime) from Employee Emp INNER JOIN ClockIns Clock ON Emp.Emp_Id = Clock.Emp_ID INNER JOIN Direction Dir ON Clock.Direction_ID = Dir.Direction_ID), cte2 as ( select c1.FullName, ClockIn = case when c2.Name = 'IN' then c1.ClockTimeelse dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '08:00:00' end, ClockOut = case when c2.Name = 'OUT' then c2.ClockTime else dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00:00' end from cte c1 left join cte c2 on c1.row_no = c2.row_no - 1 and c1.FullName = c2.FullName where c1.Name = 'IN') select FullName, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, ClockOut)), 0), 108)FROM cte2GROUP BY FullName, dateadd(day, datediff(day, 0, ClockIn), 0) |
|
|
sunitabeck
Master Smack Fu Yak Hacker
5155 Posts |
Posted - 2011-04-14 : 10:51:01
|
quote: Originally posted by Jas Thank you soooo much. Thanks to everyone who helped. :-)I just have one more question. With this data, I want to find out how many hours the person worked before 08:00:00AM and how many hours the person worked after 16:00:00 each day.How do I go about doing this?
Add two more columns to the final select, with limiting the clockout time to 8:00 AM in the first, and clockout time to 4:00 PM in the second. So the final select would be like this:select id, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, ClockOut)), 0), 108), Hours_Worked_Before8AM = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, case when DATEDIFF(hh,0,ClockIn)%24 < 8 AND DATEDIFF(hh,0,ClockOut)%24 >= 8 THEN DATEADD(hh,(DATEDIFF(hh,0,ClockOut)/24)*24+8,0) end )), 0), 108), Hours_Worked_After4PM = convert(varchar(10), dateadd(second, sum(datediff(second, case when DATEDIFF(hh,0,ClockIn)%24 < 16 AND DATEDIFF(hh,0,ClockOut)%24 >= 16 THEN DATEADD(hh,(DATEDIFF(hh,0,ClockIn)/24)*24+16,0) END, ClockOut )), 0), 108)FROM cte2GROUP BY id, dateadd(day, datediff(day, 0, ClockIn), 0) |
|
|
sunitabeck
Master Smack Fu Yak Hacker
5155 Posts |
Posted - 2011-04-14 : 10:52:45
|
quote: Originally posted by Jas Also, I want to change the query to allow for a default if the person only clocked out and not in for a day. What do I need to change in the query to accomodate this? So lets say if there is no IN clock for the day, we must assume that the person started at 08:00AM.I tried the following but it does not work:; with cte as (select Emp.Name + ' ' + Emmp.Surname As FullName, Clock.ClockTime, Dir.Name, row_no = row_number() over (partition by Emp.Name + ' ' + Emp.Surname order by C.ClockTime) from Employee Emp INNER JOIN ClockIns Clock ON Emp.Emp_Id = Clock.Emp_ID INNER JOIN Direction Dir ON Clock.Direction_ID = Dir.Direction_ID), cte2 as ( select c1.FullName, ClockIn = case when c2.Name = 'IN' then c1.ClockTimeelse dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '08:00:00' end, ClockOut = case when c2.Name = 'OUT' then c2.ClockTime else dateadd(day, datediff(day, 0, c1.ClockTime), 0) + '17:00:00' end from cte c1 left join cte c2 on c1.row_no = c2.row_no - 1 and c1.FullName = c2.FullName where c1.Name = 'IN') select FullName, Date = dateadd(day, datediff(day, 0, ClockIn), 0), Hours_Worked = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, ClockOut)), 0), 108)FROM cte2GROUP BY FullName, dateadd(day, datediff(day, 0, ClockIn), 0)
I think Corey and khtan's query already does this. |
|
|
Jas
Yak Posting Veteran
51 Posts |
Posted - 2011-04-15 : 03:36:22
|
Thank you Sunita!!Can I just ask you to explain what you did in the following lines:Hours_Worked_BeforeShiftStart = convert(varchar(10), dateadd(second, sum(datediff(second, ClockIn, case when DateDiff(hh,0,ClockIn) %24 < 8 AND Datediff(hh, 0, ClockOut)%24 >= 8 then DateAdd(hh,(DateDiff(hh, 0, ClockOut)/24)*24+8, 0) end)), 0), 108), |
|
|
Next Page
|
|
|