如何在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 

这工作在一个非常简单的方式:

  1. 初始查询id_classsorting, id_student秒sorting。
  2. @student@class被初始化为-1
  3. @class用于testing是否input下一个集合。 如果id_class (存储在@class )的前一个值不等于当前值(存储在id_class ),那么@student将归零。 否则是递增的。
  4. @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; 

任何想法哪个更好?