Reputation:
I'm a Java guy and I need some SQL pointers. Here's a simplified version of what I'm trying to do...
Let's says I have:
a USERS
table:
ID | USERNAME
---+---------
0 | Robert
1 | Bob
2 | Joe
a STATE
table:
ID | STATE_NAME
---+-----------
0 | OFFLINE
1 | ONLINE
A USER_STATE
table (which records when a user goes on and offline):
ID | USER_ID | STATE_ID | DATE_TIME
---+---------+----------+----------
0 | 0 | 1 | 8am << Robert came online at 8am
1 | 1 | 1 | 8:10am << Bob came online at 8:10am
2 | 0 | 0 | 9am << Robert went offline at 9am
Basically, I need to write a query that gives me a list of all the users who are currently online.
So far I have this ...
SELECT u.id, u.username, s.state_name, us.date_time
FROM USERS u
JOIN USER_STATE us ON u.id = us.user_id
JOIN STATE s ON us.state_id = s.id
WHERE us.state_id = 1;
... but that gives me a record for every "online" entry in the USER_STATE
table.
The question is: How do I only get the users whose newest state is "online"?
Upvotes: 0
Views: 50
Reputation: 331
i have worked with your query. Based on your question i created below tables and inserted records as you mentioned.
create table usertab
(id int,username varchar(100))
create table statetab
(id int,state_name varchar(100))
create table userstatetab
(id int,user_id int,state_id int,date_time time)
insert usertab values(0,'Robert')
insert usertab values(1,'Bob')
insert usertab values(2,'Joe')
insert statetab values(0,'OFFLINE')
insert statetab values(1,'ONLINE')
insert userstatetab values(0,0,1,'08:50')
insert userstatetab values(1,1,1,'09:00')
insert userstatetab values(2,0,0,'09:10')
insert userstatetab values(3,1,0,'10:00')
insert userstatetab values(4,1,1,'12:00')
insert userstatetab values(5,1,0,'12:05')
insert userstatetab values(6,0,1,'13:10')
insert userstatetab values(6,0,0,'14:10')
--getting last online status of all users
with lastonline as
(
SELECT
ROW_NUMBER() over(partition by us.user_id order by us.date_time desc) r_no,u.id,
u.username,
s.state_name,
us.date_time
FROM usertab u
JOIN userstatetab us ON u.id = us.user_id
JOIN statetab s ON us.state_id = s.id
WHERE us.state_id = 1
) select id, username, state_name, date_time from lastonline where r_no=1 ;
Sample output
ID | username| state_name| date_time
---+---------+-----------+-----------------
0 | Robert | ONLINE | 13:10:00.0000000
1 | Bob | ONLINE | 12:00:00.0000000
Upvotes: 1
Reputation: 35920
You can use EXISTS
as the following:
SELECT U.USERNAME, S.STATE_NAME, US.DATE_TIME
FROM USER_STATE US
JOIN STATE S ON (US.STATE_ID = S.ID)
JOIN USERS U ON (US.USER_ID = U.ID)
WHERE S.STATE_NAME = 'ONLINE'
AND NOT EXISTS
(SELECT 1
FROM USER_STATE USIN
WHERE US.DATE_TIME > USIN.DATE_TIME)
Cheers!!
Upvotes: 0
Reputation: 5201
If the id
of the user_state
table is an auto-increment field, in the subquery you can take the max id
grouping by user_id
:
SELECT users.id
, users.name
FROM users
JOIN user_state
ON users.id = user_state.user_id
WHERE user_state.id IN (
SELECT MAX(id)
FROM user_state
GROUP BY user_id
)
AND user_state.state_id = 1
Upvotes: 0
Reputation: 3970
You can try via group by with having to have the record of the most recent date.
select u.id, u.username,
max(s.state_name) ,
max(us.date_time)
from USERS u
join USER_STATE us on u.id =
us.user_id
join STATE s on us.state_id = s.id
group by u.id, u.username
having
1=(case
when
us.datetime=max(datetime) then
1 else 0 end)
) ;
Upvotes: 0
Reputation: 1270713
You can use a correlated subquery to get the most recent record and then check that it is online:
select us.*
from user_state us
where us.date_time = (select max(us2.date_time)
from user_state us2
where us2.user_id = us.user_id
) and
us.state_id = 1;
For more information, about users, just join in the users
table in the outer query.
Upvotes: 0