Storing highscore in database efficiently - mysql

How can i store a highscore efficiently in database?
I have a table player_score with two columns player_id (primary key), score (integer value).
Now i want the position of a player in the highscore and i created the following query
SELECT player_score.player_id,
(SELECT COUNT(*) FROM player_score ps WHERE ps.score >= player_score.score) AS position
FROM player_score
WHERE player_score.player_id = 19
I think this is very inefficiently have you any other ideas? It is also conceivable to use elasticsearch or any other technology to store this data efficiently.

You can use the following query to get the exact position of a user in the high scores list:
SELECT COUNT(*)+1 AS pos
FROM (SELECT *
FROM player_score
ORDER BY score DESC) AS ordered_scores, player_score
WHERE player_score.player_id=3 AND player_score.score < ordered_scores.score;

Related

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
) ;

Select rank of a specific player sql query

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');

How do I make this personal best highscore MySQL query more efficient?

I have a database for game leaderboard highscores holding currently about 30.000 entries, and the below query looks for the personal best highscore for a certain game and player, but it takes about 60 seconds to execute. I'm thinking there should be a much more efficient way to do this, maybe using a composite index, but which one would that be?
SELECT name, score, date, version, mode, attempts, time, id
FROM highscore h1
WHERE score = (
SELECT MAX( score )
FROM highscore h2
WHERE name = h1.name && gamename = "asteroids" && name = "bob"
)
I currently have indexes for:
id
score
name
name-gamename (composite)
Any help is greatly appreciated!
I would write the query using standard SQL syntax:
SELECT name, score, date, version, mode, attempts, time, id
FROM highscore h1
WHERE score = (SELECT MAX( score )
FROM highscore h2
WHERE h2.name = h1.name AND h2.gamename = 'asteroids' AND h2.name = 'bob'
);
For this purpose, you want a composite index: highscore(name, gamename, score).
If you are only looking for one row (even when there are ties), then this might be a wee bit faster:
SELECT name, score, date, version, mode, attempts, time, id
FROM highscore h
WHERE h.gamename = 'asteroids' AND h.name = 'bob'
ORDER BY score DESC
LIMIT 1;
The appropriate index is highscore(name, gamename, score).

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

Selecting values grouped to a specific identifer

I have an application that tracks high scores in a game.
I have a user_scores table that maps a user_id to a score.
I need to return the 5 highest scores, but only 1 high score for any specific user.
So if user X has the 5 highest scores on a purely numerical basis, I simply return the highest one and then the next 4 user scores.
I have tried to use:
SELECT user_id, score
FROM user_scores
ORDER BY score DESC
GROUP BY user_id
LIMIT 5
But it seems that MySQL drops any user_id with more than 1 score.
This should work:
SELECT user_id, MAX(score)
FROM user_scores
GROUP BY user_id
ORDER BY MAX(score) DESC
LIMIT 5
SELECT user_id, MAX(score) AS score
FROM user_scores
GROUP BY user_id
ORDER BY score DESC
LIMIT 5
Should do the job for you... though don't forget to create indexes...
You can't group by without a summary-function (SUM, COUNT, etc.)
The GROUP BY clause says how to group the SUMs or COUNTs.
If you simply want to break the long list into bunches with a common value, that's not SQL. That's what your application has to do.
Can you use the Distinct operator to say
SELECT DISTINCT(user_id), score
FROM user_scores
ORDER BY score DESC
LIMIT 5
didn't test so not sure if that will definitely work
Returning only the maximum score for a given user is something like the following.
SELECT user_id, max(score) FROM user_scores
GROUP BY user_id
I don't know whether it was a lack of caffeine or just brain explosion, but the answers here were so easy.
I actually got it working with this monstrosity:
SELECT s1.user_id,
(SELECT score FROM user_scores s2 WHERE s2.user_id = s1.user_id ORDER BY score DESC LIMIT 1) AS score
FROM user_scores s1
GROUP BY s1.user_id
ORDER BY s1.score DESC
LIMIT 5