MySQL實現Oracle rank()排序


一、Oracle寫法介紹

MySQL5.7版本沒有提供類似Oracle的分析函數,比如開窗函數over(...),oracle開窗函數over(...)使用的話一般是和order、partition by、row_number()、rank()、dense_rank()幾個函數一起使用,具體的用法可以參考我之前的博客oracle開窗函數用法簡介

假如要獲取成績排序第一的學生信息,可以用如下的SQL:

select *
  from (select stuId, stuName, classId,
               row_number() over(partition by classId order by score desc) rn
          from t_score)
 where rn = 1;

二、Oracle和MySQL寫法對比

ok,就用學生成績排名的例子

學號 姓名 班級 成績
111 小王 1 92
123 小李 2 90
134 小錢 3 92
145 小順 4 100

數據表為t_score,字段分別為stuId,stuName,classId ,score

環境准備,先建表,寫數據:

#成績表
CREATE TABLE t_score(
   stuId VARCHAR(20),
   stuName VARCHAR(50),
   classId INT,
   score FLOAT
);
# 寫數據
INSERT INTO t_score(stuId,stuName,classId,score) VALUES('111','小王',2,92);
INSERT INTO t_score(stuId,stuName,classId,score) VALUES('123','小李',1,90);
INSERT INTO t_score(stuId,stuName,classId,score) VALUES('134','小錢',1,92);
INSERT INTO t_score(stuId,stuName,classId,score) VALUES('145','小順',2,100);

然后給出sql,用的是臨時變量的方法:

SELECT 
  IF(
    @classId := c.classId,
    @rn := @rn + 1,
    @rn := 1
  ) AS rn,
  c.stuId,
  c.stuName,
  c.classId,
  c.score ,
  @classId := c.classId
FROM
  (SELECT 
    stuId,
    stuName,
    classId,
    score 
  FROM
    t_score 
  ORDER BY score ASC) c,
  (SELECT 
    @rn := 0,
    @classId := NULL) r ;

不過對於上面的寫法,這里也進行分析,讓學習者可以更好理解,因為很多地方都是直接貼代碼,不寫明原因,對於入門者來說,可能都不理解

用執行計划來解釋:
在這里插入圖片描述

加上Explain,對於執行計划不熟悉的學習者可以參考我之前博客:MySQL Explain學習筆記

從執行計划可以看出:

  • ①、上面SQL,執行時候是先執行這條衍生查詢SQL,SELECT @rn := 0,@classId := NULL,這個其實是為了初始化臨時變量@rn和@classId
  • ②、執行查詢t_score,SELECT stuId, stuName,classId,score FROM t_score ORDER BY score ASC,同樣是返回一個衍生表
  • ③、主查詢1,SELECT @rn := 0,@classId := NULL衍生查詢完成后,進行別名為r的主查詢
    在這里插入圖片描述
  • ④、同理,主查詢2,衍生查詢SELECT stuId, stuName,classId,score FROM t_score ORDER BY score ASC查詢成功后,在進行外面的主查詢,也就是對別名為c的主表查詢
    在這里插入圖片描述

在這里插入圖片描述

所以網上這種寫法也是值得學習的,一種是利用了mysql的執行計划執行順序對臨時變量進行賦值,然后再用臨時變量進行疊加,寫法還是值得學習的

對於臨時變量的知識點,可以參考我之前博客:MySQL變量學習筆記

注意:這里網上有很多這種寫法,不過我驗證了,並不能實現了oracle類似的partition by效果,也就是沒分組效果,也有可能是哪里寫錯了,歡迎指出!

MySQL實現的效果:
在這里插入圖片描述

Oracle實現的效果:
在這里插入圖片描述

很顯然,如圖如比對所示,在oracle里,不僅分組了,而且rn也按照班級進行排名,Oracle實現的效果顯然和網上很多地方提出的這種寫法效果是不一樣的,網上的這種寫法僅僅是進行排序而已,並沒有按照班級進行分組排名

上面都是自己動手驗證過,目的是指出網上很多地方的這種寫法是不正確的,或許也有可能是自己寫錯哪里了,都歡迎指出!

所以,對於Oracle rank()、row_number加上開窗函數進行排序,並沒有partition by分組的時候,是可以用這種方法,不過寫法要改一下,代碼如:

SELECT 
 /* IF(
    @classId := c.classId
    AND @score := c.score,
    @rn := @rn + 1,
    @rn := 1
  ) AS rn,*/
 rn := @rn+1 as rn,
  c.stuId,
  c.stuName,
  c.classId,
  c.score ,
  @classId := c.classId
FROM
  (SELECT 
    stuId,
    stuName,
    classId,
    score 
  FROM
    t_score 
  ORDER BY score ASC,classId) c,
  (SELECT 
    @rn := 0,
    @classId := NULL) r ;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM