Как найти лучший результат для каждого события в MySQL?

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

Вот таблица, содержащая данные для всех тестов, проведенных спортсменами:

+---------+-----------+-------+
| eventId | athleteId | score |
+---------+-----------+-------+
| 1       | 129907    | 900   |
| 2       | 129907    | 940   |
| 3       | 129907    | 927   |
| 4       | 129907    | 856   |
| 1       | 328992    | 780   |
| 2       | 328992    | 890   |
| 3       | 328992    | 936   |
| 4       | 328992    | 864   |
| 1       | 492561    | 899   |
| 2       | 492561    | 960   |
| 3       | 492561    | 840   |
| 4       | 492561    | 920   |
| 5       | 487422    | 900   |
| 6       | 487422    | 940   |
| 7       | 487422    | 927   |
| 5       | 629876    | 780   |
| 6       | 629876    | 890   |
| 7       | 629876    | 940   |
| 5       | 138688    | 899   |
| 6       | 138688    | 950   |
| 7       | 138688    | 840   |
+---------+-----------+-------+

Мне нужно выбрать лучший стандартный состав, проведя лучшие тесты. Результат, который я ищу, должен быть:

+---------+-----------+-------+
| eventId | athleteId | score |
+---------+-----------+-------+
| 1       | 129907    | 900   |
| 2       | 492561    | 960   |
| 3       | 328992    | 936   |
| 4       | 492561    | 920   |
| 5       | 487422    | 900   |
| 6       | 138688    | 950   |
| 7       | 629876    | 940   |
+---------+-----------+-------+
1
nl ja de

2 ответы

Если вы хотите надежно получить победителя (и совместных победителей). Следующий оператор SQL должен сделать это ...

SELECT athleteId, a.eventId, a.score
FROM tests AS a
JOIN (
  -- This select finds the top score for each event
  SELECT eventId, MAX(score) AS score
  FROM tests 
  GROUP BY eventId
) AS b
-- Join on the top scores
ON a.eventId = b.eventId
AND a.score = b.score

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


Дополнительная информация

Я собрал следующую информацию из разговоров в комментариях.

Почему основная группа по решению не надежна?

SELECT athleteId, eventId, score
FROM (
  SELECT athleteId, eventId, score
  FROM tests
  ORDER BY eventId, score DESC
) AS a
GROUP BY eventId

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

Первое, что нужно отметить

If you are using a GROUP BY clause you are no longer talking about individual records but an unordered set of records!

Вы можете использовать агрегированные функции для выполнения некоторых довольно мощных и полезных кросс-записей в MySQL http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html , но для того, чтобы связать группы с отдельными записями, вам, вероятно, потребуется выполните JOIN .

Во втором примере мы возвращаем группы, как если бы они были отдельными записями.

Почему второй пример работает?

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

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

В документации MySQL указано, что значения для неагрегированных столбцов в элементе, содержащем GROUP BY , являются неопределенными. Это означает, что результирующие значения для неагрегированных столбцов не должны считаться результатом событий до группировки (т. Е. Любого упорядочения в наборе записей), хотя практически в этой текущей реализации это выглядит так.

In future version it may not be the case, it may not even be the case that the result may not even be the same if you run it twice. The fact it is documented explicitly is reason enough for me to avoid it!

Почему неагрегированные столбцы являются неопределенными?

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

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

Риск

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

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

Почему решение для соединения отличается?

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

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


Выбор одной записи в качестве победителя

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

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

SELECT a.eventId, athleteId, a.score
FROM tests AS a
JOIN (
  -- This select finds the first testId for each score + event combination
  SELECT MIN(testId) AS testId, c.eventId, c.score
  FROM tests AS c
  JOIN (
    -- This select finds the top score for each event
    SELECT eventId, MAX(score) AS score
    FROM tests
    GROUP BY eventId
  ) AS d
  ON c.eventId = d.eventId
  AND c.score = d.score
  GROUP BY eventId, score
) AS b
ON a.testId = b.testId

Что происходит здесь, так это то, что мы создаем группы, представляющие наивысший балл для каждого события, которое мы затем объединяем, с группами, которые представляют самый низкий testId для каждой комбинации баллов и событий и, наконец, внутреннее соединение, которое с записями в тестовой таблице позволяет получить отдельные записи ,

Это также можно записать (с несколько иным планом выполнения) следующим образом.

SELECT a.eventId, athleteId, a.score
FROM tests AS a
JOIN (
  -- This select finds the top score for each event
  SELECT eventId, MAX(score) AS score
  FROM tests
  GROUP BY eventId
) AS b
ON a.eventId = b.eventId
AND a.score = b.score
JOIN (
  -- This select finds the first testId for each score + event combination
  SELECT MIN(testId) AS testId, eventId, score
  FROM tests
  GROUP BY eventId, score
) AS c
ON a.testId = c.testId

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

5
добавлено
Несоответствие с «выходом должно быть» из вопроса в: 6 138688 950 вместо 6 492561 950 .
добавлено автор hakre, источник
И как результат определяется, если и eventId, и оценка - MAX (оценка) group by eventId с несколькими совпадениями? Например. несколько спортсменов достигли того же самого высокого балла за одно и то же событие?
добавлено автор hakre, источник
Таким образом, это не ограничение для первого. Я попытался присвоить числа (вроде виртуального столбца). Это сработало, однако я не могу сказать, является ли оно детерминированным. Может быть, основная проблема здесь заключается в том, что первичный ключ в таблице не существует?
добавлено автор hakre, источник
О, крики, спасибо, что указали, что пропустили псевдоним столбца, я обновил ответ
добавлено автор Stuart Wakefield, источник
Да, есть несоответствие с вводом и ожидаемым выходом в OP, SQL не лежит ...
добавлено автор Stuart Wakefield, источник
Когда вы используете GROUP BY в SELECT вместо выбора записей, вы выбираете группы, MAX (оценка) - это агрегация, что означает, что это не относится к одной записи, но вместо этого это максимальный балл для группы в целом (это определенно). MySQL не может получить определенную информацию для отдельной записи из группы, поэтому для ее привязки к отдельным записям я использую JOIN . Ответ выше вернет всех и всех спортсменов, которые достигли того же самого результата, что и самый высокий балл для мероприятия, а не произвольно выбирают его.
добавлено автор Stuart Wakefield, источник
Я не уверен, что следую вашему решению виртуальной колонки. Отправьте его как ответ. Он должен быть детерминированным, если он не попадает в ту же ловушку.
добавлено автор Stuart Wakefield, источник
Для первой «ошибки», если три спортсмена получают верхний балл для турнира, как вы решаете, какой спортсмен должен выбрать первый для верхней оценки, будет ли он первым получить этот результат? Если это так, вам, вероятно, понадобится testId, чтобы сначала записать записи в последовательности. Боюсь, я не следую второй части об ограничении спортсмена одним событием. Наверное, лучше, если вы обновите или опубликуете следующий вопрос.
добавлено автор Stuart Wakefield, источник
«Неизвестная колонка« b.score »в разделе« on »)
добавлено автор djpredator17, источник
Это хорошо, спасибо!
добавлено автор djpredator17, источник
Я заметил «ошибку». Если наилучший результат существует более одного раза в базе данных (т. Е. Если верхняя оценка «Pole Vault» равна 710 в три раза), я получаю 3 результата. Как я могу получить только один?
добавлено автор djpredator17, источник
И, также ... Можем ли мы ограничить спортсмена, чтобы он мог появиться только в одном случае? Если он лучший для двух разных событий, он появится только в своем лучшем, а затем второй спортсмен появится в другой гонке.
добавлено автор djpredator17, источник

Попробуй это:

SELECT t1.eventId, t1.athleteId, t1.score  
FROM tests t1 
LEFT JOIN tests t2 ON t2.eventId = t1.eventId AND t2.score > t1.score 
WHERE t2.athleteId IS NULL
ORDER BY t1.eventId 

http://sqlfiddle.com/#!2/80e34/3/0

0
добавлено
Это работает для меня sqlfiddle.com/#!2/80e34/3/0
добавлено автор piotrekkr, источник
t1 и t2? Это приводит к ошибке ...
добавлено автор djpredator17, источник
DBA - русскоговорящее сообщество
DBA - русскоговорящее сообщество
1 345 участник(ов)

Общаемся и обсуждаем темы, посвященные DBA, PostgreSQL, Redis, MongoDB, MySQL, neo4j, riak и т.д. См. также: @devops_ru, @kubernetes_ru, @docker_ru, @nodejs_ru Рекомендуем сразу отключить уведомления, чтобы пребывание здесь было полезным и комфортным.

MySQL
MySQL
995 участник(ов)

The group is about MySQL. For code use hastebin.com. Admin: @smlkw