Fetch recent item from each and every group - mysql

In MySQL query, I wanted to query a list of recent login 1 user
information form each user type.
Table Information:
user table has columns: user_id, usertype_id, last_login_date
usertype table has columns: usertype_id, user_id
I have tried,
SELECT MAX(u.last_login_date), u.user_id FROM `user` u
JOIN usertype ut ON u.usertye_id = ut.usertype_id
GROUP BY ut.usertype_id;
But the above query returns 'last_login_date' irrespective of the user_id.
Any ideas will be appreciated.

select *
from (select u.last_login_date, u.user_id
from user u
order by u.last_login_date desc) x
group by last_login_date
With this solution you can avoid complicated subqueries that try to find the max() etc, and also the problems of returning multiple rows when there are more than one with the same maximum value

Related

use COUNT(*) values from one table to another

Suppose I have two tables, users and posts. Posts has the following fields, userid, postid, etc and userid can appear multiple times as one user can write multiple posts....I'm just trying sort the users table based off the # of occurrences per userid in the posts table. I can get the # of occurrences per user using this
SELECT userid, COUNT(*)
FROM posts
GROUP BY userid;
I would like to use the values under COUNT(*) column, maybe add it to my other table because then I can simply to something like this
SELECT * FROM users
ORDER BY newcolumn ASC;
but I'm having trouble doing that. Or can I do it without having to add an extra column? Hints please. Thanks
Left join is the key here!
SELECT users.userid,count(posts.userid) AS total_count
FROM users
LEFT JOIN posts on posts.userid = users.userid
GROUP BY users.userid
ORDER BY total_count DESC;
We are taking the left join on two tables with same user_id and we are counting the total number of posts per user using group by. Finally sort by count and show results.
try an left join:
select users.userid, [user fields],count(postid) as posts_count
from users
left join posts on posts.userid = users.userid
group by users.userid,[user fields]
order by posts_count desc.
You want to select users (FROM users) but you want to sort based on criteria in another table (COUNT(*) FROM posts) -- therefore you need to use a JOIN
Off-hand I can't seem to recall if "JOIN" or "RIGHT JOIN" or "FULL JOIN" is what you need if you wanted to get a cartesian product of the tables then group and aggregate on a single field, but I can avoid the need to remember with a subquery (hopefully someone will soon post a smaller and smarter answer):
SELECT users.* FROM users
JOIN (
SELECT userid, COUNT(*) as count
FROM posts
GROUP BY userid
) as subquery ON users.id = subquery.userid
ORDER BY subquery.count
Note: I haven't tested this query, but it looks good to me. Again: hopefully someone will post a better answer soon as I'm not doing my due dilligence, but you definitely need a JOIN :)
You could add a post_count column to the users table, but you would also have to update that count column every time a user creates a new post and you would have to build that logic into your application.
Otherwise, it looks like the answer from FallAndLearn will get you what you need.

Result of one query, input for other query in mysql

I want to get information from two tables users and results.
users table has
id, name, email, password columns etc.
and results table has
id, user_id, last_attempt_time etc.
The result table gets populated every time a user takes a quiz.
I want to display user_id,name and last_attempt_time but my query returns oldest time and i have no idea how to solve this problem.
SELECT u.id,u.email,u.name,u.joined,r.last_attempt_time FROM users u
LEFT JOIN results r
ON u.id=r.user_id
GROUP BY u.id
ORDER BY u.id ASC
Try:
SELECT u.id,u.email,u.name,u.joined,MAX(r.last_attempt_time) AS LastAttempt
FROM users u left join results r on u.id=r.quiz_id
GROUP BY u.id,u.email,u.name,u.joined
ORDER BY u.id ASC

SQL Query: Must go through table, COUNT certain entries, and use that result as Column name in rest of Query

I am having trouble making a query which must behave as following:
Imagine a table named "Users" with these column names: id, name, vote.
The vote column holds the id of a row. For example, if I am id = 0, and you are id = 1, I wish to vote for you so my vote entry holds "1", you wish to vote for yourself so your's too holds "1".
I want a query which returns these columns: id, name, vote, totalVotes.
So it should count all the votes that have your id and place that number under total votes.
My "totalVotes" would be "0", and your's would be "2".
The problem is that I do not understand how to go through the entire table, calculate the total number of votes for a user, and then repeat for every user.
Any tips would be greatly appreciated and if it is difficult to understand feel free to tell me what part I should word better.
select
u1.id, u1.name, u1.vote,
ifnull(u2.totalVotes, 0) as totalVotes
from Users as u1
left outer join (
select u2.vote as id, count(*) as totalVotes
from Users as u2
group by u2.vote
) as u2 on u2.id = u1.id
sql fiddle demo
a bit of explanation:
I'm using a subquery, grouping records from Users by vote, so I'll have a table where I have count of votes for each user
After that join Users with this table
in the final recordset I'm using ifnull to display 0 for users who have no votes.
This, I believe, the fastest way to do this query in MySQL (without window functions), here you can see sql fiddle for only 1280 rows with my query and Carter's one using subquery.
sql fiddle demo
Results:
My query: 4ms
Carter's query with subquery: 832ms
This would be one way...
select
u1.id,
u1.name,
u1.vote,
(select count(*) from Users as u2 where u2.vote = u1.id) as totalVotes
from
Users as u1;
See: this SQL Fiddle for an exmaple.
Addendum: As Roman points out, when it comes to efficiency, his answer is clearly the better of our two. In general joins are more efficient than subqueries.

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

is it possible to have group by sort in any order other than ascending?

I have a query that joins a couple of tables and produces a lot of rows, in a situation where I only wish to see one row per user. I have solved the "only one user" problem by using group by user.id, however, I'm noticing that now for each user I get the values from the joined table that represent the first entry (rather than the last).
so in other words
user:
id | phone
item:
id | user_id | timestamp
my intention is to join these tables and select the latest item (based on timestamp, or item.id desc) but to only get one item per user (rather than see all the items that each user has). group by user.id solves the problem of giving me just one item per user, but they always turn up with the first item that has the lowest item.id, whereas I want the most recent one.
Is there a better way to achieve this... I was initially noodling with distinct but that doesn't seem to do the trick.
TIA
[EDIT]
In response to Jocelin's question below:
select user.id, item.timestamp from item join user on user.id = item.user_id order by user.id
mysql has a "cheat" for getting the first row only of each group:
select *
from (select
u.id as user_id,
u.name,
i.id as item_id,
i.timestamp
from user u
join item i on i.user_id = u.id
order by timestamp desc -- "desc" = order descending
) x
group by user_id
The "cheat" is that mysql allows you not aggregate the non-group by columns (unlike every other database I know). Instead of giving an SQL syntax error, it returns the first record only of each group. Not SQL standard, but very handy.
The beauty of this is that you don't need any correlated subqueries, which are dreadfully slow.
The inner query is used to order the records in timestamp latest-first order, so the first record encountered for each user is the "most recent".
Use an ORDER BY with DESC clause:
select
user.id, item.timestamp
from
item
join
user on user.id = item.user_id
group by
user.id
order by
user.id DESC
I'm suspecting (from your comments below) that your issue isn't really what you describe. Try something like this instead (untested, because I don't use MySQL - it works in SQL Server and SQLite):
select
user.id, i.ts
from
(select id, max(timestamp) as ts from items group by id) i
join
user u on u.id = i.id
You will need to use a JOIN. First create a subselect grouping by user and selecting the MAX of timestamp. This gets you one row per user, with the latest entry (and loses all other fields).
Then join it back using the timestamp in the JOIN expression, to restore the missing fields.
Sorry, but without the table schema it isn't easy to be more specific than this. Hope that the JOIN reference points you in the right direction to modify your query.