当我们遇到一些需求,比如组内分组排序,分组topN等,很容易想到用row_number()函数
在MySQL8.0版本中支持row_number函数,本文不做讨论,如果是MySQL5.7版本,要怎么写SQL呢?
测试表:
思路:
1.定义变量,用来存row_number
SET @row_number = 0;
SELECT*,( @row_number := @row_number + 1 ) AS ROW_NUMBER
from student
ORDER by score;
1.1 给每列数据标号
另一种简单优雅的写法是
SELECT*,( @row_number := @row_number + 1 ) AS ROW_NUMBER
from student,(select @row_number := 0) b
ORDER by score;
解释一下,因为sql执行顺序是 最先执行 FROM(可以参考sql执行顺序),所以会先初始化变量
2.组内排序
1.1 单个分组的情况:
-- 按照学科分组求排名:SELECTname, subject, score,@row_number := IF( @subject = subject , @row_number + 1, 1 ) AS row_number , @subject := subject AS `分组字段`-- 用来分组的字段
FROMstudent,(select @row_number := 0) b
ORDER BYsubject,score DESC;
要注意的是,
查询条件中的@row_number语句一定要在@FIRST语句的前面。
因为第一次查询的时候,第一个@subject = NULL, 就会设置@row_number= 1,然后设置@subject = 数学
,然后第二次查询的时候@subject与数学相等,然后执行@row_number= @row_number+ 1;如此类推。
如果顺序调换,@subject字段先赋值为数学
,然后再判断@row_number:=IF(@subject = a.SUBJECT, @last + 1, 1 ) 的@subject= a.SUBJECT条件恒成立,
就会导致@row_number总是=@row_number+ 1。
最终的结果就导致组内分组排序失败。
不信你可以试试
2.2假如有多个分组字段:
比如这里,我要求同一门学科同一个学生的多次成绩排名
#求同一门学科同一个学生的多次成绩排名
SELECT a.name, a.subject, a.score, @row_number :=IF(@FIRST = a.name and @SECOND =a.subject, @row_number + 1, 1 ) AS rn,@FIRST := a.name as `分组字段1`, @SECOND :=a.subject as `分组字段2`
FROM student a, ( SELECT @row_number := 0, @FIRST := NULL,@SECOND := NULL ) b
ORDER BY a.name, a.subject, a.score DESC
3.分组topN
有了以上内容,分组topN就好说了,只需要再在外面套一层,过滤一下
# 查询每门学科前三的学生:
SELECT * FROM(
SELECTname, subject, score,@row_number := IF( @subject = subject , @row_number + 1, 1 ) AS row_number , @subject := subject AS `分组字段` -- 用来分组的字段
FROMstudent,(select @row_number := 0) b
ORDER BYsubject,score DESC) twhere row_number<=3
附上建表语句
建测试表
CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',`name` varchar(5) NOT NULL COMMENT '学生姓名',`subject` varchar(6) DEFAULT NULL COMMENT '科目',`score` smallint(3) DEFAULT NULL COMMENT '分数',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
插入数据
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (1, '张三', '语文', 77);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (2, '李四', '语文', 67);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (3, '王五', '语文', 85);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (4, '张三', '数学', 82);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (5, '李四', '数学', 67);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (6, '王五', '英语', 85);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (7, '王五', '数学', 85);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (13, '张三', '语文', 87);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (14, '李四', '语文', 63);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (15, '王五', '语文', 55);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (16, '张三', '数学', 72);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (17, '李四', '数学', 97);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (18, '王五', '英语', 89);
INSERT INTO `test`.`student`(`id`, `name`, `subject`, `score`) VALUES (19, '王五', '数学', 87);