Reputation: 3
I am trying to build a simple query for working out times when staff clock in and clock out. The DB records the person's name, if their clocking in or out and the time. It records more then that but I don't need that info.
So an example DB would be:
Id | User | In_out | Date_Time
----------------------------------
2 | Bob | In | 13/Oct/16 9:30:35AM
3 | Ken | In | 13/Oct/16 9:34:27AM
4 | Jon | In | 13/Oct/16 9:34:46AM
5 | Billy | In | 13/Oct/16 9:52:06AM
6 | Bob | Out | 13/Oct/16 4:30:05PM
7 | Jon | Out | 13/Oct/16 4:32:55PM
The result I want to bring back in a SQL is:
User | Time_In | Time_Out
-------------------------------
Bob | 9:30:35AM | 4:30:05PM
Jon | 9:34:46AM | 4:32:55PM
The SQL I am trying is not right I know that. But what do I need to change to get the result I want?
SELECT
Clock.Checkin_Checkout.User,
Clock.Checkin_Checkout.In_Out,
(SELECT TO_CHAR(Clock.Checkin_Checkout.DATETIME, 'hh:mi:ss AM')
FROM Clock.Checkin_Checkout
WHERE Clock.Checkin_Checkout.In_Out = 'In') AS "Time In",
To_Char(Clock.Checkin_Checkout.Create_Datetime, 'hh:mi:ss AM') AS "Time Out"
FROM
Clock.Checkin_Checkout
WHERE
Clock.Checkin_Checkout.In_Out = 'Out'
AND Clock.Checkin_Checkout.Datetime >= '13/Oct/2016'
Upvotes: 0
Views: 215
Reputation: 4309
I don't think you need a sub-query. Something like the following may work...
select c1.User, c1.Date_Time as "Time_In", c2.Date_Time as "Time Out"
from Clock.Checkin_Checkout c1
inner join Clock.Checkin_Checkout c2 on c2.User = c1.User
where c1.In_Out = 'In'
and c2.In_Out = 'Out'
Please excuse possible syntax errors. I haven't used Oracle for ages, and I don't have any way to test this.
Upvotes: 0
Reputation:
It is not clear why you don't want to show Ken and Billy in your output (showing they clocked in but never clocked out), but if that's your requirement, you could do something like this:
select user, min(date_time) as time_in, max(date_time) as time_out
from checkin_checkout
group by user
having count(*) = 2;
This should be more efficient than any join-based solution. This is what the query does: First it groups by user; then it keeps the groups that have two rows (one for IN and the other for OUT); then it selects the user, the earlier time (out of the two for that group) and calls it time_in
, and the later time and it calls it time_out
. I don't think it can be simpler than this.
Remarks:
(1) This assumes the table is already "grouped" by DATE (in your table all dates are the same). Otherwise you would need to also group by trunc(date_time)
and to include trunc(date_time)
among the result columns as well; other than that the query will work exactly the same way.
(2) You have the user
column; I hope in your real-life data you don't have names, but some id or code that is unique to each user. "Bob" is not a unique identifier.
(3) The solution assumes the data is validated as it is inserted into the base table; so a "user" can have at most two rows, there is no "Out" row if there isn't an "In" row, the "Out" time is after the "In" time, etc.
Upvotes: 1
Reputation: 810
I've created an example here.
In the naive assumption that in the table there will be alwas one "In" and at most one "Out" for a single name, the following query should work:
Select c1.user,
to_char(MAX(CASE WHEN c1.In_Out='In' then c1.Date_Time end), 'hh:mi:ss AM') time_in,
to_char(MAX(CASE WHEN c1.In_Out='Out' then c1.Date_Time end), 'hh:mi:ss AM') time_out
From Clock.Checkin_Checkout as c1
group by c1.user
having MAX(CASE WHEN c1.In_Out='Out' then c1.Date_Time end) is not null;
If you want also the cases where there is no 'Out', just omit the "having" clause
Edited: more realistic scenario
If you want the query to be able to recognize multiple shifts from the same person, you might use this query:
select c1.user, c1.Date_Time as Time_In, c2.Date_Time as Time_Out
from Clock.Checkin_Checkout c1
join Clock.Checkin_Checkout c2
on c2.user= c1.user
where c1.In_Out= 'In'
and c2.In_Out= 'Out'
and c1.Date_Time <c2.Date_Time
and not exists (select 1 from Clock.Checkin_Checkout c3
where c1.user=c3.user
and c1.Date_Time <c3.Date_Time
and c3.Date_Time <c2.Date_Time )
I've also tested this solution on rextester.
Disclaimer: I started from @user1751825 query for this second scenario as it was simple to arrive to this solution from his query rather than from my previous one.
Upvotes: 2