2011-01-30 5 views
1

J'ai une table dans MySQL, disons juste qu'elle a deux champs Username, GameName et Score. Je veux calculer le rang d'un utilisateur pour un nom de jeu indivudal pour que je puisse faire la requêteCalcul de rang en PHP/MySQL

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

pour obtenir une liste de tous les utilisateurs afin de haut au plus bas et attribuer un numéro à chaque utilisateur.

Mais existe-t-il un moyen plus simple d'obtenir le classement d'un utilisateur individuel plutôt que de sélectionner la totalité de la table car cela ne semble pas trop efficace.

Merci

+0

Ma pensée est que si vous pouvez le faire dans MySQL, il serait à l'aide d'une table temporaire, subselect, et/ou calculer le numéro de ligne avec un alias Rank.L'astuce serait de faire en sorte que MySQL remplisse une table/sous-requête temporaire, puis de ne sélectionner qu'un seul résultat (le plus haut rang du joueur). Ce serait ce que je ne sais pas comment faire, sans essais et erreurs et tests. Voir: http://jimlife.wordpress.com/2008/09/09/displaying-row-number-rownum-in-mysql/ –

+0

Je vous ai bien compris que vous recherchez le classement le plus élevé d'un utilisateur joueurs, et pas le meilleur score de ce joueur, non? –

Répondre

4

Si vous voulez des classements généraux, vous devez malheureusement trier toute la table. Autrement dit, vous ne pouvez pas connaître le rang de quelqu'un dans la table sans connaître les autres rangs du tableau. Cela dit, si vous êtes inquiet au sujet des performances, il y a une solution assez simple ici - cachez le résultat de votre requête de classement (peut-être dans une autre table MySQL!), Et interrogez-la pour toutes vos lectures. Lorsque quelqu'un publie une nouvelle note, recalculez votre table temporaire. Vous pouvez périodiquement vider tous les records sous un certain rang (par exemple, toute personne de moins de 100 ans est retirée du tableau des scores) pour accélérer les recalculs, car personne ne monterait dans le classement après avoir été renversé par un score plus élevé.

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

Ensuite, chaque fois que vous voulez lire un rang pour un jeu:

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

Je savais que c'était possible, je ne savais pas exactement comment. Serait-ce vraiment plus rapide que de simplement limiter la requête à ce qui était nécessaire (top 100) et laisser PHP itérer? Je n'ai jamais eu besoin de tables temporaires, donc je ne suis pas sûr. En outre, cela pourrait être une procédure stockée, non? –

+0

Il serait probablement extrêmement rapide si vous gardez la partie qui vide les vieux scores de la table des scores. Une solution PHP sera probablement assez rapide tant que vous avez des index sur 'game' et' score', mais ce sera certainement plus lent, puisque vous recalculerez rang et lisant 100 lignes de chaque table, plutôt que 1 rangée sur 1 table pour chaque lecture. Cela pourrait facilement être une procédure stockée, et vous pourriez le réécrire pour passer l'étape de sélectionner dans la table de temp si vous vouliez aller cette route. –

+0

De plus, vous avez des leadersboars au lieu des classements dans la dernière requête. –

1

obtenir l'ID utilisateur de votre table d'utilisateurs et l'utiliser dans votre requête


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

Assurez-vous que $ useridvalue' est échappé. – zneak

+0

oui c'est très nécessaire –

+0

Vous ne pouvez pas utiliser ORDER avant l'instruction WHERE – Nazariy

1

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

+1

Cela n'obtiendra pas un classement, car il n'ordonnera les résultats qu'après avoir été réduit par la clause 'where'. Il veut des classements généraux - un classement. –

0

Vous devez ajouter des index à GameName et Score. (Et n'oubliez pas d'échapper le GameName avant de l'insérer dans la requête => mysql_real_escape_string). @Brian: ne devrait-il pas être "AND" que "& &"?

+0

Je ne vois pas de problème avec &&. Je l'utilise dans mon php. http://stackoverflow.com/questions/2803321/and-vs-as-operator – Bryan

+0

Il n'y a pas de problème avec && dans MySQL, ils sont tous deux valides et agissent comme des alias l'un pour l'autre. Beaucoup de gens utilisent conventionnellement ET dans MySQL, mais ce n'est pas écrit dans la pierre. http://dev.mysql.com/doc/refman/5.0/en/logical-operators.html#operator_and –

+0

En outre, cela ne répond pas à la question, c'est juste pour accélérer la recherche elle-même. Il appartient plus à un commentaire à la question originale. –

1

intéressant de voir s'il y avait un moyen d'obtenir le rang de MySQL, mais voici comment vous pourriez faire il en PHP:

function getRank($user, $game, $limit=50) { 
    $sql = " 
SELECT @rank:[email protected]+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; 
} 

Remarque, je mets la limite là parce que sinon vous n'obtiendrez que 30 résultats en arrière. Et il renvoie -1 si le joueur n'est pas classé.

1

Vous ne pouvez pas échapper à la lecture d'un grand nombre de données dans la table - mais vous n'avez pas besoin de le transporter tout le chemin du retour à votre script de traitement:

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

(depuis que vous avez probablement voulez que la première personne ait un rang de '1' plutôt que '0', incrémentez le résultat). Cependant, si vous devez exécuter la requête souvent, cela vaut la peine de conserver une vue matérialisée des résultats triés.

1

Ma seule solution de requête:

select @rank:[email protected]+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;