mysql實現窗口函數功能

有時候我們想要得到每個分組的前幾條記錄,這個時候oracle中row_number函數使用非常方便,可惜MYSQL從8.0版本開始才支持窗口函數。本文介紹一些通過sql實現窗口函數效果的方法。

1.利用用戶變量實現數據自增

表flow_task有phaseno(序列號),objectno(編號)等幾個字段,我們想實現根據編號字段分組,然後組內根據序列號排序功能

select @rownum:=@rownum+1 rownum,a.objectno,a.phaseno,
  if(@objno=a.OBJECTNO or (@objno is null and a.objectno is null),
  @rank:=@rank+1,
  @rank:=1) as row_number,
  @objno:=a.OBJECTNO
from(SELECT * from flow_task order by OBJECTNO,phaseno asc)a,
(select @rownum :=0,@objno:=null,@rank:=0)b

注意:order by OBJECTNO,phaseno asc 分組字段在前,排序字段在後

 

原理是,先 order by OBJECTNO,phaseno asc,這樣後相同編號的記錄會在一塊兒,並且已經是phaseno有序asc的

select的字段一個一個的看:

@rownum:=@rownum+1,每一行在上行@rownum變量值的基礎上+1

if(@objno=a.OBJECTNO or (@objno is null and a.objectno is null),@rank:=@rank+1,@rank:=1),每一行判斷,當前行的objectno(編號)是否等於上一個@objno變量值,如果是在上一個@rank變量值基礎上+1,否則@rank賦值1

@objno:=a.OBJECTNO,當前行objectno賦值給變量@objno

 

ps:如果想要分組後某個字段的幾個值,也可以使用group_concat函數

select a.objectno,group_concat(ifnull(a.phaseno,'')) phaseno from(SELECT * from flow_task order by OBJECTNO,phaseno asc)a GROUP BY a.objectno

 

可以看到,group_concat函數把分組後某個字段的值用,拼接起來

要獲取前3個值,使用substring_index函數

 select a.objectno,group_concat(ifnull(a.phaseno,'')) phaseno,substring_index(group_concat(ifnull(a.phaseno,'')),',',3) sub_phaseno from(SELECT * from flow_task order by OBJECTNO,phaseno asc)a GROUP BY a.objectno

 

2.子查詢

mysql> select a.*,
    -> (select count(*) from tbl where col<=a.col) as rownum
    -> from tbl a;
+----+------+--------+
| id | col | rownum |
+----+------+--------+
| 1 |   26 |      1 |
| 2 |   46 |      3 |
| 3 |   35 |      2 |
| 4 |   68 |      4 |
| 5 |   93 |      6 |
| 6 |   92 |      5 |
---------------------

 

具體實現方法:

SELECT a.* FROM student a WHERE (SELECT COUNT(*) FROM student 
WHERE class = a.class AND id < a.id ) < 2 ORDER BY a.class,a.id;

 

通過SELECT COUNT(*) FROM student WHERE class = a.class AND id < a.id 這一句來查詢當前記錄在class相同的情況下的排序位置,然後判斷 < 2的則是前兩名的記錄。

 

缺點,顯然效率會差一些。

 

 

3.top N

查詢每門課程分數最高的2個學生以及成績

 

方法一:

select a.name,a.course,a.score

from test1 a left join test1 b on a.course=b.course and a.score<b.score

group by a.name,a.course,a.score

having count(b.id)<2

order by a.course,a.score desc;

方法二:

select *

from test1 a

where 2>(select count(*) from test1 where course=a.course and score>a.score)

order by a.course,a.score desc;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章