¿Cómo se ha podido recuperar 3 primeros lugares de cada juego de la tabla de puntuación en MySQL?
-
21-08-2019 - |
Pregunta
Tengo la siguiente tabla:
CREATE TABLE `score` (
`score_id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) unsigned NOT NULL,
`game_id` int(10) unsigned NOT NULL,
`thescore` bigint(20) unsigned NOT NULL,
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`score_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Esa es una tabla de puntuación las tiendas de la user_id y game_id y la puntuación de cada juego. hay trofeos para los 3 primeros lugares de cada juego. Tengo un user_id y me gustaría comprobar si ese usuario específico consiguió ningún trofeo de cualquiera de los juegos.
¿Hay algún modo de crear esta consulta sin crear una tabla temporal?
Solución
SELECT s1.*
FROM score s1 LEFT OUTER JOIN score s2
ON (s1.game_id = s2.game_id AND s1.thescore < s2.thescore)
GROUP BY s1.score_id
HAVING COUNT(*) < 3;
Esta consulta devuelve las filas de todos los juegos ganadores. A pesar de que los lazos se incluyen; si los resultados son 10,16,16,16,18 entonces hay cuatro ganadores: 16,16,16,18. No estoy seguro de cómo manejar eso. Necesita alguna manera de resolver los lazos en la condición de unión.
Por ejemplo, si los lazos se resuelven por el ganador del juego anterior, entonces se podría modificar la consulta de esta manera:
SELECT s1.*
FROM score s1 LEFT OUTER JOIN score s2
ON (s1.game_id = s2.game_id AND (s1.thescore < s2.thescore
OR s1.thescore = s2.thescore AND s1.score_id < s2.score_id))
GROUP BY s1.score_id
HAVING COUNT(*) < 3;
Usted podría también utilizar el timestamp
columna para resolver los lazos, si puede confiar en que sea UNIQUE
.
Sin embargo, MySQL tiende a crear una tabla temporal para este tipo de consulta de todos modos. Aquí está la salida de EXPLAIN
para esta consulta:
+----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+
| 1 | SIMPLE | s1 | ALL | NULL | NULL | NULL | NULL | 9 | Using temporary; Using filesort |
| 1 | SIMPLE | s2 | ALL | PRIMARY | NULL | NULL | NULL | 9 | |
+----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+
Otros consejos
SELECT game_id, user_id
FROM score score1
WHERE (SELECT COUNT(*) FROM score score2
WHERE score1.game_id = score2.game_id AND score2.thescore > score1.thescore) < 3
ORDER BY game_id ASC, thescore DESC;
Una forma más clara a hacerlo, y semitested.
SELECT DISTINCT user_id FROM ( select s.user_id, s.game_id, s.thescore, (SELECT count(1) from scores where game_id = s.game_id AND thescore > s.thescore ) AS acount FROM scores s ) AS a
donde acount <3
Dejase't probarlo, pero debería funcionar bien:
SELECT
*,
@position := @position + 1 AS position
FROM
score
JOIN (SELECT @position := 0) p
WHERE
user_id = <INSERT_USER_ID>
AND game_id = <INSERT_GAME_ID>
ORDER BY
the_score
No se puede comprobar el campo Posición para ver si Está entre 1 y 3.