2010-03-28 13 views
3

J'ai un tableau dans lequel les utilisateurs stockent des scores et d'autres informations sur ce score (par exemple des notes sur le score, le temps pris, etc.). Je veux une requête MySQL qui trouve chaque utilisateur meilleur score personnel et il est associé des notes et du temps, etc.Trouver un score maximum des utilisateurs et les détails associés

Ce que j'ai essayé d'utiliser est quelque chose comme ceci:

SELECT *, MAX (score) FROM table GROUP BY (utilisateur)

Le problème avec ceci est que même si vous pouvez ajouter le meilleur résultat personnel de cette requête [MAX (score)], les notes et les temps retournés ne sont pas associés au score maximum, mais à un score différent (spécifiquement celui contenu dans *). Est-il possible que je puisse écrire une requête qui sélectionne ce que je veux? Ou devrais-je le faire manuellement en PHP?

+0

Si un utilisateur a deux scores égaux qui sont à la fois maximum, voulez-vous que les deux soient retournés ou juste l'un d'eux. Si ce dernier, lequel? –

Répondre

1

Vous pouvez vous joindre à une sous requête, comme dans l'exemple suivant:

SELECT t.*, 
     sub_t.max_score 
FROM  table t 
JOIN  (SELECT MAX(score) as max_score, 
        user 
      FROM  table 
      GROUP BY user) sub_t ON (sub_t.user = t.user AND 
            sub_t.max_score = t.score); 

La requête ci-dessus peut être expliqué comme suit. Il commence par:

SELECT t.* FROM table t; 

... Ceci en soi va évidemment lister tout le contenu de la table. L'objectif est de conserver uniquement les lignes qui représentent un score maximal d'un utilisateur particulier. Par conséquent, si nous avions les données ci-dessous:

+------------------------+ 
| user | score | notes | 
+------+-------+---------+ 
| 1 | 10 | note a | 
| 1 | 15 | note b |  
| 1 | 20 | note c | 
| 2 |  8 | note d | 
| 2 | 12 | note e | 
| 2 |  5 | note f | 
+------+-------+---------+ 

... Nous aurions voulu garder juste la « note c » et « e » note des lignes.

Pour trouver les lignes que nous voulons garder, nous pouvons simplement utiliser:

SELECT MAX(score), user FROM table GROUP BY user; 

Notez que nous ne pouvons pas obtenir les notes attribut de la requête ci-dessus, parce que vous aviez déjà remarqué, vous ne seriez pas obtenir les résultats attendus pour les champs non agrégés avec un aggregate function, comme MAX() ou ne faisant pas partie de la clause GROUP BY. Pour lire plus loin sur ce sujet, vous pouvez consulter:

Maintenant, nous ne devons garder les lignes de la première requête qui correspondent à la seconde question. Nous pouvons le faire avec un INNER JOIN:

... 
JOIN  (SELECT MAX(score) as max_score, 
        user 
      FROM  table 
      GROUP BY user) sub_t ON (sub_t.user = t.user AND 
            sub_t.max_score = t.score); 

La sous requête porte le nom sub_t. C'est l'ensemble de tous les utilisateurs avec le meilleur score personnel. La clause ON du JOIN applique la restriction aux champs pertinents. Rappelez-vous que nous voulons seulement conserver les lignes qui font partie de cette sous-requête.

+0

Merci beaucoup, cela fonctionne. :) Y at-il une chance que vous puissiez expliquer ce qui se passe avec cette entreprise de sous-requête, ou me diriger vers un tutoriel ou quelque chose? Je n'ai jamais vu ça auparavant. Sinon, merci et je chercherai à comprendre cela le matin! – VolatileStorm

+0

Heureux que ça a aidé. Permettez-moi de réviser la réponse avec quelques points ... –

+0

Cela fonctionne, mais notez qu'il peut renvoyer plusieurs résultats si le même joueur marque plus d'une fois le même score. –

1
SELECT * 
FROM table t 
ORDER BY t.score DESC 
GROUP BY t.user 
LIMIT 1 

Side note: Il est préférable de spécifier les champs que d'utiliser SELECT *

+0

Je ne pense pas que ce soit ce qu'il cherche. Il veut une rangée pour chaque utilisateur. –

1

Je suppose que vous ne voulez qu'un résultat par joueur, même s'ils ont marqué le même score maximum plus d'une fois.Je suppose également que vous voulez que la première fois de chaque joueur, ils obtiennent leur record personnel dans le cas où il y a des répétitions.

Il existe plusieurs façons de procéder. Voici une manière spécifique MySQL:

SELECT user, scoredate, score, notes FROM (
    SELECT *, @prev <> user AS is_best, @prev := user 
    FROM table1, (SELECT @prev := -1) AS vars 
    ORDER BY user, score DESC, scoredate 
) AS T1 
WHERE is_best 

Voici une manière plus générale qui utilise SQL ordinaire:

SELECT T3.* FROM table1 AS T3 
JOIN (
    SELECT T1.user, T1.score, MIN(scoredate) AS scoredate 
    FROM table1 AS T1 
    JOIN (SELECT user, MAX(score) AS score FROM table1 GROUP BY user) AS T2 
    ON T1.user = T2.user AND T1.score = T2.score 
    GROUP BY T1.user 
) AS T4 
ON T3.user = T4.user AND T3.score = T4.score AND T3.scoredate = T4.scoredate 

Résultat:

1, '2010-01-01 17:00:00', 50, 'Much better' 
2, '2010-01-01 14:00:00', 100, 'Perfect score' 

Les données d'essai j'ai utilisé pour tester ceci:

CREATE TABLE table1 (user INT NOT NULL, scoredate DATETIME NOT NULL, score INT NOT NULL, notes NVARCHAR(100) NOT NULL); 
INSERT INTO table1 (user, scoredate, score, notes) VALUES 
(1, '2010-01-01 12:00:00', 10, 'First attempt'), 
(1, '2010-01-01 17:00:00', 50, 'Much better'), 
(1, '2010-01-01 22:00:00', 30, 'Time for bed'), 
(2, '2010-01-01 14:00:00', 100, 'Perfect score'), 
(2, '2010-01-01 16:00:00', 100, 'This is too easy');