Нахождение максимального балла пользователей и связанных с ним деталей - PullRequest
3 голосов
/ 29 марта 2010

У меня есть таблица, в которой пользователи хранят баллы и другую информацию о них (например, заметки о баллах, или время, потраченное и т. Д.). Я хочу запрос mysql, который находит личный рекорд каждого пользователя, связанные с ним заметки, время и т. Д.

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

SELECT *, MAX (оценка) ИЗ таблицы GROUP BY (пользователь)

Проблема с этим заключается в том, что, хотя вы можете получить больше личных результатов пользователей из этого запроса [MAX (оценка)], возвращенные заметки, время и т. Д. Связаны не с максимальной оценкой, а с другой (особенно содержащей в *). Есть ли способ, которым я могу написать запрос, который выбирает то, что я хочу? Или мне придется делать это вручную в PhP?

Ответы [ 3 ]

2 голосов
/ 29 марта 2010

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

Есть несколько способов сделать это. Вот способ, специфичный для 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

Вот более общий способ использования обычного SQL:

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

Результат:

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

Тестовые данные, которые я использовал, чтобы проверить это:

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');
1 голос
/ 29 марта 2010

Вы можете присоединиться с подзапросом, как в следующем примере:

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

Вышеуказанный запрос можно объяснить следующим образом. Начинается с:

SELECT t.* FROM table t;

... Это само по себе, очевидно, перечислит все содержимое таблицы. Цель состоит в том, чтобы сохранить только те строки, которые представляют максимальную оценку конкретного пользователя. Поэтому, если бы у нас были данные ниже:

+------------------------+
| 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 |
+------+-------+---------+

... Мы бы хотели сохранить только строки "note c" и "note e".

Чтобы найти строки, которые мы хотим сохранить, мы можем просто использовать:

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

Обратите внимание, что мы не можем получить атрибут notes из приведенного выше запроса, поскольку, как вы уже заметили, вы не получите ожидаемых результатов для полей, не агрегированных с помощью агрегатной функции , например MAX() не является частью пункта GROUP BY. Для дальнейшего чтения по этой теме вы можете проверить:

Теперь нам нужно только сохранить строки из первого запроса, которые соответствуют второму запросу. Мы можем сделать это с 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);

Подзапросу присваивается имя sub_t. Это набор всех пользователей с личным лучшим счетом. Предложение ON в JOIN применяет ограничение к соответствующим полям. Помните, что мы хотим сохранить только те строки, которые являются частью этого подзапроса.

1 голос
/ 29 марта 2010
SELECT *
FROM table t
ORDER BY t.score DESC
GROUP BY t.user
LIMIT 1

Примечание: лучше указать поля, чем использовать SELECT *

...