Select rank of a specific player sql query - mysql

I'm trying to make a query and I already searched for an answer on stackof but didn't find one matching my needs.
I have a table named player in which there are two columns, "nickname" and "score".
I use this query to get the top 5 players:
SELECT nickname, score
FROM player
ORDER BY score DESC LIMIT 5;
and I got this as the answer:
nickname - score:
zod - 30
ciao - 20
jiji - 20
mayina - 20
jon - 0.
Now, I'd like to have the rank of a single player, let's say "jiji" and get 3 as a result, because it's the third result in the list.
I tried many queries like
SELECT COUNT(*) AS rank
FROM player
WHERE score >= (SELECT score FROM player WHERE nickname = 'jiji')
but they always return 4 for "jiji" or "ciao", which is the rank of the last player who gets 20 as score in that table.
How can I get to have 3 for "jiji", instead? Thank you very much.

Try this:
SET #rank=0;
SELECT *
FROM (SELECT #rank:=#rank+1, nickname, score
FROM player
ORDER BY score
DESC) AS t
WHERE t.nickname = 'jiji';
Correct comment about this not being stable in case of score ties. To make it stable, we can change the sorting to be based on score and then nickname:
SELECT *
FROM (SELECT #rank:=#rank+1, nickname, score
FROM player
ORDER BY score, nickname
DESC) AS t
WHERE t.nickname = 'jiji';

Using commonly used definitions, the rank for jiji would be:
SELECT count(*) + 1 AS rank
FROM player
WHERE score > (SELECT score FROM player WHERE nickname = 'jiji');
This returns "2", because there are ties when score = 30.
If you want the rank to be stable and different for each row, you need an additional key. An obvious key (in this case) is nickname:
SELECT count(*) AS rank
FROM player p CROSS JOIN
(SELECT score FROM player WHERE nickname = 'jiji') s
WHERE p.score > s.score or
(p.score = s.score and p.nickname <= 'jiji');

Related

How can I select the value of a column depending on where the maximum value between a second and third column is, in sql?

I am a first year student learning SQL. I would like to know how I can select the data_jogo (date_game) value, where a certain player achieved his maximum score, in a match.
This player can appear as player 1 (id_jogador1) or player 2 (id_jogador2).
Table name is "partidas", translating it means matches, and
"pontuacao_jog1" means player 1's score, and "pontuacao_jog2" is the same for player 2.
enter image description here
I tried this way, but doesn't work (#1111 - Uso inválido de função de agrupamento (GROUP)):
SELECT partidas.data_jogo AS Date
FROM partidas
WHERE (partidas.id_jogador1 = 'CR7' OR partidas.id_jogador2 = 'CR7')
AND GREATEST (max (partidas.pontuacao_jog1), max (partidas.pontuacao_jog2));
Can someone help please?
Thank you, João
You can use UNION ALL to get a player's scores in an easy way. Then you can order the scores and use LIMIT to get the row with the maximum score:
WITH scores AS
(
SELECT pontuacao_jog1 AS score, data_jogo
FROM partidas
WHERE id_jogador1 = 'CR7'
UNION ALL
SELECT pontuacao_jog2 AS score, data_jogo
FROM partidas
WHERE id_jogador2 = 'CR7'
)
SELECT *
FROM scores
ORDER BY score DESC
LIMIT 1;
Only problem: If there are two days with the same maximum score, you'll only show one of them arbitrarily chosen. If you want to show both:
WITH scores AS ( <same as above> )
SELECT *
FROM scores
WHERE score = (SELECT MAX(score) FROM scores);
And here is an alternative way to write the CTE (aka WITH clause):
WITH scores AS
(
SELECT
CASE WHEN id_jogador1 = 'CR7'
THEN pontuacao_jog1
ELSE pontuacao_jog2
END AS score,
data_jogo
FROM partidas
WHERE (id_jogador1 = 'CR7' OR id_jogador2 = 'CR7')
)

MYSQL query and pulling second column of data based on first column

Hopefully I can explain this well. I'm trying to pull in my database the max points, assists, and rebounds from one column and then based on that number, grab the game number (gid) where each of those numbers came from. I started it with something simple, thinking I could get it, but the gid it grabs is just the first id for that particular season, not for the right game in any case.
SELECT gid, sid, max(points), max(assists), max(rebounds)
FROM game_stats_lakers
WHERE playerid = 2
GROUP By gid
I want to get the gid (which is the game id) for the max points, assists, and rebounds which most likely will be different for each. I can't seem to figure out how to pull any gid correctly.
Thanks for any help!
Hmmm . . . Is this what you want?
(select gsl.*
from game_stats_lakers gsl
where playerid = 2
order by gsl.points desc
limit 1
) union all
(select gsl.*
from game_stats_lakers gsl
where playerid = 2
order by gsl.assists desc
limit 1
) union all
(select gsl.*
from game_stats_lakers gsl
where playerid = 2
order by gsl.rebounds desc
limit 1
) ;

Calculating rank not working - Mysql

I have a DB Table user_points which contains user's points and I am trying to calculate ranking based on points. It is working fine for all users except users having 1 point.
If user have 1 point it is showing it's rank as 0 but it should display it's rank as last or in last numbers like: 12083 etc.
Higher points are, ranking should be higher as well. For example:
1000 points = rank 1
1 point = rank 1223
Following is the query.
SELECT id, mobileNo, points,
FIND_IN_SET( points, (SELECT GROUP_CONCAT( points ORDER BY points DESC )
FROM users_points )) AS rank
FROM users_points
WHERE mobileNo = '03214701777'
What should I change to fix it?
SELECT a.id, a.mobileNo, a.points,
IFNULL((SELECT COUNT(*) AS rank
FROM users_points b
WHERE b.points<a.points), 0)+1 as rank
FROM user_points a
WHERE a.mobileNo = '03214701777'
Seems to be what you are looking for. While it is still very innefficient it is better than your approach using FIND_IN_SET(). If you really want to use FIND_IN_SET() then you need to pad the scores to a consistent width and divide by the width+1 to get the rank.

SQL query to return the three highest values for one column "grouped" by another column

Let's say I have a table like this:
Player Score
A 5
B 4
A 3
B 2
A 1
B 1
A 2
B 3
A 4
B 5
I need an SQL query that will return the three highest scores per player in descending order "grouped" by player i.e.
Player Score
A 5
A 4
A 3
B 5
B 4
B 3
Very grateful for any pointers.
This is old-fashioned (read: basic sql) way of producing top-n per group. You might join the table to itself on group condition (here it is player) and pick records with higher score on right side; if there are three or less such records, the row is one of top n rows per group.
select player.player, player.score
from Player
left join Player p2
on p2.player = player.player
and p2.score > player.score
group by player.player, player.score
having count(distinct p2.score) < 3
order by 1, 2 desc
Alternative version you might check, using not exists:
select player, score
from player
where not exists
(
select p2.player
from Player p2
where p2.player = player.player
and p2.score > player.score
group by p2.player
having count(distinct p2.score) > 3
)
order by 1, 2 desc
This two versions differ in presentation of ties - while first one returns one row (by nature of group by) and needs to be joined back to original table to show all records, second one works directly from original table showing all data and ties at once.
You can find Demo at Sql Fiddle.
in SQL server:
select p.player, p.score
from PS p
where p.score in (select top 3 score from PS
where player = p.player order by score desc)
order by p.player asc, p.score desc
in MySql:
select p.player, p.score
from PS p
where p.score in (select score from PS
where player = p.player order by score desc limit 3)
order by p.player asc, p.score desc
I think what you are looking for can be found here:
http://www.sql-ex.ru/help/select16.php
Basically, the best solution uses the RANK function. Here is the example code from the site:
SELECT maker, model, type FROM
(
SELECT maker, model, type, RANK() OVER(PARTITION BY type ORDER BY model) num
FROM Product
) X
WHERE num <= 3
You would just need to modify the Partition By section to order by your score in descending order.
EDIT
Based upon the information that you will be using MySQL, you will need to make some modifications to the above query (which works with Microsoft SQL). You need to replace the RANK function with your own RANK implementation. It isn't that hard. Complete instructions can be found here:
http://thinkdiff.net/mysql/how-to-get-rank-using-mysql-query/
That will show you how to implement a counter that can give you a rank number.
Depending on what DBMS you use, you may be able to use row_number in some form
In SQL Server 2008 you can use
create table #player
( Player char, Score int )
insert into #player (Player, Score) Values
('A',5),('B',4),('A',3),('B',2),('A',1),('B',1),('A',2),('B',3),('A',4),('B',5)
select * from #player
select Player, Score from
(
select *, ROW_NUMBER() over(partition by Player order by Score desc) as rowNo
from #player
) as tmp
where tmp.rowNo <= 3
drop table #player

sql and getting elements around a specific one

My sql database stores the highscores for the players of a game like this:
playerName
scores
To show the scores I'm trying to create a query that returns for a playerId "joe" the positon of joe as well as one player with more and less points than joe:
(rank) (playerName) (scores)
5000 luci 2001
5001 joe 1900
5002 marc 1750
(I added the rank here just to make it more clear. It isn't stored and represents just the position in the list when sorting by the scores)
Is there a way to do soemthing like this with a query without getting half of the database as result?
You'd ideally want to be using window functions to do this, namely row_number(), lead() and lag().
These are not available in MySQL, but here are a few workarounds (also row_number(), picked up from the same author).
Using window functions:
with
players as (
select row_number() over w as rank,
playername,
scores
from players
window w (order by scores desc)
),
player as (
select rank
from players
where playername = 'joe'
)
select rank,
playername,
scores
from players
join player on players.rank between player.rank - 1 and player.rank + 1
Alternatively (slightly faster):
with
player_range as (
select scores - 100 as scores -- or whatever is large enough based on your stats
from players
where playername = 'joe'
),
players as (
select row_number() over w as rank,
players.playername,
players.scores
from players
join player_range
on player_range.scores <= players.scores
window w (order by scores desc)
),
player as (
select rank
from players
where playername = 'joe'
)
select rank,
playername,
scores
from players
join player on players.rank between player.rank - 1 and player.rank + 1
To determine joe's rank you have to query the db and order by scores.