在網上搜到的一種算法是利用自增長變量進行排序,然后再根據位置序號取。感覺有些復雜了。
一. group_concat來的省事些, 缺點是 group_concat默認有總長度限制,不能對太多的數求中位數。可以修改長度限制,也可以用第二種方法
1. 按順序聚合,逗號分隔,並計數
group_concat( number order by number asc)
2. 根據逗號拆分,判斷奇偶數去截取中間位置的那個數
具體代碼如下:
SELECT doctor_name doctor, -- 分組 count(1) patientNum, -- 總數 group_concat(dnt order by dnt asc), substring_index(SUBSTRING_INDEX(group_concat(dnt order by dnt asc),',',(count(1)+1) div 2),',',-1) dnt, case when count(1)%2=1 then substring_index(SUBSTRING_INDEX(group_concat(dnt order by dnt asc),',',(count(1)+1) div 2),',',-1)
else (substring_index(SUBSTRING_INDEX(group_concat(dnt order by dnt asc),',',(count(1)+2) div 2),',',-1) + substring_index(SUBSTRING_INDEX(group_concat(dnt order by dnt asc),',',count(1) div 2),',',-1))/2 end mid_dnt FROM ( SELECT distinct doctor_name, record_id, dnt from rp_green_channel_patient_detaile where dnt is not null AND visit_day >= '2020-03-30' AND visit_day <= '2020-06-27' ) AS a group by doctor_name
二. 組內排序
比如: 計算每個病種的住院費用中位數,數據如下
思路:
(1) 對每個病種下的,住院費用按照從小到大生成序號,生成a表
(2) 對每個病種的總人數進行計數, 生成b表
(3) a表和b表進行關聯,取a表最中間的1個數或兩個數:
如果總數是奇數,那么 ceil(b表總人數/2) 或 floor(b表總人數/2 +1) 即為中間那個數的序號
如果總數是偶數,那么中間兩個數依次是 ceil(b表總人數/2)、floor(b表總人數/2 +1)
所以兩表關聯后,取ceil(b表總人數/2) 或 floor(b表總人數/2 +1) 兩個位置的數,再取平均值即為中位數
代碼:
select a.base_group_name, round(avg(total_fee),1) median_fee -- 對已取出的兩個數求平均值 from ( select x.*, if(@p2=base_group_name,@r2:=@r2+1,@r2:=1) as rank, -- 在每個病種內進行排序,生成序號 @p2:=base_group_name from ( select base_group_name, record_id, total_fee from ads_gc_patient_detail )x inner join ( SELECT @p2:=NULL,@r2:=0 -- 排序方式的初始化參數 ) y order by base_group_name,total_fee asc -- 組內根據住院總費用增序排列 )a join ( select base_group_name, count(1) cnt from ads_gc_patient_detail group by base_group_name )b on a.base_group_name=b.base_group_name and (floor(b.cnt/2+1)=a.rank or ceil(b.cnt/2)=a.rank) -- 取中間的兩個數(組總數為偶數)或一個數(組總數為奇數) group by a.base_group_name