Count rows after specific one - mysql

A table users has three columns: id, name, pass.
Another table logins has user_id column, an isright boolean (tinyint) column which says whether the login was successful or not and a date column.
I need a simple left join to get the user's name and his password (1), the last login datetime (successful or not) (2) and the count of the logins for the specific user since his last successful login (3).
(1) and (2) I can achieve using
SELECT name, pass, MAX(date)
FROM users
LEFT JOIN logins ON logins.id = users.id
-- here either "GROUP BY users.id" or "WHERE users.id = 1234"
But (3) seems to be harder. I googled it and found many similar question but none of them was asking on exactly how to count rows after specific condition is true. (It's even more complicated - count the logins for that user, not everyone)
I don't even know how to do it in a separate query (I'd prefer having one query for the 3 things and I suppose I'd have to use a subquery, although I prefer joins).
SQL fiddle with the tables and some data: http://sqlfiddle.com/#!9/a932b
Any ideas?

The straight-forward way is to have two derived tables: One to get the last login date per user, the other to get the last successful login date per user. Then select from users, outer join the two derived tables and look whether the last login was successful and count the (failed) logins after the last successful login. (With another DBMS you would rather use analytic functions that MySQL lacks.)
select
users.name,
users.pass,
(
select max(isright)
from logins
where user_id = students.id and date = last_login.date
) as last_login_successful,
(
select count(*)
from logins
where user_id = students.id and date > last_successful_login.date
) as last_logins_failed
from users
left outer join
(
select user_id, max(date) as date
from logins
group by user_id
) last_login on last_login.user_id = users.id
left outer join
(
select user_id, max(date) as date
from logins
where isright = 1
group by user_id
) last_successful_login on last_successful_login.user_id = users.id;
This gives you four possibilities per user:
The user never tried to login. last_login_successful is null and last_logins_failed is meaningless.
The user's logins all failed. last_login_successful is 0 and last_logins_failed is meaningless.
The user's last login was successful. last_login_successful is 1 and last_logins_failed is meaningless.
The user logged in successfully once, but failed at least th last time they tried. last_login_successful is 0 and last_logins_failed is the number of failures after last successful login.
And here is a fiddle: http://sqlfiddle.com/#!9/57b7d/1.
EDIT: To also count failed logins when a user never logged in: If a user never logged in, their last_login.date is null. In last_logins_failed you want to count all records for which the last login occurred before OR never:
(
select count(*)
from logins
where user_id = students.id and (date > last_successful_login.date
or last_successful_login.date is null)
) as last_logins_failed

I guess you could do something like
select count(*)
from logins as l join users as u on l.user_id = u.id
where l.timestamp > (select max(timestamp)
from logins
where user_id = u.id and isright = 1)
Get the last timestamp of the login that was successful (subquery) for user
then get a count for all the logins where the timestamp is greater than that of the user, this automatically gives you only the unsuccessful logins because you took the last successful one as a reference timestamp/datetime

How about using a view which only has latest login detail with below query
select l.user_id, l.date, l.isright from logins l
where l.date >= (select max(l2.date) from logins l2
where l2.isright=1 and l2.user_id = l.user_id)

Related

sort details order by user id from another mysql table by activity less than 3600

I wish to fetch all users from "members" table but also check if the member_id from members table and user_id exist in"login" table and then see if column "activity" (current_timestamp) is less than 3600 seconds in login table than order those users on top rest users if don't exist in login table shows those users in bottom?
how cani query this please.
this is how i fetch users
$query = "SELECT * FROM members WHERE member_id != '".$_SESSION['member_id']."'";
but now how do i query the rest?
really thank you for your help.
Thanks
You need something like
SELECT members.*
FROM members
JOIN logins ON members.member_id = logins.member_id
WHERE logins.logged_in_at >= CURRENT_TIMESTAMP - INTERVAL 3600 SECOND
-- AND members.member_id != '$_SESSION['tfs_member_id']'
JOIN provides the presence in logins table, WHERE by logged_in_at filters "active" logins.
Use a LEFT JOIN with the logins table so you get members that aren't in the table. Then test whether the user is found in the logins table in the ORDER BY clause, and follow that by ordering by whether the last activity is recent.
SELECT m.*
FROM members AS m
LEFT JOIN logins as l ON m.member_id = l.member_id
ORDER BY l.member_id IS NULL,
l.activity > DATE_SUB(NOW(), INTERVAL 1 HOUR) DESC

mysql select latest timestamp for two matching rows with leftjoin

I have two tables in MySQL.
The logs table has columns: time, user_id, event_type
The users table has columns: email, id
Every time a user logs in to the system that generates the logs file, the event_type column value is logged with the value '/dashboard'.
The select statement here will show my every time they logged in...
SELECT users.email, logs.time
FROM users
LEFT JOIN logs ON users.id=logs.user_id
WHERE logs.event_type LIKE '/dashboard'
GROUP BY email, time;
...but I want to return the result set that shows the email address and timestamp for only the first time each user logged in.
You can use min for that:
SELECT users.email, min(logs.time)
FROM users
INNER JOIN logs ON users.id=logs.user_id
WHERE logs.event_type LIKE '/dashboard'
GROUP BY email;
Also no need for an outer join -- you are negating that in your where criteria. If you want to keep the outer join and return users that might not have matching records in the logs table, then move the criteria to the on:
SELECT users.email, min(logs.time)
FROM users
LEFT JOIN logs ON users.id=logs.user_id
AND logs.event_type LIKE '/dashboard'
GROUP BY email;

joining two tables based on recent logged in datetime

I am working on MySql and have two tables login_users and login_timestamps
Table login_users keeps a record of user_id, name and address whereas table login_timestamps keeps a record of user_id and timestamp, this table adds a new entry each time user logs in, so for example if the user_id '1' logs in 10 times a day, this table will have 10 entries for user_id '1' for today.
Now I need to fetch user profiles based on their last logged in time.
for example if there are 3 users, the MySql query should give me 3 records with their latest logged in time.
The query I am using is
SELECT * FROM login_users LEFT JOIN login_timestamps ON login_users.user_id = login_timestamps.user_id ORDER BY login_timestamps.timestamp DESC
but this gives me all the previous logged in entries rather than the recent one only.
Of course you will get all logged in entries while you didnt specify when or what day or something , hete you need a where clause.
try that:
SELECT * FROM login_users
LEFT JOIN login_timestamps ON login_users.user_id = login_timestamps.user_id
WHERE DATE(`timestamp`) = CURDATE()
ORDER BY login_timestamps.timestamp DESC
this will give you entries for curent day. of course you can specify any condition you want.
EDIT: from your comment.
try that
SELECT l.user_id, max(timestamp) as lasttime
FROM login_users l
LEFT JOIN login_timestamps lt ON l.user_id = lt.user_id
GROUP BY l.user_id
ORDER BY lasttime DESC

mysql group and sort without using order by on outter most

I am trying to return a list of users and their last login date. I need to get something that doesn't effect my outter order by statement because its dynamically populated by parameters the users chooses to sort the list. The other thing is the table that stores the login times requires 2 fields to link to the user table a user_id and a user_type.
table_admin
field: id, name
table_logs
field: id, user_id, user_type, login_date
*table_admin.id = table_logs.user_id
table_logs.user_type needs to be "admin"*
I need to pull all the admin users from table_admin with their last login date (it also needs to work if there is no record in the log table), the user_type in the log table would be "admin".
Thank you for your time.
SELECT SQL_CALC_FOUND_ROWS admins.*,ld2.ip,ld2.login_date as last_login
FROM admins
LEFT JOIN (
SELECT * FROM log_logins WHERE user_type = "admin" ORDER BY login_date DESC
) as ld2 ON (ld2.user_id = admins.id)
WHERE 1
GROUP BY user_id
ORDER BY admins.id DESC LIMIT 0,40
This is what I have so far but it doesn't grab results if they do not have an entry in the log table.
This is a join with an aggregation:
select a.name, 'admin' as userType, max(l.login_date) as lastLoginDate
from admin a left outer join
logs l
on a.id = l.id and
l.user_type = 'admin'
group by a.name

MySQL COUNT Filtering

This is my first post on stackoverflow, although I've used it as a reference for a long time. So thanks for all the guidance you've provided me in the past. I really appreciate it. Here is my issue:
I have two tables, one called USERS and one called SIGNIN
USERS
-----------------
userid | password
SIGNIN
--------------
suserid | date
I would like to count the number of times that a user has signed in over the period of the last week.
Here's what I've got:
SELECT userid, password FROM USERS JOIN (SELECT suserid, COUNT(*) AS logins
FROM signin
WHERE WEEKOFYEAR(date) = WEEKOFYEAR(CURDATE()) && signin.suserid = users.userid)
GROUP BY userid
I just can't wrap my head around how to JOIN a subquery and make it count each individuals' logins (date column) and return an individualized number for each individual. I know that query is totally jacked up but I'm just at that point, you know, where I've become so confused that I just need some guidance.
Any help and direction would be fantastic! I've read so many pages to no avail. Thanks, in advance!
** and thank you for the edits. They'll help in the future when I post again.
Don't try to do a subquery, just do the join and group by user id. Also, you want YEARWEEK so you are matching both the year and week, not just the week from any year.
select userid, count(*)
from users inner join signin on userid=suserid and yearweek(`date`) = yearweek(curdate())
group by userid;
left join instead of inner join if you want results of 0 for users who haven't signed in.
This follows what I assume is your intent of "last week" meaning the week of the current date (by default starting Sunday, but see the optional mode parameter to yearweek).
Note that the users table here is completely optional; you could just do:
select suserid, count(*) from signin
where yearweek(`date`) = yearweek(curdate())
group by suserid;
but I'm guessing you may be getting other columns from user or wanting to exclude signins for deleted users.
sThis should be it:
select suserid, count(suserid)
from signin
where date > NOW() - INTERVAL 1 WEEK
group by suserid;
You don't need a subquery if I understand you correctly.
Something like:
SELECT a.userid, a.password, count(b.date) AS logins
FROM users a
INNER JOIN signin b ON a.userid = b.suserid and WEEKOFYEAR(date) = WEEKOFYEAR(CURDATE())
GROUP BY a.userid, a.password
Sorry - haven't got time to try the query to double check syntax, but hopefully will set you in right direction
Edit: If you want to include users with 0 logins, change to LEFT OUTER JOIN
you don't need a join here. what you need is
SELECT userid, password, (SELECT COUNT(*)
FROM SIGNIN
WHERE WEEKOFYEAR(date) = WEEKOFYEAR(CURDATE()) AND signin.suserid = users.userid) AS logins
FROM USERS WHERE 1 GROUP BY userid;