当前位置:  开发笔记 > 后端 > 正文

如何在MySQL中执行分组排名

如何解决《如何在MySQL中执行分组排名》经验,为你挑选了4个好方法。

所以我有一张表如下:

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

现在我知道你可以使用临时变量进行排名,就像这里一样,但是如何对分组进行排序呢?感谢您的任何见解!



1> Quassnoi..:
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步测试中使用.


这可以保证按预期工作吗?MySQL的[文档](http://dev.mysql.com/doc/refman/5.0/en//user-variables.html)说:"作为一般规则,你永远不应该为用户变量赋值并读取同一声明中的值"

2> Nicolas Paya..:

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)的结果连接到预期的结果是透明的.

不是很优雅,但它的工作原理!



3> Jon Armstron..:
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 |
+------------+----------+-------+------+



4> achinda99..:

从上面修改,这可以工作,但它比我认为它需要更复杂:

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

推荐阅读
mobiledu2402851203
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有