This question already has answers here:
Rank function in MySQL
(13 answers)
Closed 4 years ago.
SELECT u.user_id, u.user_uid, s.ostats, s.attack, s.defense
FROM stats s JOIN
users u
ON s.id = u.user_id
ORDER BY s.ostats DESC;
So in above data, "ostats"(overall) is just a sum of attack+defense and by using this query I could display users in descending order of their "ostats" values..
But how do I assign and display rank of each user, like the one with most "ostats" valued user as Rank 1 and the second highest "ostats" valued user as Rank 2 and so on..?
What about using a variable to keep track of the row number?
SET #rank = 0;
SELECT
u.user_id,
u.user_uid,
s.ostats,
s.attack,
s.defense,
(#rank:=#rank + 1) AS rank
FROM stats s
JOIN users u on s.id = u.user_id
ORDER BY s.ostats DESC;
You can assign a row number using variables:
SELECT u.user_id,u.user_uid, s.ostats, s.attack, s.defense,
s.ranking
FROM (SELECT s.*, (#rn := #rn + 1) as ranking
FROM (SELECT s.* FROM stats s ORDER BY s.ostats DESC) s CROSS JOIN
(SELECT #rn := 0) params
) s JOIN
users u
ON s.id = u.user_id
ORDER BY s.ostats DESC;
In the event of ties, this will give different users different rankings. If that is an issue, you can use this modified form:
SELECT u.user_id,u.user_uid, s.ostats, s.attack, s.defense,
s.ranking
FROM (SELECT s.*,
(#rn := if(#o = ostats, #rn,
if(#o := ostats, #rn + 1, #rn + 1)
)
) as ranking
FROM (SELECT s.* FROM stats s ORDER BY s.ostats DESC) s CROSS JOIN
(SELECT #rn := 0, #o := -1) params
) s JOIN
users u
ON s.id = u.user_id
ORDER BY s.ostats DESC;
Of course, in MySQL 8.0, you can use row_number(), rank() or dense_rank() for this purpose.
Related
I'm trying to rank with the left outer join
My sql is :
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, h.name, COUNT(v.hId) AS votes
FROM users h LEFT OUTER JOIN users_votes v ON h.id = v.hId GROUP BY h.id
ORDER BY rank ASC
;
The right thing would be to return like this
rank | name | votes
1 Luck 4
2 Marc 3
3 Santos 2
4 Matheus 0
But it's returning the wrong way:
rank | name | votes
1 Santos 2
2 Marc 3
3 Luck 4
4 Matheus 0
You are ordering in ASC way, change it to DESC. Like this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, h.name, COUNT(v.hId) AS votes
FROM users h LEFT OUTER JOIN users_votes v ON h.id = v.hId GROUP BY h.id
ORDER BY rank DESC
;
In MySQL 8+, you should use row_number():
SELECT ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as rank,
h.name, COUNT(*) AS votes
FROM users h LEFT OUTER JOIN
users_votes v
ON h.id = v.hId
GROUP BY h.id
ORDER BY rank ASC;
Variables are deprecated in 8+.
In earlier version, you can use variables, but they tend not to respect ORDER BY or GROUP BY. So, with those constructs, you need a subquery:
SELECT (#rn := #rn + 1) as rank, hv.*
FROM (SELECT h.name, COUNT(*) AS votes
FROM users h LEFT OUTER JOIN
users_votes v
ON h.id = v.hId
GROUP BY h.id
ORDER BY rank ASC
) hv CROSS JOIN
(SELECT #rn := 0) params;
Doesn't works, the rank number is wrong. I want to sort by votes and rank the most voted
I Hope someone could help me.
My Query.
SELECT * FROM `tbl_device`
INNER JOIN `tbl_temperature` ON tbl_device.ID = tbl_temperature.DevID
Result.
How can get something like this as shown in the photo on the link.
I need to get only the last 3 result of the tbl_device.DevID
I apologize for my bad english, it's really hard to explain. Any help is realy much appreciated.
Thanks a lot!
It should be something like
SELECT * FROM `tbl_device`
INNER JOIN `tbl_temperature` ON tbl_device.ID = tbl_temperature.DevID
ORDER BY tbl_temperature.DevID DESC
LIMIT 3 OFFSET 0
The order column can be not the same as in my example. Google MySQL LIMIT OFFSET
Or if you need to get only three last results from one of your tables then join it to another then it will be something like
SELECT * FROM tbl_device INNER JOIN
(SELECT .... FROM other_table ORDER BY your_column DESC LIMIT 3 OFFSET 0) as
T1 ON T1.id = tbl_device.ID
The simplest way is to use ANSI-standard window functions (row_number() in particular). But MySQL does not support those (yet).
In MySQL, probably the best way is to use variables:
SELECT . . .
FROM tbl_device d JOIN
(SELECT t.*,
(#rn := if(#d = t.devid, #rn + 1,
if(#d := t.devid, 1, 1)
)
) as rn
FROM (SELECT t.*
FROM tbl_temperature t
ORDER BY DevID, id DESC
) t CROSS JOIN
(SELECT #d := -1, #rn := 0) params
) t
ON tbl_device.ID = tbl_temperature.DevID
WHERE rn <= 3;
EDIT:
Here is a simpler way to express the logic. It might be performant with the right indexes:
SELECT d.*, t.*
FROM tbl_device d INNER JOIN
tbl_temperature t
ON d.ID = t.DevID
WHERE t.ID >= (SELECT t2.ID
FROM tbl_temperature t2
WHERE t2.DevId = t.DevId
ORDER BY t2.ID DESC
OFFSET 2 LIMIT 1
);
For performance, this can use an index on tbl_temperature(devid, id).
Let's say I have two tables like the following:
user_id
1
2
3
post_id user_id
1 2
2 3
3 2
The first table has all of the user information and the second table is a list of posts from users.
With these table, I want to make a ranking of who post the most posts. To do this, I can use the following sql
select user.user_id from user
left join post on user.user_id=post.user_id
order by count(post.post_id)
which will give me
user_id
2
3
1
, but what if I only want a single user's rank? In other words, I want to write a sql statement that will return what place the user is in given the user_id. For example, I want 1 as output if I have user_id of 2, 2 as the output if I have user_id 3, and 3 as the output if I have user_id 1.
Is this possible, or would I have to select the entire table and do a while loop in php until I hit the user and count the rows above?
Unfortunately, for a single user's rank, you pretty much have to calculate the rank of everyone and then pull out the single user. So, the ranking for all users is:
select u.user_id, count(p.post_id), (#rn := #rn + 1) as ranking
from user u left join
post p
on u.user_id = p.user_id cross join
(select #rn := 0) params
group by u.user_id
order by count(p.post_id) desc;
And for one user, use a subquery:
select *
from (select u.user_id, count(p.post_id), (#rn := #rn + 1) as ranking
from user u left join
post p
on u.user_id = p.user_id cross join
(select #rn := 0) params
order by count(p.post_id) desc
) u
where u.user_id = $USERID;
You can use user-defined variables for this:
select rnk
from (
select user.user_id, #rnk:#rnk+1 rnk
from user
left join post on user.user_id=post.user_id
cross join (select #rnk:=0) t
group by user.user_id
order by count(post.post_id) desc
) t
where user_id = ?
BTW -- I believe your query was missing a group by clause. Added above.
I have like this table at my mysql db for highscore.
and I got SQL for get rank of all users.
SELECT b.id
, b.name
, #rank_cnt := IF(#prev_score = b.score,#rank_cnt,#rank_cnt+1) AS rank
, #prev_score := b.score AS score
FROM BBR b
CROSS
JOIN ( SELECT #rank_cnt := 0, #prev_score := NULL) i
ORDER BY b.score DESC, b.id DESC
if I run above SQL, I get following result,
But I want to know from here, specific user's rank info only.
If I wrote WHERE name = 'sim' before ORDER BY, his rank become 1.
I expect here '4' as result.
How should I revise?
Thanks much.
SET #rank_cnt := 0;
SET #prev_score := NULL;
SELECT * FROM (
SELECT b.id
, b.name
, #rank_cnt := IF(#prev_score = b.score,#rank_cnt,#rank_cnt+1) AS rank
, #prev_score := b.score AS score
FROM BBR b
ORDER BY b.score DESC, b.id DESC
) AS subQ
WHERE subQ.name = "sim";
If you are using the same connection, you shouldn't need that bogus "JOIN" to initialize your session variables.
You can try below query,
select name
,Find_in_set(score, (select group_concat(distinct score order by score desc) from BBR)) rank
from BBR
where name='sim';
I need to place set #rank:=0; in this query, but where can i place it?
SELECT #rank:=#rank+1 AS rank, p.* FROM points p
inner join distributor d
on p.distributor_id=d.id_distributor
where p.month='$prev_month'
and d.group='$dist_group'
ORDER BY p.tot_point DESC
i have to use mysql_query("set #rank:=0;"); before the main query, it's works. but in another server it won't work.
any ideas ?
SELECT #rank:=#rank+1 AS rank, p.* FROM points p, (SELECT #rank:=0) AS dummy
inner join distributor d
on p.distributor_id=d.id_distributor
where p.month='$prev_month'
and d.group='$dist_group'
ORDER BY p.tot_point DESC
Basicly just add , (SELECT #rank:=0) AS dummy after FROM points p.
My prefered layout on this query:
SELECT #rank := #rank + 1 AS rank, p.*
FROM
points AS p
CROSS JOIN
(SELECT #rank:=0) AS dummy
INNER JOIN
distributor AS d
ON p.distributor_id = d.id_distributor
WHERE p.month = '$prev_month'
AND d.group='$dist_group'
ORDER BY p.tot_point DESC ;