MySQL中多种排名实现

前言

我们主要以MySQL5.7和8为例,分别实现了多种排名,唯一连续排名、并列连续排名、并列不连续排名,演示了分组和不分组的情况,基本已经涵盖了所有情况了。

一、准备数据

新建一张表forlan_score

CREATE TABLE `forlan_score` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `student_name` varchar(255) DEFAULT NULL COMMENT '学生名称',
  `score` int(20) DEFAULT '-1' COMMENT '分数',
	`course_name` varchar(255) DEFAULT NULL COMMENT '课程',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='学生成绩表';

插入数据

INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (1, '小明', 70, '数学');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (2, '小红', 65, '英语');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (3, '小林', 100, '数学');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (4, '小黄', 100, '语文');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (5, '小东', 80, '语文');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (6, '小美', 90, '英语');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (7, '小伟', 88, '英语');
INSERT INTO `test`.`forlan_score` (`id`, `student_name`, `score`, `course_name`) VALUES (8, '小小', 100, '数学');

二、实现排名(不分组)

1、不重复,连续排名

1)mysql5.7实现

  • 使用自定义变量(外部sql)
SET @cur_rank := 0;
SELECT
	student_name,
	score,
	@cur_rank := @cur_rank + 1 AS ranking 
FROM
	forlan_score 
ORDER BY
	score DESC;
  • 使用自定义变量(内部sql)(推荐)
SELECT
	fs.student_name,
	fs.score,
	( @cur_rank := @cur_rank + 1 ) AS ranking 
FROM
	forlan_score fs,
	( SELECT @cur_rank := 0 ) r 
ORDER BY
	score DESC;

2)mysql8实现

ROW_NUMBER()

SELECT
	student_name,
	score,
	ROW_NUMBER() OVER ( ORDER BY score DESC ) AS ranking 
FROM
	forlan_score;

3)效果

+--------------+-------+---------+
| student_name | score | ranking |
+--------------+-------+---------+
| 小林         |   100 |       1 |
| 小黄         |   100 |       2 |
| 小小         |   100 |       3 |
| 小美         |    90 |       4 |
| 小伟         |    88 |       5 |
| 小东         |    80 |       6 |
| 小明         |    70 |       7 |
| 小红         |    65 |       8 |
+--------------+-------+---------+

2、并列,连续排名

1)mysql5.7实现

  • 使用自定义变量 + IF
SELECT
	fs.student_name,
	fs.score,
	IF( @pre_score = fs.score, @cur_rank, @cur_rank := @cur_rank + 1 ) AS ranking,
	@pre_score := fs.score 
FROM
	forlan_score fs,( SELECT @cur_rank := 0, @pre_score := NULL ) r 
ORDER BY
	fs.score DESC;
  • 使用自定义变量 + CASE WHEN
SELECT
	fs.student_name,
	fs.score,
	(
		CASE
			WHEN @pre_score = fs.score THEN @cur_rank 
			WHEN @pre_score := fs.score THEN @cur_rank := @cur_rank + 1 
		END 
	) AS ranking 
	FROM
		forlan_score fs,(SELECT @cur_rank := 0,@pre_score := NULL) r 
ORDER BY
	fs.score DESC;

2)mysql8实现

DENSE_RANK()

SELECT
	student_name,
	score,
	DENSE_RANK() OVER ( ORDER BY score DESC ) AS ranking 
FROM
	forlan_score;

3)效果

+--------------+-------+---------+
| student_name | score | ranking |
+--------------+-------+---------+
| 小林         |   100 |       1 |
| 小黄         |   100 |       1 |
| 小小         |   100 |       1 |
| 小美         |    90 |       2 |
| 小伟         |    88 |       3 |
| 小东         |    80 |       4 |
| 小明         |    70 |       5 |
| 小红         |    65 |       6 |
+--------------+-------+---------+

3、并列,不连续排名

1)mysql5.7实现

  • 使用自定义变量 + IF
SELECT
	fs.student_name,
	fs.score,
	@row_num := @row_num + 1,
	IF( @pre_score = fs.score, @cur_rank, @cur_rank := @row_num ) AS ranking,
	@pre_score := fs.score 
FROM
	forlan_score fs,
	(SELECT @cur_rank := 0,@pre_score := NULL,@row_num := 0 ) r 
ORDER BY
	fs.score DESC;
  • 使用自定义变量 + CASE WHEN
SELECT
	fs.student_name,
	fs.score,
	@row_num := @row_num + 1,
	( CASE WHEN @pre_score = fs.score THEN @cur_rank WHEN @pre_score := fs.score THEN @cur_rank := @row_num END ) AS ranking 
FROM
	forlan_score fs,
	( SELECT @cur_rank := 0, @pre_score := NULL, @row_num := 0 ) r 
ORDER BY
	fs.score DESC;

2)mysql8实现

RANK()

SELECT
	student_name,
	score,
	RANK() OVER ( ORDER BY score DESC ) AS ranking 
FROM
	forlan_score;

3)效果

+--------------+-------+---------+
| student_name | score | ranking |
+--------------+-------+---------+
| 小林         |   100 |       1 |
| 小黄         |   100 |       1 |
| 小小         |   100 |       1 |
| 小美         |    90 |       4 |
| 小伟         |    88 |       5 |
| 小东         |    80 |       6 |
| 小明         |    70 |       7 |
| 小红         |    65 |       8 |
+--------------+-------+---------+

三、实现按照课程分组排名

1、不重复,连续排名

1)mysql5.7实现

  • 使用自定义变量 + IF
SELECT
	fs.student_name,
	fs.course_name,
	fs.score,
	IF(@cur_couse = course_name, @cur_rank := @cur_rank+1, @cur_rank :=1) AS ranking,
	@cur_couse := fs.course_name
FROM
	forlan_score fs,
	( SELECT @cur_rank := 0,  @cur_couse := NULL ) r 
ORDER BY
	fs.course_name,fs.score DESC;

2)mysql8实现

ROW_NUMBER()

SELECT
	student_name,
	course_name,
	score,
	ROW_NUMBER() OVER (PARTITION BY course_name ORDER BY course_name,score DESC) AS ranking
FROM
	forlan_score;

3)效果

+--------------+-------------+-------+---------+
| student_name | course_name | score | ranking |
+--------------+-------------+-------+---------+
| 小林         | 数学        |   100 |       1 |
| 小小         | 数学        |   100 |       2 |
| 小明         | 数学        |    70 |       3 |
| 小美         | 英语        |    90 |       1 |
| 小伟         | 英语        |    88 |       2 |
| 小红         | 英语        |    65 |       3 |
| 小黄         | 语文        |   100 |       1 |
| 小东         | 语文        |    80 |       2 |
+--------------+-------------+-------+---------+

2、并列,连续排名

1)mysql5.7实现

  • 使用自定义变量 + IF
SELECT
	fs.student_name,
	fs.course_name,
	fs.score,
	IF(@cur_couse = course_name, IF( @pre_score = fs.score, @cur_rank, @cur_rank := @cur_rank+1 ), @cur_rank :=1) AS ranking,
	@pre_score := fs.score,
	@cur_couse := fs.course_name
FROM
	forlan_score fs,
	( SELECT @cur_rank := 0, @pre_score := NULL, @cur_couse := NULL ) r 
ORDER BY
	fs.course_name,fs.score DESC;

2)mysql8实现

DENSE_RANK()

SELECT
	student_name,
	course_name,
	score,
	DENSE_RANK() OVER (PARTITION BY course_name ORDER BY course_name,score DESC) AS ranking
FROM
	forlan_score;

3)效果

+--------------+-------------+-------+---------+
| student_name | course_name | score | ranking |
+--------------+-------------+-------+---------+
| 小林         | 数学        |   100 |       1 |
| 小小         | 数学        |   100 |       1 |
| 小明         | 数学        |    70 |       2 |
| 小美         | 英语        |    90 |       1 |
| 小伟         | 英语        |    88 |       2 |
| 小红         | 英语        |    65 |       3 |
| 小黄         | 语文        |   100 |       1 |
| 小东         | 语文        |    80 |       2 |
+--------------+-------------+-------+---------+

3、并列,不连续排名

1)mysql5.7实现

  • 使用自定义变量 + IF
SELECT
	fs.student_name,
	fs.course_name,
	fs.score,
	IF(@cur_couse = course_name, @row_num := @row_num + 1, @row_num :=1),
	IF(@cur_couse = course_name, IF( @pre_score = fs.score, @cur_rank, @cur_rank := @row_num ),@cur_rank :=1) AS ranking,
	@pre_score := fs.score,
	@cur_couse := fs.course_name
FROM
	forlan_score fs,
	( SELECT @cur_rank := 0, @pre_score := NULL, @row_num := 0,@cur_couse := NULL ) r 
ORDER BY
	fs.course_name,fs.score DESC;

2)mysql8实现

RANK()

SELECT
	student_name,
	course_name,
	score,
	RANK() OVER (PARTITION BY course_name ORDER BY course_name,score DESC) AS ranking
FROM
	forlan_score;

3)效果

+--------------+-------------+-------+---------+
| student_name | course_name | score | ranking |
+--------------+-------------+-------+---------+
| 小林         | 数学        |   100 |       1 |
| 小小         | 数学        |   100 |       1 |
| 小明         | 数学        |    70 |       3 |
| 小美         | 英语        |    90 |       1 |
| 小伟         | 英语        |    88 |       2 |
| 小红         | 英语        |    65 |       3 |
| 小黄         | 语文        |   100 |       1 |
| 小东         | 语文        |    80 |       2 |
+--------------+-------------+-------+---------+
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员Forlan

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值