MySQL не имеет аналитические функции, как и другие базы данных (Oracle, SQL Server), которые были бы очень удобно для возвращения результата, как это.
Первые три столбца просты, всего GROUP BY user_id, category_id
и SUM(points)
.
Получение возвращаемого столбца rank
является немного более сложной задачей. Помимо этого на клиенте, если вам нужно сделать это в инструкции SQL, вы можете использовать переменные, определенные пользователем MySQL.
SELECT @rank := IF(@prev_category = r.category_id, @rank+1, 1) AS rank
, @prev_category := r.category_id AS category_id
, r.user_id
, r.total_points
FROM (SELECT @prev_category := NULL, @rank := 1) i
CROSS
JOIN (SELECT s.category_id, s.user_id, SUM(s.points) AS total_points
FROM users s
GROUP BY s.category_id, s.user_id
ORDER BY s.category_id, total_points DESC
) r
ORDER BY r.category_id, r.total_points DESC, r.user_id DESC
Целью зрения инлайн с псевдонимом i
, чтобы инициализировать пользовательские переменные. Встроенный просмотр с псевдонимом как r
возвращает total_points для каждого (user_id, category_id).
«Трюк» - это сравнить значение category_id предыдущей строки со значением текущей строки; если они совпадают, мы увеличиваем ранг на 1. Если это «новая» категория, мы возвращаем ранг на 1. Обратите внимание, что это работает только в том случае, если строки упорядочены по категориям, а затем по итоговым точкам, по убыванию, поэтому нам нужен ORDER BY пункт. Также обратите внимание, что порядок выражений в списке SELECT важен; нам нужно выполнить сравнение предыдущего значения до того, как оно будет перезаписано текущим значением, поэтому присвоение @prev_category должно соответствовать условному тесту.
Также обратите внимание, что если у двух пользователей одинаковые тотальные точки в категории, они получат разные значения для ранга ... указанный выше запрос не дает того же ранга для галстука. (Запрос можно также изменить, чтобы сделать это, но нам также необходимо сохранить total_points из предыдущей строки, поэтому мы можем сравнить с текущей строкой.
Также обратите внимание, что этот синтаксис специфичен для MySQL и что это поведение не гарантировано.
Если вам нужны столбцы в определенной последовательности и/или строки в определенном порядке (чтобы получить точный набор результатов указанных), мы должны обернуть запрос, приведенный выше, как встроенный вид.
SELECT t.user_id
, t.category_id
, t.total_points
, t.rank
FROM (
SELECT @rank := IF(@prev_category = r.category_id, @rank+1, 1) AS rank
, @prev_category := r.category_id AS category_id
, r.user_id
, r.total_points
FROM (SELECT @prev_categor := NULL, @rank := 1) i
CROSS
JOIN (SELECT s.category_id, s.user_id, SUM(s.points) AS total_points
FROM users s
GROUP BY s.category_id, s.user_id
ORDER BY s.category_id, total_points DESC
) r
ORDER BY r.category_id, r.total_points DESC, r.user_id DESC
) t
ORDER BY t.user_id, t.category_id
ПРИМЕЧАНИЕ: У меня есть нет настроить демонстрацию скрипта SQL. Я привел пример запроса, который проверен только на столе.
Используйте SELECT user_id, category_id, sum (points) как total_points FROM table GROUP BY user_id, category_id для первых 3 столбцов ... –
Посмотрите эту скрипту: http://www.sqlfiddle.com/#!2/317da3/2 –