Display only top sum-up results using group by - mysql

My table records three different scores (site, park, division) for the same group of user.
A user's total score is "sum of all the site scores" + "sum of all the park scores" + "sum of all the division scores".
The code is the following and I am using MySQL:
select sum(points)as points, user_name
from
(SELECT sum(site_point) as points, user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(park_point) as points , user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(division_point) as points , user_name from visits v join users u
on u.user_id = v.user_id group by user_name
) V group by user_name order by sum(points) DESC ;
I want to display only the users whose score is in top 5 and their scores.
Ten users may have the same highest score. I need all of them being displayed.
I appreciate any help.

I think this is what you want:
select user_name,
sum(site_point + park_point + division_point) as points
from visits v join
users u
on u.user_id = v.user_id
group by user_name
order by points desc
limit 5;
A single aggregation simplifies the calculation.
You may not realize it, but the union is going to return incorrect results under some circumstances. Union removes duplicates, so if a user has the same partial scores, then the two rows becomes one.
EDIT:
It is a little trickier to get the top 5 scores, but possible:
select user_name,
sum(site_point + park_point + division_point) as points
from visits v join
users u
on u.user_id = v.user_id
group by user_name
having points >= (select distinct points
from (select sum(site_point + park_point + division_point)
from visits v join
users u
on u.user_id = v.user_id
group by user_name
) vu
order by points desc
limit 1 offset 4
)
order by points desc
limit 5;
Assuming that a user_name is unique for a given id you can simplify this a wee bit:
having points >= (select distinct points
from (select sum(site_point + park_point + division_point) as points
from visits v
group by user_id
) vu
order by points desc
limit 1 offset 4
)
And, if you want anyone who matches the top 5 users -- but to return more than 5 rows if there are ties -- then change the select distinct to select in the subquery.

Use the LIMIT command. More info here
So to tack it onto your SQL would result in:
select sum(points)as points, user_name
from
(SELECT sum(site_point) as points, user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(park_point) as points , user_name from visits v join users u on
u.user_id = v.user_id group by user_name
union
select sum(division_point) as points , user_name from visits v join users u
on u.user_id = v.user_id group by user_name
) V group by user_name order by sum(points) DESC LIMIT 5

Related

How to write a query that retrieves 3 replies for each user in my users table?

i want to display three replies from each user i have in my users table, so for instance if i have 3 users and each of them had replied to lets say 10 messages, i want my query to only retrieve 9 replies and not all of the replies in my messages_reply table.
heres what i tried:
$replyquery="select *
from messages_reply
LEFT JOIN users
ON messages_reply.from_id=users.id
GROUP BY messages_reply.id LIMIT 3";
i know that what i wrote means that bring me 3 replies only, so how do i bring 3 replies from each user in my users table?
In many databases, you can use row_number() for this:
select *
from (
select mr.*, u.*, row_number() over(partition by u.id order by mr.id desc) rn
from messages_reply mr
inner join users u on mr.from_id = u.id
) t
where rn <= 3
If you are running MySQL < 8.0, as I suspect from the lax use of group by in your query:
select mr.*, u.*
from messages_reply mr
inner join users u on mr.from_id = u.id
where mr.id >= (
select mr1.id
from messages_reply mr1
where mr1.from_id = u.id
order by mr1.id desc
limit 2, 1
)
This gives you the 3 message replies with the greatest id for each user.
Query not tested, just a concept
SELECT *
FROM users
LEFT JOIN (
SELECT *
FROM messages_reply
WHERE from_id = users.id
ORDER BY <your wanted order field> DESC
LIMIT 3) replies
ON users.id = replies.from_id

MySQL select top rows with same condition values

I don't know how to title this problem. Correct me if you have better words.
I have two tables, Users and Posts.
Users:
id | username | password | ...
Posts:
id | author_id | title | content | ...
Now I want to list the "most active" users - the users who have written the most posts. And specifically, I want the top 10 result.
SELECT u.username, COUNT(p.id) AS count
FROM Posts p, Users u
WHERE u.id=p.author_id
GROUP BY p.author_id
ORDER BY count DESC
LIMIT 10;
I can get the expected result. However, the ranking may not be "fair" if some users have same number of posts.
E.g., I may get results like:
User 1 | 14
User 2 | 13
...
User 9 | 4
User 10 | 4
Here, there are actually several more users who have 4 posts.
So, the top 10 could be not exactly 10 results. How can I get a more "fair" result that contains extra rows of users who have 4 posts?
This is the right solution, I think: you need the subquery to know how much post has the 10th place in your top ten. Then, you use the outer query to extract the users with almost that postcount.
SELECT u.username, COUNT(p.id) AS count
FROM Posts p
JOIN Users u ON u.id = p.author_id
GROUP BY p.author_id
HAVING COUNT(p.id) >=
(
SELECT COUNT(p.id) AS count
FROM Posts p
JOIN Users u ON u.id = p.author_id
GROUP BY p.author_id
ORDER BY count DESC
LIMIT 9, 1
)
ORDER BY count DESC
Maybe not the best solution
select u.username, COUNT(p.id) AS count
FROM Posts p
join Users u on u.id = p.author_id
GROUP BY p.author_id
having COUNT(p.id) in
(
SELECT COUNT(p.id)
FROM Posts p
join Users u on u.id = p.author_id
GROUP BY p.author_id
ORDER BY count DESC
LIMIT 10
)
ORDER BY count DESC
Try this:
SELECT username, PostCount
FROM (SELECT username, PostCount, IF(#PostCount = #PostCount:=PostCount, #idx:=#idx+1, #Idx:=1) AS idx
FROM (SELECT u.username, COUNT(p.id) AS PostCount
FROM Posts p
INNER JOIN Users u ON u.id=p.author_id
GROUP BY p.author_id
) AS A, (SELECT #PostCount:=0, #Idx:=1) AS B
ORDER BY PostCount DESC
) AS A
WHERE idx <= 10;

DISTINCT Is not working in inner join

I have this query
SELECT DISTINCT u.fbid, u.name,r.points
FROM users u, players_records r
WHERE u.fbid = r.user_id
ORDER BY r.points DESC LIMIT 5
I want to get the top players but only different 5 players, this query is not working it shows duplicated users ids
any help ?
Result for the above query
1112222 Name 1 9310
3334444 Name 2 8380
3334444 Name 2 7010
5555666 Name 3 6080
1112222 Name 1 4890
so the ids are duplicated
It sounds like you want the maximum point per user. So you could do it something like this:
SELECT
users.fbid,
users.name,
maxRecords.points
FROM
users
JOIN
(
SELECT
MAX(players_records.points) AS points,
players_records.user_id
FROM
players_records
GROUP BY
players_records.user_id
) AS maxRecords
ON maxRecords.user_id=users.fbid
ORDER BY
maxRecords.points DESC
LIMIT 5
If I understand you data. Then the output will be like this:
1112222 Name 1 9310
3334444 Name 2 8380
5555666 Name 3 6080
If you need TOP 5 players by points:
SELECT TOP 5 u.fbid, u.name, max(r.points) AS points
FROM users u
LEFT JOIN players_records r ON u.fbid = r.user_id
GROUP BY u.fbid, u.name
ORDER BY points DESC
If you need TOP 5 players by SUM points:
SELECT TOP 5 u.fbid, u.name, SUM(r.points) AS points
FROM users u
LEFT JOIN players_records r ON u.fbid = r.user_id
GROUP BY u.fbid, u.name
ORDER BY points DESC
You could just select max score and group by user.
SELECT u.fbid, u.name, MAX(r.points) max_points
FROM users u, player_records r
WHERE u.fbid = r.user_id
GROUP BY u.fbid, u.name
ORDER BY max_points DESC LIMIT 5
Something like this might resemble what you want.
select fbid, u.name, sum(r.points) totalpoints
from users u join players_records r on u.fbid = r.user_id
group by fbid, u.name
order by totalpoints desc
limit 5

Select rows in order of total count

I have a table of users which hold a a users id that they voted for like this:
uid | voted_for
1 | 3
2 | 3
3 | 1
What i'm aiming to do is order uid based on how many people have voted for that uid. But I have no idea how to do it.
So the end result would be:
uid | Total_Votes
3 | 2
1 | 1
2 | 0
Hope you can help explain the best way to structure the SQL for this.
Perhaps something like this will help joining the table on itself:
SELECT u.*, voted_for_cnt
FROM users u
LEFT JOIN (
SELECT voted_for, count(1) voted_for_cnt
FROM users
GROUP BY voted_for
) t ON u.uid = t.voted_for
ORDER BY t.voted_for_cnt DESC
SQL Fiddle Demo
This simple query will produce the output you requested:
select voted_for as uid, count(*) as total_votes
from users
group by 1
order by 2 desc
If you want all data about each user in the output, join users to itself:
select u.*, count(v.uid) as total_votes
from users u
left join users v on v.voted_for = u.uid
group by 1,2,3,4,5 -- put as many numbers here as there are columns in the users table
order by total_votes desc
This second query will give a total_votes score of zero if no one voted for the user.
Alternatively, you can select only those columns you want:
select u.uid, u.name, count(v.uid) as total_votes
from users u
left join users v on v.voted_for = u.uid
group by 1,2
order by 3 desc
```
To return only the winners, do this:
select u.uid, u.name, count(*) as total_votes
from users u
left join users v on v.voted_for = u.uid
group by 1,2
having count(*) = (
select max(c) from (
select count(*) as c from users group by voted_for))
order by 3 desc

Sub Query counting character strings in MySQL

LEFT JOIN
(
SELECT user_id, review, COUNT(user_id) totalCount
FROM reviews
GROUP BY user_id
) b ON b.user_id= b.user_id
I am trying to fit WHERE LENGTH(review) > 100 in this somewhere but every I put it, it gives me problems.
The sub-query above counts all total reviews by user_id. I simply want to add one more qualification. Only count reviews greater than 100 length.
On a side note, I've seen the function CHAR_LENGTH -- not sure if that i what I need either.
EDIT:
Here is complete query working perfectly as expected for my needs:
static public $top_users = "
SELECT u.username, u.score,
(COALESCE(a.totalCount, 0) * 4) +
(COALESCE(b.totalCount, 0) * 5) +
(COALESCE(c.totalCount, 0) * 1) +
(COALESCE(d.totalCount, 0) * 2) +
(COALESCE(u.friend_points, 0)) AS totalScore
FROM users u
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM items
GROUP BY user_id
) a ON a.user_id= u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM reviews
GROUP BY user_id
) b ON b.user_id= u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM ratings
GROUP BY user_id
) c ON c.user_id = u.user_id
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount
FROM comments
GROUP BY user_id
) d ON d.user_id = u.user_id
ORDER BY totalScore DESC LIMIT 25;";
LENGTH() returns the length of the string measured in bytes. You probably want CHAR_LENGTH() as it will give you the actual characters.
SELECT user_id, review, COUNT(user_id) totalCount
FROM reviews
WHERE CHAR_LENGTH(review) > 100
GROUP BY user_id, review
You're also not using GROUP BY correctly.
See the documentation
The query that you want is:
LEFT JOIN
(
SELECT user_id, COUNT(user_id) totalCount,
sum(case when length(review) > 100 then 1 else 0 end
) as NumLongReviews
FROM reviews
GROUP BY user_id
) b ON b.user_id= b.user_id
This counts both the reviews and the "long" reviews. That count is done using a case statement nested in a sum() function.