如何在MySQL中执行分组sorting
所以我有一个表格如下:
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
我需要然后分组,sorting和命令他们给:
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
现在我知道你可以使用一个临时variables来sorting, 就像这里一样 ,但是我怎么去做一个分组呢? 感谢任何见解!
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
sorting,id_student
秒sorting。 -
@student
和@class
被初始化为-1
-
@class
用于testing是否input下一个集合。 如果id_class
(存储在@class
)的前一个值不等于当前值(存储在id_class
),那么@student
将归零。 否则是递增的。 -
@class
被分配了@class
的新值,并将在下一行的第3步中用于testing。
Quassnoi的解决scheme存在问题(标记为最佳答案)。
我有同样的问题(即在MySQL模拟SQL窗口函数),我用来实现Quassnoi的解决scheme,使用用户定义的variables来存储以前的行值…
但是,也许在MySQL升级之后,我的查询不再工作。 这是因为SELECT中字段的评估顺序不能保证。 可以在@student赋值之前评估@class赋值,即使它放在SELECT之后。
这在MySQL文档中被提及如下:
作为一般规则,您不应该为用户variables赋值并在相同的语句中读取该值。 您可能会得到您所期望的结果,但不能保证。 涉及用户variables的expression式的评估顺序是未定义的,并且可能会根据给定语句中包含的元素进行更改; 另外,这个顺序不能保证在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()函数只是用来设置@classvariables。 然后,将left()(等于NULL)的结果连接到预期的结果是透明的。
不是很优雅,但它的作品!
从上面修改,这工作,但它比我想它需要更复杂:
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
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 | +------------+----------+-------+------+
我做了一些search,发现这篇文章拿出这个解决scheme:
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;
任何想法哪个更好?