¿Cómo generar estos dos informes con MySQL?
-
09-10-2019 - |
Pregunta
Mi esquema
Tengo las siguientes tablas
table notes/example values
------------------------------------------------
users (
id
email # "foo@example.com"
)
games (
id
name # "Space Invaders", "Asteroids", "Centipede"
)
players (
id
name # "uber dude"
user_id # player belongs to user
game_id # player belongs to game
)
scores (
id
player_id # belongs to one player
value # 50
created_at # "2010-09-10", "2010-08-05"
month # "2010-09", "2010-08"
)
Necesito crear dos informes.
1) Los mejores jugadores
Los mejores jugadores que realizan (suma todas las puntuaciones de cada jugador) para el más reciente 4 meses. Mostrar los 10 de cada mes.
2010-07 2010-08 2010-09 2010-10
1 plyA 5,000 pts plyB 9,400 pts ... ...
Centipede Solitaire
2 plyB 3,600 pts plyC 8,200 pts ... ...
Asteroids Centipede
3 plyC 2,900 pts plyA 7,000 pts ... ...
Centipede Centipede
4 ... ... ... ...
5 ... ... ... ...
6 ... ... ... ...
7 ... ... ... ...
8 ... ... ... ...
9 ... ... ... ...
10 ... ... ... ...
2) Top usuarios:
Los mejores usuarios que realizan (suma todas las puntuaciones de cada jugador para cada usuario) para el la mayoría de los últimos 4 meses. Mostrar los 10 de cada mes.
2010-07 2010-08 2010-09 2010-10
1 userA 50,000 pts userB 51,400 pts ... ...
2 userB 40,500 pts userA 39,300 pts ... ...
3 userC 40,200 pts userC 37,000 pts ... ...
4 ... ... ... ...
5 ... ... ... ...
6 ... ... ... ...
7 ... ... ... ...
8 ... ... ... ...
9 ... ... ... ...
10 ... ... ... ...
MySQL ayudante de vista ??h2>
Para la unión de propósitos, tengo una vista almacenada a consulta la ayuda de los meses para los informes. Siempre va a devolver los últimos 4 meses la mayoría.
report_months (
month
)
SELECT * FROM report_months;
2010-07
2010-08
2010-09
2010-10
El problema
En el informe # 1, por ejemplo, puedo conseguir las sumas bastante facilidad.
select
p.name as player_name,
g.name as game_name,
s.month as month,
sum(s.score) as sum_score
from players as p
join games as g
on g.id = p.game_id
join scores as s
on s.player_id = p.id
join report_months as rm -- handy view helper
on rm.month = s.month
group by
p.name, g.name
order by
sum(s.score) desc
-- I can't do this :(
-- limit 0, 40
Sin embargo, no puedo simplemente buscar a los 40 mejores resultados y difundirlas a través de 4 meses ya que esto no me garantizaría 10 por cada mes.
La pregunta ??h2>
¿Cómo puedo modificar mi consulta para asegurarse de que me gustaría obtener 10 por cada mes?
Solución
Yo no trataría de hacer una consulta SQL que tabula por mes, ya que han demostrado.
En su lugar, consulta los mejores 10 jugadores al mes como filas, no como columnas:
Month Rank Player TotalScore Game
2010-07 1 plyA 5,000 pts Centipede
2010-07 2 plyB 3,600 pts Asteroids
2010-07 3 plyC 2,900 pts Centipede
...
2010-08 1 plyB 9,400 pts Solitaire
2010-08 2 plyC 8,200 pts Centipede
2010-08 3 plyA 7,000 pts Centipede
...
Esto se convierte en un problema greatest-n-per-group
, donde n
es 10.
CREATE VIEW PlayerScoresByMonth AS
SELECT month, player_id, SUM(value) AS score
FROM scores
GROUP BY month, player_id;
SELECT s1.month, COUNT(s2.month)+1 AS Rank, s1.player_id, s1.score AS TotalScore
FROM PlayerScoresByMonth s1
LEFT OUTER JOIN PlayerScoresByMonth s2 ON s1.month = s2.month
AND (s1.score < s2.score OR s1.score = s2.score AND s1.player_id < s2.player_id)
GROUP BY s1.month, s1.player_id
HAVING COUNT(*) < 10
ORDER BY s1.month, Rank;
(eso no está probado, pero debería empezar)
Luego hay que escribir algo de código de la aplicación para obtener los resultados de esta consulta y separar las listas por mes, y presentar los datos sin embargo que iba a hacer eso.