Вычисление ранга в PHP / MySQL

У меня есть таблица в MySQL, которая позволяет просто сказать, например, что она получила два поля: Username, GameName и Score. Я хочу рассчитать ранг пользователя для индивидуального имени игры, чтобы я мог выполнить запрос

SELECT * FROM scores WHERE `GameName` = 'Snake' ORDER BY `Score` DESC 

чтобы получить список всех пользователей в порядке наивысшего наименьшего значения и присвоить номер каждому пользователю.

Но есть ли более простой способ получить звание отдельного пользователя, а не выбирать всю таблицу, поскольку это не кажется слишком эффективным.

благодаря

Если вам нужен общий рейтинг, вам, к сожалению, придется сортировать всю таблицу. Проще говоря, вы не можете знать чей-то звание в таблице, не зная других рангов в таблице.

Тем не менее, если вас беспокоит производительность, здесь есть довольно простое решение – кешируйте результат вашего запроса на ранжирование (возможно, в другую таблицу MySQL!) И запрашивайте это для всех ваших чтений. Когда кто-то публикует новый балл, пересчитайте временную таблицу. Вы можете периодически очищать все записи под определенным рейтингом (скажем, любой рейтинг менее 100 удаляется из таблицы баллов), чтобы быстро перекомпилировать, так как никто никогда не залезет в ранг после того, как будет сбит более высокий балл.

 # Create your overall leaderboards once create table leaderboards (rank integer primary key, score_id integer, game varchar(65), user_id integer, index game_user_id_idx (game, user_id)) # To refresh your leaderboard, we'll query the ranks for the game into a temporary table, flush old records from scores, then copy # the new ranked table into your leaderboards table. # We'll use MySQL's CREATE TABLE...SELECT syntax to select our resultset into it directly upon creation. create temporary table tmp_leaderboard (rank integer primary key auto_increment, score_id integer, game varchar(65), user_id integer) select ID, GameName, UserID, from scores where GameName = '$game' order by score desc; # Remove old rankings from the overall leaderboards, then copy the results of the temp table into it. delete from leaderboards where game = '$game'; insert into leaderboards (rank, score_id, game, user_id) select rank, score_id, game, user_id from tmp_leaderboard; # And then clean up the lower scores from the Scores table delete from scores join tmp_leaderboard on scores.id = tmp_leaderboard.score_id, scores.GameName = tmp_leaderboard.game where tmp_leaderboard.rank < 100; # And we're done with our temp table drop table tmp_leaderboard; 

Затем, когда вы хотите прочитать ранг для игры:

 select rank from leaderboards where game = '$game' and user_id = '$user_id'; 

получить идентификатор пользователя из таблицы пользователей и использовать его в своем запросе

 SELECT * FROM scores WHERE `GameName` = 'Snake' and `youruseridfield` = '$useridvalue' ORDER BY `Score` DESC 

SELECT * FROM scores WHERE 'GameName' = 'Snake' && userID = '$userID' ORDER BY 'Score' DESC

Интересно посмотреть, есть ли способ получить ранг в MySQL, но вот как вы могли бы это сделать в PHP:

 function getRank($user, $game, $limit=50) { $sql = " SELECT @rank:=@rank+1 AS Rank, User, GameName FROM scores, (SELECT @rank:=1) AS i WHERE `GameName` = '$game' ORDER BY `Score` DESC LIMIT 0, $limit "; $result = mysql_query($sql); while ($row = mysql_fetch_assoc($result)) { if ($row['User'] == $user) { return $row['Rank']; } } return -1; } 

Заметьте, я поставил лимит, потому что иначе вы не получите всего 30 результатов. И он возвращает -1, если игрок не зарегистрирован.

Вы не можете уйти от чтения большого количества данных в таблице, но вам не нужно перетаскивать все обратно на ваш сценарий обработки:

 SELECT COUNT(*) FROM scores WHERE `GameName` = 'Snake' AND user=$some_user; 

(поскольку вы, вероятно, хотите, чтобы первый человек имел ранг «1», а не «0», увеличивайте результат).

Однако, если вам нужно часто выполнять запрос, стоит сохранить материализованное представление отсортированных результатов.

Мое одно решение:

 select @rank:=@rank+1 AS Rank,L1.* from (select @rank:=0) as L2, (select i.*,count(*) as sum FROM transactions t LEFT JOIN companies c on (c.id = t.company_id) LEFT JOIN company_industries ci on (c.id = ci.company_id) LEFT JOIN industries i on (ci.industry_id = i.id) GROUP by i.name ORDER BY sum desc ) as L1; 

Вы должны добавить индексы в GameName и Score. (И не забывайте избегать GameName перед вставкой в ​​query => mysql_real_escape_string). @Bryan: не должно быть "AND", чем "&&"?