有時候我們想要得到每個分組的前幾條記錄,這個時候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;