Foros del Web » Programación para mayores de 30 ;) » Bases de Datos General » Mysql »

Ranking de lista de usuarios

Estas en el tema de Ranking de lista de usuarios en el foro de Mysql en Foros del Web. Estimados, Estoy necesitando realizar una consulta en MySQL que me traiga una lista de 6 usuarios y que siempre un usuario determinado este en la ...
  #1 (permalink)  
Antiguo 18/09/2013, 15:52
 
Fecha de Ingreso: abril-2011
Ubicación: Mendoza
Mensajes: 42
Antigüedad: 13 años
Puntos: 0
Exclamación Ranking de lista de usuarios

Estimados,

Estoy necesitando realizar una consulta en MySQL que me traiga una lista de 6 usuarios y que siempre un usuario determinado este en la posicion 4, y mostrar 3 arribas y 2 abajo. Ordenado por el puntaje que tiene cada puntos...

Entonces, lo que necesito es buscar un usuario por su ID ordenado por puntos y que en vez de mostrarme un solo resultado, me muestre 3 usuarios arriba y 2 abajo.

Me explico mejor

Usuario 1 - Puntos 100
Usuario 2 - Puntos 90
Usuario 3 - Puntos 80
Usuario 4 - Puntos 70 (Yo)
Usuario 5 - PUntos 60
Usuario 6 - Puntos 50

Tienen idea de una aproximado a esto???
__________________
Claudio A. Marrero - Proyect Manager
  #2 (permalink)  
Antiguo 18/09/2013, 16:45
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 16 años, 5 meses
Puntos: 2658
Respuesta: Ranking de lista de usuarios

Nosotros tenemos idea. Pero empieza por mostrar como lo harías tú.
Este no es un sito para regalar soluciones, sino para ayudar a lograrlas.
Muestra lo que intentaste.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #3 (permalink)  
Antiguo 18/09/2013, 16:56
 
Fecha de Ingreso: abril-2011
Ubicación: Mendoza
Mensajes: 42
Antigüedad: 13 años
Puntos: 0
Respuesta: Ranking de lista de usuarios

Gracias Gnzsoloyo por responder...

Empece por pensarlo a traves de un Store Procedure, ya que una sola consulta no me iba a ser suficiente..

Intente con esto:

Código MySQL:
Ver original
  1.  
  2.     SET @query = CONCAT("SELECT b.userId as user, b.nicename,
  3.     (select points from historypoints h where h.userId = user ORDER BY id desc limit 1) as orderpoints
  4.     from users as b where b.confirm_email_key = ''
  5.     order by orderpoints desc limit ",setLimit,",15;");
  6.  
  7.     PREPARE stmt FROM @query;
  8.     EXECUTE stmt;
  9.     DEALLOCATE PREPARE stmt;
  10.  

Ahora, esta consulta me devuelve los 15 resultados que tienen mejor puntaje, pero lo que yo necesito, y no encuentro como resolverlo es que me muestre como planteo arriba, a quienes tengo arriba y a quienes abajo.. y yo en en el medio... y si no tengo arriba, tendria que estar primero, y los demas abajo, y viceversa...

La otra manera era crear un between con los puntos, pero como mi limite sera de 6 usuarios, me preocupa que tenga muchos usuarios..

ejemplo:
Código MySQL:
Ver original
  1. SELECT b.userId as user, b.nicename
  2.     from bp_users as b where b.confirm_email_key = ''   and
  3.   b.userId IN(select userId from bp_historypoints where points >= 4000)
  4.     order by userId desc limit 0,10;
Saludos
__________________
Claudio A. Marrero - Proyect Manager

Última edición por gnzsoloyo; 18/09/2013 a las 19:32
  #4 (permalink)  
Antiguo 18/09/2013, 19:47
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 16 años, 5 meses
Puntos: 2658
Respuesta: Ranking de lista de usuarios

Este tema es parecido a uno tratado hace tiempo, y la solucion no pasa por ordenar, o armar búsquedas como lo planteas.
Si lo miras bien, lo que estás pidiendo una tabla como resultado de una consulta tal que:
1) Siempre tenga un valor dado en la cuarta posición.
2) Siempre tenga tres registros precedentes mayores al cuarto.
3) Siempre tenga dos registros posteriores, y menores al cuarto.

En principio, el planteo carece de sentido desde el momento en que en un sistema estadístico o de sumatorias, no puedes predecir que siempre habrá un conjunto de X registros antes o después, porque podría suceder que la suma de puntajes sea en uno, más de uno o todos, inferior o superior al valor dado de referencia (el cuarto).
Por ende, no puedes, a priori, predefinir que siempre habrá algo que lo preceda, o lo suceda.

Pero dejando de lado que no es racional el planteo, una consulta como la que quieres únicamente se puede construir en forma simple segmentando los rangos en diferentes consultas, y generando una respuesta única, con UNION:
Código MySQL:
Ver original
  1.       (SELECT U.userId Usuario, U.nicename , H.points
  2.       FROM users u INNER JOIN historypoints h ON U.user = H.userId = user
  3.       WHERE H.points > (SELECT points FROM historypoints WHERE userId = 4)
  4.       ORDER BY H.poits DESC
  5.       LIMIT 3) T1
  6. SELECT U.userId Usuario, U.nicename , H.points
  7. FROM users u INNER JOIN historypoints h ON U.user = H.userId = user
  8. WHERE H.userId = 4
  9.       (SELECT U.userId Usuario, U.nicename , H.points
  10.       FROM users u INNER JOIN historypoints h ON U.user = H.userId = user
  11.       WHERE H.points < (SELECT points FROM historypoints WHERE userId = 4)
  12.       ORDER BY H.userId
  13.       LIMIT 2) T2;
Si bien esto debería funcionar, no garantiza que obtengas realmente los tres registros superiores o los dos inferiores pedidos. Sólo que el valor de este usuario ID = 4 (que uso de ejemplo) aparezca siempre, como mucho, en la posicion 4 si la consulta devuelve hasta 6 registros.
El resto, no se puede asegurar, porque dependerá de los datos existentes.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #5 (permalink)  
Antiguo 19/09/2013, 05:03
 
Fecha de Ingreso: abril-2011
Ubicación: Mendoza
Mensajes: 42
Antigüedad: 13 años
Puntos: 0
Respuesta: Ranking de lista de usuarios

Gracias por la respuesta,

Te aclaro el panorama para entiendas un poco mas para ver si logramos crear una query que nos funcione..

Como vos decis, no siempre vas a tener registros arriba y abajo, de hecho, eso lo tendria que contemplar, en caso de no tener arriba, el motivo seria por que estoy primero en el ranking y deberia aparecer primero, y viceversa si estoy en el ultimo..

Tome la query que me pasaste y a primeras no me funciono, por lo que le hice algunas modificaciones, nada importante, pero no funciono...

Código SQL:
Ver original
  1. SELECT *
  2. FROM
  3.       (SELECT U.userId, U.nicename , H.points
  4.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  5.       WHERE H.points > (SELECT points FROM bp_historypoints WHERE userId = 21)
  6.       ORDER BY H.points DESC
  7.       LIMIT 3) T1
  8. UNION
  9. SELECT U.userId, U.nicename , H.points
  10. FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  11. WHERE H.userId = 21
  12. UNION
  13. SELECT *
  14. FROM
  15.       (SELECT U.userId, U.nicename , H.points
  16.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  17.       WHERE H.points < (SELECT points FROM bp_historypoints WHERE userId = 21)
  18.       ORDER BY H.userId
  19.       LIMIT 2) T2;
El tema que no se si mencione, es que la tabla historypoints mantiene una historia de puntos, por lo que cada usuario tiene varios registros en esta tabla y el ultimo registro de cada usuario es el que corresponde como puntos actuales.

Por lo que esta consulta me tira un error diciendome que "La Subquery tieme mas de 1 resultado)..

Voy a seguir intentando para ver q puedo lograr, pero mas que hacer prueba y error, me gustaria poder comprender exactamente la idea de lo que me propusiste que no la entendi del todo

Muchas Gracias

Best
C
__________________
Claudio A. Marrero - Proyect Manager
  #6 (permalink)  
Antiguo 19/09/2013, 06:14
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 16 años, 5 meses
Puntos: 2658
Respuesta: Ranking de lista de usuarios

Cita:
El tema que no se si mencione, es que la tabla historypoints mantiene una historia de puntos, por lo que cada usuario tiene varios registros en esta tabla y el ultimo registro de cada usuario es el que corresponde como puntos actuales.
Bueno, eso es crítico y debí haberlo supuesto, a causa del nombre de la tabla.
De todos modos la cosa se vuelve simple, pero ahora deberás definir cuál es el valor que deseas obtener: el máximo puntaje, el mínimo, o la media. Porque incluso si restringes la búsqueda a un período, deberás asegurarte que en ese periodo hay aun único registro por cada user, o bien aplicar las funciones que corresponda.
Supongamos buscar el máximo puntaje:

Código MySQL:
Ver original
  1.       (SELECT U.userId, U.nicename, MAX(H.points) points
  2.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  3.       WHERE H.points > (SELECT points FROM bp_historypoints WHERE userId = 21)
  4.       GROUP BY U.userId
  5.       ORDER BY H.points DESC
  6.       LIMIT 3) T1
  7.       (SELECT U.userId, U.nicename , MAX(H.points) points
  8.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  9.       WHERE H.userId = 21
  10.       GROUP BY U.userId
  11.       LIMIT 1) T2
  12.       (SELECT U.userId, U.nicename, MAX(H.points) points
  13.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  14.       WHERE H.points < (SELECT points FROM bp_historypoints WHERE userId = 21)
  15.       GROUP BY U.userId
  16.       ORDER BY H.userId
  17.       LIMIT 2) T3;
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #7 (permalink)  
Antiguo 19/09/2013, 06:56
 
Fecha de Ingreso: abril-2011
Ubicación: Mendoza
Mensajes: 42
Antigüedad: 13 años
Puntos: 0
Respuesta: Ranking de lista de usuarios

Bien, gracias.. Probe la query que me pasaste, y sigo con el mismo problema "Subquery return more than 1 result"...

Despues de mirar muchos tutoriales, manuales y de mas cuestiones llegue a la conclusion de modificar mis tablas..

Lo que hice fue agregar una sola tabla que mantendra un solo registro por usuario...

Esto me soluciona bastante las cosas, pero aun sigo sin lograr el resultado esperado en una varidad de pruebas, el codigo siguiente funciona a medias:

Código SQL:
Ver original
  1. BEGIN
  2.     DECLARE countRankingTop INTEGER DEFAULT 0;
  3.     DECLARE countRankingBottom INTEGER DEFAULT 0;
  4.     DECLARE limitTop INTEGER DEFAULT 3;
  5.     DECLARE limitBottom INTEGER DEFAULT 2;
  6.  
  7.     SELECT COUNT(userId) INTO countRankingTop FROM bp_user_points b
  8. WHERE points >= (SELECT points FROM bp_user_points b WHERE userId = `userId`)
  9. AND userId != `userId`;
  10.  
  11.     SELECT COUNT(userId) INTO countRankingBottom FROM bp_user_points b
  12. WHERE points <= (SELECT points FROM bp_user_points b WHERE userId = `userId`)
  13. AND userId != `userId`;
  14.  
  15. IF countRankingTop < 3 THEN
  16.     SET limitBottom = 3 - countRankingTop + 3;
  17. ELSE
  18.     SET limitBottom = 2;
  19. END IF;
  20.  
  21. IF countRankingBottom < 2 THEN
  22.     SET limitTop = 2 - countRankingBottom + 2;
  23. ELSE
  24.     SET limitTop = 3;
  25. END IF;
  26.  
  27.     SET @query = CONCAT("SELECT * FROM
  28. (SELECT * FROM bp_user_points b
  29. where points >= (SELECT points FROM bp_user_points b where userId = ",userId,"  limit 1)
  30. AND userId != ",userId," order by points desc limit ",limitTop,") T1
  31. UNION
  32. SELECT * FROM (SELECT * FROM bp_user_points b where userId = ",userId,") T2
  33. UNION
  34. SELECT * FROM
  35. (SELECT * FROM bp_user_points b
  36. where points <= (SELECT points FROM bp_user_points b where userId = ",userId,"  limit 1)
  37. AND userId != ",userId," order by points desc limit ",limitBottom,") T3;");
  38.  
  39.     PREPARE stmt FROM @query;
  40.     EXECUTE stmt;
  41.     DEALLOCATE PREPARE stmt;
  42.  
  43. END

El problema que estoy teniendo con esta consulta, es que por algun motivo los condicionales de arriba no funcionan como deberian o con la intension que tengo...

Esto basicamente lo que hace es contar cuantos registros tengo arriba mio y en caso que sean menor a 3 amplio la cantidad de registros que mostrare a abajo, y viceversa con los registros de abajo, de esta manera siempre mostraria 6 resultados sin importar en la posicion en la que estoy yo, si primero, ultimo o en el medio...

Como te mencionaba, parece no funcionar los condicionales, ya que por alguna razon me muestra mas resultados de los que supuestamente deberia limitar.

Gracias.

Best
C
__________________
Claudio A. Marrero - Proyect Manager
  #8 (permalink)  
Antiguo 19/09/2013, 07:12
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 16 años, 5 meses
Puntos: 2658
Respuesta: Ranking de lista de usuarios

Cita:
"Subquery return more than 1 result"
Eso significa simplemente que al menos una de las subconsultas está devolviendo más de un registro, y eso es tan simple como:
Código MySQL:
Ver original
  1.       (SELECT U.userId, U.nicename, MAX(H.points) points
  2.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  3.       WHERE H.points > (SELECT MAX(H.points)
  4.                         FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  5.                         WHERE H.userId = 21
  6.                         GROUP BY U.userId
  7.                         LIMIT 1)
  8.       GROUP BY U.userId
  9.       ORDER BY H.points DESC
  10.       LIMIT 3) T1
  11.       (SELECT U.userId, U.nicename , MAX(H.points) points
  12.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  13.       WHERE H.userId = 21
  14.       GROUP BY U.userId
  15.       LIMIT 1) T2
  16.       (SELECT U.userId, U.nicename, MAX(H.points) points
  17.       FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  18.       WHERE H.points < (SELECT MAX(H.points)
  19.                         FROM bp_users u INNER JOIN bp_historypoints h ON U.userId = H.userId
  20.                         WHERE H.userId = 21
  21.                         GROUP BY U.userId
  22.                         LIMIT 1)
  23.       GROUP BY U.userId
  24.       ORDER BY H.userId
  25.       LIMIT 2) T3;

Un consejo: Trata de no hacer códigos más complicados de lo necesario.
Si ya la query devuelve lo que necesitas, no tienes necesidad de implementarlo por medio de una sentencia preparada...
¿No te parece?
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #9 (permalink)  
Antiguo 19/09/2013, 08:00
 
Fecha de Ingreso: abril-2011
Ubicación: Mendoza
Mensajes: 42
Antigüedad: 13 años
Puntos: 0
Respuesta: Ranking de lista de usuarios

Excelente, esta consulta casi funciona como espero... Me faltaria resolver 2 cosas en esta consulta...

1. Que me muestra una determinada cantidad de resultados arriba o abajo segun la posicion en la que caigo, por ejemplo, esta consulta, me devuelve el user 21 arriba de todo, y solo 2 resultados abajo, lo mismo pasaria con un usuario que aparezca abajo de todo, me mostraria solo 3 resultados por encima..

2. El ranking:
Esta parte, estoy pensando todavia como resolverla, necesito mostrar la posicion 1,2,3 segun sus puntos, por ejemplo el que mas puntos tiene esta en la posicion 1, y el que menos en X posicion, por ende, cuando muestro los resultados, deberia tener la posicion de cada uno, que dependeria de la tabla..


Aca hice un intento, pero veo que tenemos un problema con estas consultas, tanto la que me propones como la que yo intente hacer..
Código SQL:
Ver original
  1. BEGIN
  2.  
  3.     DECLARE countRankingTop INTEGER DEFAULT 0;
  4.     DECLARE countRankingBottom INTEGER DEFAULT 0;
  5.     DECLARE limitTop INTEGER DEFAULT 3;
  6.     DECLARE limitBottom INTEGER DEFAULT 2;
  7.  
  8.     SELECT COUNT(userId) INTO countRankingTop FROM bp_user_points b
  9. WHERE points >= (SELECT points FROM bp_user_points b WHERE userId = `userId`)
  10. AND userId != `userId`;
  11.  
  12.     SELECT COUNT(userId) INTO countRankingBottom FROM bp_user_points b
  13. WHERE points <= (SELECT points FROM bp_user_points b WHERE userId = `userId`)
  14. AND userId != `userId`;
  15.  
  16.  
  17.     SET @query = CONCAT("SELECT * FROM
  18. (SELECT b.userId,b.points,u.nicename as username,
  19. (SELECT count(userId)+1 FROM bp_user_points WHERE points >= b.points and userId != b.userId) as rank
  20. FROM bp_user_points b
  21. INNER JOIN bp_users u ON u.userId = b.userId
  22. WHERE points >= (SELECT points FROM bp_user_points b WHERE b.userId = ",userId,"  limit 1)
  23. AND b.userId != ",userId," order by b.points desc limit ",limitTop,") points
  24. UNION
  25. SELECT * FROM
  26. (SELECT b.userId,b.points,u2.nicename as username,
  27. (SELECT count(userId)+1 FROM bp_user_points WHERE points >= b.points and userId != b.userId) as rank  
  28. FROM bp_user_points b
  29. INNER JOIN bp_users u2 ON u2.userId = b.userId
  30. WHERE b.userId = ",userId,") points
  31. UNION
  32. SELECT * FROM
  33. (SELECT b.userId,b.points,u3.nicename as username,
  34. (SELECT count(userId)+1 FROM bp_user_points WHERE points >= b.points and userId != b.userId) as rank
  35. FROM bp_user_points b
  36. INNER JOIN bp_users u3 ON u3.userId = b.userId
  37. WHERE b.points <= (SELECT points FROM bp_user_points b WHERE b.userId = ",userId,"  limit 1)
  38. AND b.userId != ",userId," order by points desc limit ",limitBottom,") points;");
  39.  
  40.     PREPARE stmt FROM @query;
  41.     EXECUTE stmt;
  42.     DEALLOCATE PREPARE stmt;
  43.  
  44. END

Resulta que los resultados de arriba de un usuario determinado estan ordenados de tal manera que me muestra los primeros resultados de la tabla, no los 2 anteriores a mi, si no los primeros de la consulta, por ende el resultado si lo pusiera en un raking es asi:

1
2
5 (YO)
6
7

Si lo ordenas al revez, queda mal tambien.

Muchas gracias por la ayuda, con respecto al store procedure que arme, visto lo de los condicionales????, tenes idea por que no funcionan?, eso me solucionara el primero puntoq ue te menciono aca..

Abrazo y gracias
__________________
Claudio A. Marrero - Proyect Manager

Última edición por cmarrero01; 19/09/2013 a las 08:40

Etiquetas: lista, ranking, sql, usuarios
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 09:55.