Understanding GROUP BY with a LIMIT - mysql

I've got a simple SQL query to give me information about all of the users who have commented on an image like this...
SELECT user.id, user.userName, user.displayName, user.currentDefault, user.discipline
FROM user
INNER JOIN imageComment ON user.id = imageComment.commentAuthorId
WHERE imageComment.imageAlbumJunctionId = 37
GROUP BY user.id
LIMIT 2
I have the LIMIT 2 on there because I know there are only two comments for this image. And I have the GROUP BY user.id on there because I only want to show information about a user once, even if they comment multiple times.
So lets say that "Mike" commented on a photo twice. My question is, does this query...
Only search for 2 comments because of the LIMIT, and then perform the GROUP BY user.id
Perform the GROUP BY user.id and then search the full table for a second unique user
I am hoping that this query does #1 because if it does #2 that would cause it to search the entire table looking for a second user when "Mike" was actually the one that did both comments. And I did try an EXPLAIN but I don't really understand it because it gives the same output whether there is a GROUP BY or LIMIT. Thank you for reading.

The query finds the first two users who have commented an Image, so it's #2.
I'd suggest:
select ...
from user
where exists
(
select * from imageComment
where imageComment.commentAuthorId = user.id
and imageComment.imageAlbumJunctionId = 37
)
where exists is faster than a inner join because it can stop after the first. Good indices should be set.

LIMIT is applied after the GROUP BY user.id. So in this case, #2 is happening. But the WHERE clause will filter the table first, so it's not searching the whole table. Your query will give you correct results, but I think this should be better:
SELECT DISTINCT user.id, user.userName, user.displayName, user.currentDefault, user.discipline
FROM user
INNER JOIN imageComment ON user.id = imageComment.commentAuthorId
WHERE imageComment.imageAlbumJunctionId = 37
LIMIT 2

Sample Tables
User Table imageComment Table
id | userName id | commentAuthorId | imageAlbumJunctionId
--------------- -------------------------------------------
1 | Mike 1 | 1 | 37
2 | John 2 | 1 | 37
3 | Carla 3 | 2 | 37
4 | 3 | 37
Split explanation about what going on
SELECT user.id, user.userName
FROM user
INNER JOIN imageComment ON user.id = imageComment.commentAuthorId
WHERE imageComment.imageAlbumJunctionId = 37
this will return
1 | Mike
1 | Mike
2 | John
3 | Carla
after your GROUP BY user.id you will now get
1 | Mike
2 | John
3 | Carla
now we use the LIMIT 2 and get
1 | Mike
2 | John

Related

MYSQL - Order a table by another table

I have a problem
I have two tables
The table "Memes"
id imglink name
----------------------------------
1 img.Png Polite cat
2 img2.png Crying cat
And the table "Vote"
id idmeme vote
---------------------
1 1 5
2 1 2
3 2 4
So basically the table "meme" contains memes with their image and their name
And the table "votes" contains the notes on 5 that users assign to the memes
I would like my sql query to rank by the same the highest rated with the highest rating
I already look at other topic but the problem is that for each vote with the id of the same it duplicates in the result of the SELECT *
thank you in advance
One method is to use a subquery right in the order by:
select m.*
from memes m
order by (select max(v.vote) from vote v where v.idmeme = m.id);
Of course, you can also include this in the from clause (as an aggregation query) and use a join.
The most efficient way is to use a query that returns all the maximum votes from Vote and join it to the table:
select m.*
from Memes m left join (
select idmeme, max(vote) vote
from Vote
group by idmeme
)v on v.idmeme = m.id
order by v.vote desc, m.name
See the demo.
Results:
| id | imglink | name |
| --- | -------- | ---------- |
| 1 | img.Png | Polite cat |
| 2 | img2.png | Crying cat |

Mysql finding results not present in jointable

I've got a mysql question that I haven't been able to figure out. Its a little bit different than the other questions I've found here on SO.
I've got three tables
users
____________
ID name
1 John
2 Mike
3 Susie
tasks
___________
ID Name
1 Brush Teeth
2 Shower
3 Check Email
users_tasks
_____________________
ID user_id task_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 3 1
Im trying to find out what users haven't completed what tasks yet. I would like the result set to look like this:
user_id task_id
__________________
2 3
3 2
3 3
The closest I have come is this query, which only gives me users that haven't completed at least one of the tasks, but doesn't give me the task.
select * FROM users
right JOIN users_tasks on users.id = users_tasks.user_id
right JOIN tasks on users_tasks.task_id = tasks.id
where tasks.id is null
I cant figure out how to return duplicate users and tasks based on what is missing form the join table.
Thanks in advance
An easy solution is just to require that the entry is not in your completed tasks table:
select * from users, tasks
where not exists (
select * from users_tasks
where users.id = users_tasks.user_id and tasks.id = users_tasks.task_id
);
Result:
+------+-------+------+-------------+
| id | name | id | name |
+------+-------+------+-------------+
| 3 | susie | 2 | Shower |
| 2 | mike | 3 | Check Email |
| 3 | susie | 3 | Check Email |
+------+-------+------+-------------+
One way to do this is to create a set representing the cartesian product of the users and tasks tables. This is what would be the result if every user had done every task. Then do a left join with the actual users_tasks table and filter for null to get the missing items:
select sub.*
from (
select u.id user_id, t.id task_id
from users u, tasks t
) sub
left join users_tasks ut on sub.user_id = ut.user_id and sub.task_id = ut.task_id
where ut.ID is null;

SQL LIMIT expression

Users:
UserID | UserNick
3 | Joe
23 | Peter
4 | Mary
Messages:
FromUserID | theMSG
3 | Hi
3 | What' up?
23 | asdfg
23 | OK...
4 | Hi, this is Mary
I have a query that gives the following result:
UserID | Message
1 | Hello
1 | How are ya?
2 | yadda yadda
5 | Cool.
5 | I didn't know that.
I now want to limit the result. Not by the number of rows I get back, by the number of different users from whom I want to see the messages.
"Give me three messages of the first 2 users"
UserID | Message
1 | Hello
1 | How are ya?
2 | yadda yadda
But if I write LIMIT 2, it will give me only
UserID | Message
1 | Hello
1 | How are ya?
How do I achieve what I want? What's the keyword I need?
I think you need to use a nested query like this:
SELECT * FROM messages
WHERE UserID IN
(SELECT DISTINCT UserID FROM messages ORDER BY UserID LIMIT 2)
but without knowing the table structure it is difficult to say more.
The idea is:
get the users you want in nested query
get their messages with outer query
You could use the following query. It gives you options to select first n unique users or last n unique users. Use ASC for first or DESC for last n users.
SELECT *
FROM messages
WHERE UserID IN (
SELECT DISTINCT UserID
FROM messages
ORDER BY UserID ASC/DESC
LIMIT 2
)
LIMIT 2
Inner Limit defines the number of unique users you want to select.
Outer Limit definesthe number of messages you wish to see from users
If I understand your question, you can get those results with:
SELECT * FROM table WHERE UserID IN(1, 2)
or
SELECT * FROM table WHERE UserID BETWEEN 1 AND 2

MySQL SELECT Multiple DISTINCT COUNT

Here is what I'm trying to do. I have a table with user assessments which may contain duplicate rows. I'm looking to only get DISTINCT values for each user.
In the example of the table below. If only user_id 1 and 50 belongs to the specific location, then only the unique video_id's for each user should be returned as the COUNT. User 1 passed video 1, 2, and 1. So that should only be 2 records, and user 50 passed video 2. So the total for this location would be 3. I think I need to have two DISTINCT's in the query, but am not sure how to do this.
+-----+----------+----------+
| id | video_id | user_id |
+-----+----------+----------+
| 1 | 1 | 1 |
| 2 | 2 | 50 |
| 3 | 1 | 115 |
| 4 | 2 | 25 |
| 5 | 2 | 1 |
| 6 | 6 | 98 |
| 7 | 1 | 1 |
+-----+----------+----------+
This is what my current query looks like.
$stmt2 = $dbConn->prepare("SELECT COUNT(DISTINCT user_assessment.id)
FROM user_assessment
LEFT JOIN user ON user_assessment.user_id = user.id
WHERE user.location = '$location'");
$stmt2->execute();
$stmt2->bind_result($video_count);
$stmt2->fetch();
$stmt2->close();
So my query returns all of the count for that specific location, but it doesn't omit the non-unique results from each specific user.
Hope this makes sense, thanks for the help.
SELECT COUNT(DISTINCT ua.video_id, ua.user_id)
FROM user_assessment ua
INNER JOIN user ON ua.user_id = user.id
WHERE user.location = '$location'
You can write a lot of things inside a COUNT so don't hesitate to put what you exactly want in it. This will give the number of different couple (video_id, user_id), which is what you wanted if I understood correctly.
The query below joins a sub-query that fetches the distinct videos per user. Then, the main query does a sum on those numbers to get the total of videos for the location.
SELECT
SUM(video_count)
FROM
user u
INNER JOIN
( SELECT
ua.user_id,
COUNT(DISTINCT video_id) as video_count
FROM
user_assessment ua
GROUP BY
ua.user_id) uav on uav.user_id = u.user_id
WHERE
u.location = '$location'
Note, that since you already use bindings, you can also pass $location in a bind parameter. I leave this to you, since it's not part of the question. ;-)

Select random user with existing images from database

I have two tables:
USER:
userID | uName |
-----------------
1 | John
2 | Bohn
3 | Kohn
4 | Lohn
5 | Zohn
6 | Rohn
IMAGES:
imageID | url | userID
----------------------
1 | x | 2
2 | x | 2
3 | x | 1
4 | x | 3
5 | x | 3
6 | x | 3
As you can see there is USER database which connects to IMAGES database by userID.
There is only one unique user row but there can be more than one image per user.
What i want is to fetch ONE RANDOM user from database and all of his images. Condition is that user should not be selected if he has no images.
Can you help me build ONE mysql query to achieve that?
P.S. It doesnt matter if user data duplicates (because of multiple image rows), there is only one user in resultset anyways.
First, INNER JOIN both tables, to exclude users that don't have any images; then, ORDER BY RAND(), to randomize the resultset; finally, select the first row to obtain a random userId:
SELECT u.*
FROM user u INNER JOIN images i ON (u.userId = i.userId)
ORDER BY RAND()
LIMIT 1
DEMO.
EDIT: To select all images from a random user, use the following:
SELECT i.*
FROM images i
INNER JOIN
(SELECT u.userId
FROM user u INNER JOIN images i ON (u.userId = i.userId)
ORDER BY RAND()
LIMIT 1) rand_user
ON (i.userId = rand_user.userId);
DEMO.