1. 程式人生 > >Mysql實現先排序後分組的需求

Mysql實現先排序後分組的需求

在工作中做報表的時候,需要按建立時間排序,然後在對某些欄位進行分組排序。
首先遇到的問題是以前使用oracle資料庫時可以使用分組排序函式直接排序,由於切換到了Mysql資料庫,所以不能使用相同的解決方法。查詢相關資料後得出了一些靈感。oracle分組排序函式如下(複習下):

Oracle中row_number()、rank()、dense_rank() 的區別:
		--row_number() 順序排序 1 2 3 4 5 
		--rank() 跳躍排序,如果有兩個第一級別時,接下來是第三級別 1 2 2 4 5
		--dense_rank() 連續排序,如果有兩個第一級別時,接下來是第二級別  1 2 2 3 4
			關於Parttion by:
				Parttion by關鍵字是Oracle中分析性函式的一部分,用於給結果集進行分割槽。它和聚合函式Group by不同的地方在於它只是將原始資料進行名次排列,能夠返回一個分組中的多條記錄(記錄數不變),而Group by是對原始資料進行聚合統計,一般只有一條反映統計值的結果(每組返回一條)。
			總結:
				在使用排名函式的時候需要注意以下三點:
				1、排名函式必須有 OVER 子句。
				2、排名函式必須有包含 ORDER BY 的 OVER 子句。
				3、分組內從1開始排序。
			TIPS:
				使用rank over()的時候,空值是最大的,如果排序欄位為null, 可能造成null欄位排在最前面,影響排序結果。
				(使用rank()/dense_rank() 時,必須要帶order by否則非法)
			可以這樣: rank over(partition by course order by score desc nulls last) 

Mysql的分組排序功能實現如下(我是按照兩個欄位進行分組的排序的):

select t.*,IF(@tmp=t.appr_node_name and @tmp1=t.stage,@rank:[email protected] + 1,@rank:=1) as rownum,
		 @tmp:=t.appr_node_name as tmp,
			@tmp1 :=t.stage as tmp1
 from (select rmt_work_appr_record.appr_record_id,rmt_work_appr_record.appr_node_id,
		 rmt_work_appr_record.approver_name,
	   rmt_work_appr_record.appr_node_name,
	   rmt_work_appr_record.stage,
	   rmt_work_appr_record.created_dt
from rmt_work_appr_record
where work_permit_id = 2000000004181 
GROUP BY appr_node_id,approver_name,appr_node_name,stage ORDER BY created_dt) t; -- 按順序遍歷累計,順序非常重要,直接會影響查詢排序的正確性

效果如下:
查詢結果
可是這裡的建立時間欄位created_dt 並沒有按照原有的語義進行排序。查閱資料後知道Mysql按照先分組後排序的順序執行,所以最後的order by子句沒有生效。可以使用子查詢解決。
未分組的查詢結果如下:
在這裡插入圖片描述
可以看出分組排序並未按照建立時間的逆序查詢出相應的效果。於是嘗試先使用子查詢進行排序,然後再進行分組排序。

select t2.*,IF(@tmp=t2.appr_node_name and @tmp1=t2.stage,@rank:[email protected] + 1,@rank:=1) as rownum,
		 @tmp:=t2.appr_node_name as tmp,
			@tmp1 :=t2.stage as tmp1
 from (
select t.appr_record_id,t.appr_node_id,
		 t.approver_name,
	   t.appr_node_name,
	   t.stage,
	   t.created_dt
from
(select  rmt_work_appr_record.appr_record_id,rmt_work_appr_record.appr_node_id,
		 rmt_work_appr_record.approver_name,
	   rmt_work_appr_record.appr_node_name,
	   rmt_work_appr_record.stage,
	   rmt_work_appr_record.created_dt
from rmt_work_appr_record
where work_permit_id = 2000000004181 ORDER BY created_dt desc) t GROUP BY t.appr_node_id,t.approver_name,t.appr_node_name,t.stage) t2;

先通過子查詢進行排序,然後再進行分組,最後使用臨時變數記錄分組排序的值:
在這裡插入圖片描述
現在就實現了先排序後分組排序的需求了。
參考資料:
分組排序
Mysql下實現先排序後分組