在oracle、mysql8.0以上版本有ROW_NUMBER() OVER (PARTITION BY) 函数可以进行分组并进行组内排序,
但是5.7以下版本是没有这个函数,我们这时候可以利用临时变量来实现这个效果。
CREATE DATABASE /*!32312 IF NOT EXISTS*/`db_test` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `db_test`; /*Table structure for table `test1` */
DROP TABLE IF EXISTS `test1`;
CREATE TABLE `test1` ( `id` int(10) NOT NULL, `score` int(20) DEFAULT NULL, `class` char(10) COLLATE utf8_bin DEFAULT NULL, `name` char(20) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
/*Data for the table `test1` */
insert into `test1`(`id`,`score`,`class`,`name`) values (1,100,'语文','张三'),(2,98,'语文','李四'),(3,98,'语文','王五'),(4,98,'数学','张三'),(5,96,'数学','李四'),(6,92,'数学','王五'),(7,85,'数学','张三'),(8,96,'语文','张三'),(9,96,'语文','张三'),(10,91,'语文','张三'),(11,91,NULL,'张三');
row_number():
rank():
#使用开窗函数,兼容oracle
SELECT * FROM (
SELECT *,row_number() over(PARTITION BY class ORDER BY score DESC) mm FROM a
) b WHERE mm=1
#自定义实现row_number() over(PARTITION BY class ORDER BY )
SELECT id,class,score,rank FROM (
SELECT
b.*,
@rownum := @rownum+1 ,-- 定义用户变量@rownum来记录数据的行号。通过赋值语句@rownum := @rownum+1来累加达到递增行号。
IF(@pdept=b.class,@rank:=@rank+1,@rank:=1) AS rank,-- 如果当前分组编号和上一次分组编号相同,则@rank(对每一组的数据进行编号)值加1,否则表示为新的分组,从1开始
@pdept:=b.class -- 定义变量@pdept用来保存上一次的分组id
FROM (SELECT * FROM test1 a ORDER BY a.class,a.score DESC) b ,-- 这里的排序不确定是否需要,保险点还是加上吧
(SELECT @rownum :=0 , @pdept := NULL ,@rank:=0) c -- 初始化自定义变量值
ORDER BY b.class,b.score DESC -- 该排序必须,否则结果会不对
) result
HAVING rank < 2;
-- MySQL8语法
-- row_number()
SELECT T.*,
ROW_NUMBER() OVER(PARTITION BY T.class ORDER BY T.score DESC) RNK FROM test1 T
-- rank()
SELECT T.*,
RANK() OVER(PARTITION BY T.class ORDER BY T.score DESC) RNK FROM test1 T
-- MySQL5语法,8不能用,会报错
-- row_number() desc降序
SELECT
a.*,
@rownum := @rownum+1 ,
IF(@class=a.class ,@rank:=@rank+1,@rank:=1) AS rank,
@class:=a.class
FROM test1 a,
(SELECT @rownum :=0 , @class := NULL , @score:=NULL, @rank:=0) b
ORDER BY a.class DESC, a.score DESC;
-- row_number() asc升序,默认
SELECT
a.*,
@rownum := @rownum+1 ,
IF(@class=a.class ,@rank:=@rank+1,@rank:=1) AS rank,
@class:=a.class
FROM test1 a,
(SELECT @rownum :=0 , @class := NULL , @score:=NULL, @rank:=0) b
ORDER BY a.class ASC, a.score ASC;
-- MySQL5语法,8不能用,会报错
-- rank() desc
SELECT
a.*,
@rownum := @rownum+1 ,
IF(@class=a.class ,@rank1:=@rank1+1,@rank1:=1) AS rank1, #为了拿到rank1参数,给下面一行使用
IF(@class=a.class,@rank:=(IF(@score=a.score, @rank, @rank1)),@rank:=1) AS rank,
@class:=a.class,
@score:=a.score
FROM test1 a,
(SELECT @rownum :=0 , @class := NULL , @score:=NULL, @rank:=0) b
ORDER BY a.class DESC, a.score DESC; #partition by谁,就放在这里order by的后面第一位
-- MySQL5语法,8不能用,会报错
-- rank() asc
SELECT
a.*,
@rownum := @rownum+1 ,
IF(@class=a.class ,@rank1:=@rank1+1,@rank1:=1) AS rank1, #为了拿到rank1参数,给下面一行使用
IF(@class=a.class,@rank:=(IF(@score=a.score, @rank, @rank1)),@rank:=1) AS rank,
@class:=a.class,
@score:=a.score
FROM test1 a,
(SELECT @rownum :=0 , @class := NULL , @score:=NULL, @rank:=0, @rank1:=0) b
ORDER BY a.class ASC, a.score ASC; #partition by谁,就放在这里order by的后面第一位
-- MySQL8语法
-- row_number()
SELECT T.*,
ROW_NUMBER() OVER(PARTITION BY T.class, T.name ORDER BY T.score DESC) RNK FROM test1 T #order by后面只是最后排序,不影响ran
-- rank()
SELECT T.*,
RANK() OVER(PARTITION BY T.class, T.name ORDER BY T.score DESC) RNK FROM test1 T
-- MySQL5语法,8不能用,会报错
-- row_number() desc降序
SELECT
a.*,
@rownum := @rownum+1 ,
IF(@class<=>a.class AND @name=a.name,@rank:=@rank+1,@rank:=1) AS rank,
@class:=a.class,
@name:=a.name
FROM test1 a,
(SELECT @rownum :=0 , @rank:=0, @class := NULL , @score:=NULL, @name:=NULL) b
ORDER BY a.class DESC, a.name DESC, a.score DESC;
-- MySQL5语法,8不能用,会报错
-- rank() desc降序
SELECT
a.*,
@rownum := @rownum+1 ,
IF(@class<=>a.class AND @name<=>a.name,@rank1:=@rank1+1,@rank1:=1) AS rank1, #为了拿到rank1参数,给下面一行使用
IF(@class<=>a.class AND @name<=>a.name,@rank:=(IF(@score<=>a.score, @rank, @rank1)),@rank:=1) AS rank,
@class:=a.class,
@name:=a.name,
@score:=a.score
FROM test1 a,
(SELECT @rownum :=0 , @rank:=0, @class := NULL , @score:=NULL, @name:=NULL) b
ORDER BY a.class DESC, a.name DESC, a.score DESC;
注意:
1、当score没有相同的,那么rank()和row_number()效果一样
2、如果表数据中有null,则判断的时候需要用 <=>来判断是否想等,因为null不能用=判断 select null = null 输出为null,不对,select null <=> null才返回1,对的
参考资料:
mysql5.7 实现分组后组内排序功能 ROW_NUMBER() OVER (PARTITION BY)_普通网友的博客-CSDN博客_mysql5.7 分组排序