I have a database in which people submit votes for people in different places. At a given time, I want to find out who has the most votes at each place. (A person can be voted at two different places)
This is the SQL I have so far:
SELECT placeId, userVotedId, cnt
FROM
(SELECT uvo.userVotedId, p.placeId, count(*) as cnt
FROM users as u, users_votes as uvo, places as p
WHERE u.userId = uvo.userVotedId
AND p.placeId = uvo.placeId
GROUP BY userVotedId, placeId)
AS RESULT
which gives me this result:
Now, these are the rows I REALLY want:
What's missing in my query so I can get this?
I want one result per place. So I should see only distinct placeIds, with the userVotedId who received the most votes.
In the event of a tie, a random winner will do!
How do you get max for each group in SQL? To find the maximum value of a column, use the MAX() aggregate function; it takes a column name or an expression to find the maximum value. In our example, the subquery returns the highest number in the column grade (subquery: SELECT MAX(grade) FROM student ).
MySQL MAX() function with GROUP BY retrieves maximum value of an expression which has undergone a grouping operation (usually based upon one column or a list of comma-separated columns).
Now we can move ahead to write our SQL query for finding maximum values in all the rows, This can be done using MAX(field) function in SQL.
Seems like you need one more aggregate. Use the MAX()
aggregate on your cnt
value and GROUP BY placeId, userVotedId
:
SELECT placeId, userVotedId, max(cnt)
FROM
(
SELECT uvo.userVotedId, p.placeId, count(*) as cnt
FROM users as u
INNER JOIN users_votes as uvo
ON u.userId = uvo.userVotedId
INNER JOIN places as p
ON p.placeId = uvo.placeId
GROUP BY userVotedId, placeId
) AS RESULT
GROUP BY placeId, userVotedId
Note: I changed your query to use JOIN
syntax instead of the commas between the tables.
Edit, based on your comment the following should work:
select total.uservotedid,
total.placeid,
total.cnt
from
(
SELECT uvo.userVotedId, p.placeId, count(*) as cnt
FROM users as u
INNER JOIN users_votes as uvo
ON u.userId = uvo.userVotedId
INNER JOIN places as p
ON p.placeId = uvo.placeId
GROUP BY userVotedId, placeId
) total
inner join
(
select max(cnt) Mx, placeid
from
(
SELECT uvo.userVotedId, p.placeId, count(*) as cnt
FROM users as u
INNER JOIN users_votes as uvo
ON u.userId = uvo.userVotedId
INNER JOIN places as p
ON p.placeId = uvo.placeId
GROUP BY userVotedId, placeId
) mx
group by placeid
) src
on total.placeid = src.placeid
and total.cnt = src.mx
See SQL Fiddle with Demo
The result is:
| USERVOTEDID | PLACEID | CNT |
-------------------------------
| 65 | 11 | 1 |
| 67 | 13 | 1 |
| 67 | 25 | 1 |
| 67 | 51 | 2 |
Edit #2, if you want a random number returned if there is a tie, then you can use user variables:
select uservotedid,
placeid,
cnt
from
(
select total.uservotedid,
total.placeid,
total.cnt,
@rownum := case when @prev = total.placeid then @rownum+1 else 1 end rownum,
@prev := total.placeid pplaceid
from
(
SELECT uvo.userVotedId, p.placeId, count(*) as cnt
FROM users as u
INNER JOIN users_votes as uvo
ON u.userId = uvo.userVotedId
INNER JOIN places as p
ON p.placeId = uvo.placeId
GROUP BY userVotedId, placeId
) total
inner join
(
select max(cnt) Mx, placeid
from
(
SELECT uvo.userVotedId, p.placeId, count(*) as cnt
FROM users as u
INNER JOIN users_votes as uvo
ON u.userId = uvo.userVotedId
INNER JOIN places as p
ON p.placeId = uvo.placeId
GROUP BY userVotedId, placeId
) mx
group by placeid
) src
on total.placeid = src.placeid
and total.cnt = src.mx
order by total.placeid, total.uservotedid
) src
where rownum = 1
order by placeid, uservotedid
See SQL Fiddle with Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With