所以我有一张表如下:
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
我需要对它们进行分组,排序和排序以给出:
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
现在我知道你可以使用临时变量进行排名,就像这里一样,但是如何对分组进行排序呢?感谢您的任何见解!
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
这非常简单:
初始查询按id_class
第一个,id_student
第二个排序.
@student
并@class
初始化为-1
@class
用于测试是否输入下一组.如果id_class
(存储在其中@class
)的先前值不等于当前值(存储在其中id_class
),@student
则归零.否则是递增的.
@class
被赋值为新值id_class
,它将在下一行的第3步测试中使用.
Quassnoi的解决方案存在问题(标记为最佳答案).
我有同样的问题(即模拟SQL窗口功能在MySQL),我用来实现Quassnoi的解决方案,使用用户定义的变量来存储上一行值...
但是,也许在MySQL升级之后或者其他什么,我的查询不再起作用了.这是因为无法保证SELECT中字段的评估顺序.@class赋值可以在@student赋值之前进行求值,即使它是在SELECT之后放置的.
这在MySQL文档中提到如下:
作为一般规则,您不应该为用户变量赋值并在同一语句中读取值.您可能会得到您期望的结果,但这不能保证.涉及用户变量的表达式的评估顺序是未定义的,可能会根据给定语句中包含的元素进行更改; 此外,MySQL服务器版本之间的订单不保证相同.
来源:http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
最后我使用了这样的技巧,确保在读取后分配@class:
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
使用left()函数只是用来设置@class变量.然后,将left()(等于NULL)的结果连接到预期的结果是透明的.
不是很优雅,但它的工作原理!
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 ;
结果:
+------------+----------+-------+------+ | 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 | +------------+----------+-------+------+
从上面修改,这可以工作,但它比我认为它需要更复杂:
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