【数据库】row_number()/rank() over(PARTITION BY xxx ORDER BY的MySQL5.7实现

背景说明

在oracle、mysql8.0以上版本有ROW_NUMBER() OVER (PARTITION BY) 函数可以进行分组并进行组内排序,

但是5.7以下版本是没有这个函数,我们这时候可以利用临时变量来实现这个效果。

测试表数据:test1

  • 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,'张三');

rank()和row_number()的区别

  • row_number():当分数相同的时候还会依次进行排序,排名不同。
  • rank():当分数相同的时候不会依次排序,相同的分数排名相同。

 row_number():

【数据库】row_number()/rank() over(PARTITION BY xxx ORDER BY的MySQL5.7实现_第1张图片

rank():

【数据库】row_number()/rank() over(PARTITION BY xxx ORDER BY的MySQL5.7实现_第2张图片

-- 百度示例


#使用开窗函数,兼容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;


 单个分组依据:PARTITION BY后面跟一个

-- 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的后面第一位
  

多个分组依据:PARTITION BY后面跟2个 

 -- 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 分组排序

你可能感兴趣的:(数据库,数据库)