SQL statement for getting user rank in proper order? - php

I am looking for an SQL statement that returns users rank by score, but does so in order, which is not happening with the code below:
$sql = "SELECT COUNT(*) AS rank FROM users WHERE points>=(SELECT points FROM users WHERE uid='$uid')";
Instead, each user has the same rank depending on how many users there are. So if there are 25 users, each of their ranks will display as 25 but this is not actually the case, as surely there must be an individual rank for each user.
I have tried a couple of different of different solutions, but I can't quite put the logic together. I thought this would work:
$sql ="SELECT COUNT(*)-1 AS rank FROM users WHERE points>=(SELECT points FROM users WHERE uid='$uid' ORDER BY DESC)";
But I've had no success.
TLDR; How could I change my SQL statement to reflect users rank in order by score, without multiple users having the same rank?

If you are trying to order the data in the table using the points and give them individual rank you can use ROW_NUMBER()
SELECT *,ROW_NUMBER() OVER (ORDER BY points DESC) AS RANK FROM users
ROW_NUMBER() is not available in MYSQL. You can do following for MYSQL
SELECT
#r:=#r+1 Rank,tbl.*
FROM users tbl, (select #r:=0) as r
ORDER BY users.points DESC;
Consider you have data
Points
3
7
8
Using Select statement with ROW_NUMBER() you will get result as follows
Result:
Points Rank
8 1
7 2
3 3

Related

Getting ordered rows partly

Hello developers out there,
I want to create a ranking list in my program, and for that want to get the users of the mysql database ordered by their points. I'm doing this with:
select `name`,`points` from users order by `points` desc
For better performance i don't want to get all users but just 10+ and 10- from a given user, because i don't want to show the whole ranking list in the program anyways. Could somebody help me with this?
One method uses subqueries and union all:
(select name, points
from users
where points <= (select u2.points from users u2 where u2.name = ?)
order by points desc
limit 11
) union all
(select name, points
from users
where points > (select u2.points from users u2 where u2.name = ?)
order by points asc
limit 10
) ;
This returns 21 rows, with the specified user included.

Join multiple tables and order by sum of row

Recently began working on a matchmaking system, where in this system there are 2 tables. One table is for matches and one table is for ranks.
The table for ranks starts off like this
table_ranks:
date
playeruid
rank
table_matches:
date
playeruid
playerpoints
I'm trying to now order the player by their points. However, in order to do this, I have made a query:
SELECT * FROM table_ranks ORDER by rank DESC
But now what I realize, is that I need to add the playerpoints on top of the rank. So basically, I need to add playerpoints to the player's current ranking. So, if I had this for an example row:
table_ranks:
2/20/15
Player1
56
table_matches:
2/27/15
Player1
5
I would need to build a query that takes the 56 of player1, looks for player1 in the matches and anywhere it sees it, it would need to add his 5 points making it a sum of 56. Once this is determined it would ORDER by this value in order to determine who is ranked with what. How do I begin my query? I understand that in order to join the tables, I need to start off like this:
"SELECT table_ranks., table_matches. from table_ranks, table_matches ORDER by RANK..."
Then to finish it,I would have to take the current value of the rank, then grab the specific player it's referring to and take all the matches and add up all the playerpoints to his rank then to determine how to order it by.
Try this:
SELECT r.playeruid, r.date AS rank_date, m.date AS macthes_date,
(r.rank + m.playerpoints) AS total_points
FROM table_ranks r INNER JOIN table_matches m ON r.playeruid = m.playeruid
ORDER BY total_points DESC
This query assumes that playeruid is unique in both tables.
Try the following query. I tested on a similartable structure and it should work
SELECT * , playeruid AS player_id, (
SELECT SUM( playerpoints )
FROM `table_matches`
WHERE playeruid = player_id
) AS points
FROM table_ranks
ORDER BY points DESC

MySQL select statement - How to calculate the current ranking

I have a table called user_rankings where votes (voted) are stored for each user. I want to display the current ranking of users (this week) that depends on how much votes the user got.
example to clarify:
RANK-NR, USERNAME, VOTED,
1, name1, 18 times
2, name1, 16 times
(my ranking here), myname, 13 times
In this example my ranking should be 3. If I'd have 17 votes, I would be number 2. If there would be five users above me, I would be number 8. I guess you get the point.
Now I can display the ranking number easily with an incrementing $i in PHP. But I only want to show a list limited to ten users (a top ten list) and directly after that my current ranking, if I'm not already in that top ten list. So I'm just wondering how to get my exact ranking number using MySQL.
I'm assuming to have hundreds of users in this list with a different amount of votes.
This is my statement at the moment:
SELECT
`voted`
FROM `users_ranking`
WHERE
`uid`='".$_SESSION['uid']."'
AND
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW())
LIMIT 1
I can't give you the exact code, but i think the following can give you some idea
select 'RANK-NR', 'USERNAME', 'VOTED' from
(
select 'RANK-NR', 'USERNAME', 'VOTED', rank() over (order by 'voted' desc) as rank
from users_ranking
where
uid='".$_SESSION['uid']."'
AND
WEEKOFYEAR(date)=WEEKOFYEAR(NOW())
) as abc
where
rank<11
i think rank() over (order by<>) should work
I just found out myself that this solution works:
SELECT *
FROM
(
SELECT #ranking:= #ranking + 1 rank,
a.`uid`
FROM `users_ranking` a, (SELECT #ranking := 0) b
ORDER BY a.`votes` DESC
) s
WHERE `uid`='".$_SESSION['uid']."'
AND
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW())
LIMIT 1
OK, example to go with my comment. What you have will often work, but there is nothing to force MySQL to do the sort before it applies the ranking.
As such using an extra level of sub query would give you this (not tested). The inner sub query is getting all the user ids for the relevant week in the right order, while the next outer sub query applies the ranking to this ordered result set. The outer query just gets the single returned row you require.
SELECT c.rank, c.uid
FROM
(
SELECT #ranking:= #ranking + 1 rank, a.uid
FROM
(
SELECT uid, votes
FROM `users_ranking`
WHERE WEEKOFYEAR(`date`) = WEEKOFYEAR(NOW())
ORDER BY votes DESC
) a,
(SELECT #ranking := 0) b
) c
WHERE c.uid = '".$_SESSION['uid']."'
LIMIT 1
Another possibility avoiding the sub query and also avoiding the need for a variable is to do a join. This is (mis)using HAVING to slim down the result to the single row you are interested in. Down side of this solution is that if multiple users have the same score they will each get the same ranking.
SELECT b.uid, COUNT(a.uid)
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`)
AND a.votes >= b.votes
GROUP BY b.uid
HAVING b.uid = '".$_SESSION['uid']."'
EDIT
To give the top 10 rankings:-
SELECT b.uid, COUNT(a.uid) AS rank
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`)
AND a.votes >= b.votes
GROUP BY b.uid
ORDER BY rank
LIMIT 10
Although in this case it might be quicker to use a sub query. You could then put the LIMIT clause in the sub query with the ORDER BY, hence it would only need to use the variables to add a rank to 10 rows.
I am not sure how to combine that with the query for a single user, mainly as I am not sure how you want to merge the 2 results together.

what is the best practice leaderboard with php, mysql, memcached?

Recently I have developed mobile game (LAMP + memcached).
The game has player's score table. The table has member_id, name, score column.
I want to show leaderboard (global rank) to our user.
Just query SELECT * FROM score ORDER BY score DESC, and show the resultset.
But I don't think it is good way. If users reach 20 million, this query seem to be terrible. What is the best practice in this case? Could you give me some advice?
And how can I check specific user's rank? I want to show each user rank where they are.
Well, you don't want to display millions of users on the leader board do you?
SELECT * FROM score ORDER BY score DESC LIMIT 10
Would select top 10
Further, if you want to display a users rank on for instance a profile page this should do:
SELECT COUNT(*) + 1 AS rank FROM score WHERE score > (SELECT score FROM score WHERE member_id = :member_id);
You simply add the current users member_id to the last query and it will count all rows ahead of him and add 1 to that.

PHP MySQL Top 5 Referers Function

I have a table called users which looks like:
-id
-email
-login
-admin
-coins
-cash
-premium
-IP
-pass
-ref
-signup
-online
-promote
-activate
-banned
-rec_hash
-country
-c_changes
-sex
-daily_bonus
If say user with id 81 referred 10 people then those 10 people would have "81" in their ref column.
I would like to create a top 5 referral table but I'm having trouble with the query and displaying that in PHP, would anybody be able to help?
I FORGOT TO MENTION IF THEY HAVE NO REFERRAL IT SHOWS AS 0 HOW WOULD I EXCLUDE 0 FROM BEING SHOWN AS A REFERRAL?
You can do it in a single SQL statement like this:
SELECT ref, COUNT(*) AS num FROM users
GROUP BY ref ORDER BY num DESC LIMIT 5
But that will just get you the 5 IDs, rather than their user rows. You can then perform a further query to get the actual rows. Alternatively, use the above query with a join to do it all in one.
IF THEY HAVE NO REFERRAL IT SHOWS AS 0
messy design - this should be null. Regardless...
SELECT u.login, ilv.referred
FROM
(SELECT ref, COUNT(*) AS referred
FROM users
WHERE ref IS NOT NULL
AND ref>0
GROUP BY ref
ORDER BY COUNT(*) DESC
LIMIT 0,5) ilv
INNER JOIN users u
ON ilv.ref=users.id
ORDER BY ilv.referred DESC;
Or and SQL like this:
SELECT u.*, COUNT(*) as referrers FROM users r JOIN users u ON r.ref = u.id
GROUP BY u.id ORDER BY referrers DESC LIMIT 5
It is faster to use just one statement even with a join on the same table.

Categories