2009-02-10 7 views
22

así que tengo una tabla como sigue:Cómo realizar la clasificación agrupan en MySQL

ID_STUDENT | ID_CLASS | GRADE 
----------------------------- 
    1  | 1  | 90 
    1  | 2  | 80 
    2  | 1  | 99 
    3  | 1  | 80 
    4  | 1  | 70 
    5  | 2  | 78 
    6  | 2  | 90 
    6  | 3  | 50 
    7  | 3  | 90 

necesito a continuación, agrupar, clasificar y ordenar a dar:

ID_STUDENT | ID_CLASS | GRADE | RANK 
------------------------------------ 
    2  | 1  | 99 | 1 
    1  | 1  | 90 | 2 
    3  | 1  | 80 | 3 
    4  | 1  | 70 | 4 
    6  | 2  | 90 | 1 
    1  | 2  | 80 | 2 
    5  | 2  | 78 | 3 
    7  | 3  | 90 | 1 
    6  | 3  | 50 | 2 

Ahora sé que usted puede usar una variable de temperatura para clasificar, like here, pero ¿cómo lo hago para un conjunto agrupado? Gracias por cualquier idea!

+0

Me pregunto si MySQL admite la función de ventana RANK(): http://en.wikipedia.org/wiki/Select_(SQL)#Limiting_result_rows –

Respuesta

28
SELECT id_student, id_class, grade, 
    @student:=CASE WHEN @class <> id_class THEN 0 ELSE @student+1 END AS rn, 
    @class:=id_class AS clset 
FROM 
    (SELECT @student:= -1) s, 
    (SELECT @class:= -1) c, 
    (SELECT * 
    FROM mytable 
    ORDER BY id_class, id_student 
) t 

Esto funciona de una manera muy sencilla:

  1. consulta inicial se ordena por primera id_class, id_student segundos.
  2. @student y @class se inicializan a -1
  3. @class se utiliza para probar si se introduce la siguiente serie. Si el valor anterior de id_class (que se almacena en @class) no es igual al valor actual (que se almacena en id_class), el @student se pone a cero. De lo contrario, se incrementa.
  4. @class tiene asignado el nuevo valor de id_class, y se usará en la prueba en el paso 3 en la siguiente fila.
+0

Me dio un error en "establecer". Lo modifiqué un poco y lo hice funcionar. Lo publiqué como una respuesta a continuación. ¿Alguna forma de optimizarlo? Además, ¿puedes explicar cómo funciona? ¡Gracias por la ayuda! – achinda99

+2

¿Se garantiza que funcione como se esperaba? La [documentación] de MySQL (http://dev.mysql.com/doc/refman/5.0/en//user-variables.html) dice: "Como regla general, nunca debe asignar un valor a una variable de usuario y leer el valor dentro de la misma declaración " –

+0

@YouvalBronicki: no, no lo es. Para estar en el lado seguro, debe asignar '@ student' y' @ class' en declaraciones separadas y/o envolver todo en un procedimiento almacenado. Sin embargo, no todos los marcos admiten persistencia de conexión y procedimientos almacenados. – Quassnoi

4

Modificado desde arriba, esto funciona pero es más compleja de lo que creo que tiene que ser:

SELECT ID_STUDENT, ID_CLASS, GRADE, RANK 
FROM 
    (SELECT ID_STUDENT, ID_CLASS, GRADE, 
     @student:=CASE WHEN @class <> id_class THEN 1 ELSE @student+1 END AS RANK, 
     @class:=id_class AS CLASS 
    FROM 
     (SELECT @student:= 0) AS s, 
     (SELECT @class:= 0) AS c, 
     (SELECT * 
      FROM Students 
      ORDER BY ID_CLASS, GRADE DESC 
     ) AS temp 
    ) AS temp2 
1

lo hice un poco de búsqueda, encontraron this article para llegar a esta solución:

SELECT S2.*, 
FIND_IN_SET(
S2.GRADE 
, (
SELECT GROUP_CONCAT(GRADE ORDER BY GRADE DESC) 
FROM Students S1 
WHERE S1.ID_CLASS = S2.ID_CLASS 
) 
) AS RANK 
FROM Students S2 ORDER BY ID_CLASS, GRADE DESC; 

¿Alguna idea de cuál es mejor?

+0

Mina uno es mejor, por supuesto :) Este realizará una unión con una clase completa para cada fila seleccionada, esto es malo para el rendimiento. Sin embargo, en datos reales, apenas notará ninguna diferencia. – Quassnoi

3
SELECT g1.student_id 
    , g1.class_id 
    , g1.grade 
    , COUNT(*) AS rank 
    FROM grades AS g1 
    JOIN grades AS g2 
    ON (g2.grade, g2.student_id) >= (g1.grade, g1.student_id) 
    AND g1.class_id = g2.class_id 
GROUP BY g1.student_id 
     , g1.class_id 
     , g1.grade 
ORDER BY g1.class_id 
     , rank 
; 

Resultado:

+------------+----------+-------+------+ 
| student_id | class_id | grade | rank | 
+------------+----------+-------+------+ 
|   2 |  1 | 99 | 1 | 
|   1 |  1 | 90 | 2 | 
|   3 |  1 | 80 | 3 | 
|   4 |  1 | 70 | 4 | 
|   6 |  2 | 90 | 1 | 
|   1 |  2 | 80 | 2 | 
|   5 |  2 | 78 | 3 | 
|   7 |  3 | 90 | 1 | 
|   6 |  3 | 50 | 2 | 
+------------+----------+-------+------+ 
11

Hay un problema con la solución de Quassnoi (marcado como mejor respuesta).

que tienen la misma problemática (es decir, la simulación de la función ventana SQL en MySQL) y yo solía poner en práctica la solución de Quassnoi, el uso de variables definidas por el usuario para almacenar valor de la fila anterior ...

Pero, tal vez después de una actualización de MySQL o lo que sea, mi consulta ya no funcionaba. Esto se debe a que el orden de evaluación de los campos en SELECT no está garantizado. La asignación de clase @ podría evaluarse antes de la asignación de @student, incluso si se coloca después en SELECT.

Esto se ve mencionado en la documentación de MySQL de la siguiente manera:

Como regla general, nunca se debe asignar un valor a una variable de usuario y leer el valor dentro de la misma declaración. Es posible que obtenga los resultados que espera, pero esto no está garantizado.El orden de evaluación para expresiones que involucran variables de usuario no está definido y puede cambiar en función de los elementos contenidos en una declaración dada; Además, esta orden no garantiza que sea la misma entre las versiones del servidor MySQL.

fuente: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html

fin he utilizado un truco así que para asegurarse de asignar @class después de leerlo:

SELECT id_student, id_class, grade, 
    @student:=CASE WHEN @class <> id_class THEN concat(left(@class:=id_class, 0), 0) ELSE @student+1 END AS rn 
FROM 
    (SELECT @student:= -1) s, 
    (SELECT @class:= -1) c, 
    (SELECT * 
    FROM mytable 
    ORDER BY id_class, grade desc 
) t 

Utilizando la función de la izquierda() sólo se utiliza para establecer @ variable de clase Luego, concatene el resultado de left() (igual a NULL) al resultado esperado es transparente.

No muy elegante pero funciona!

Cuestiones relacionadas