在网上搜到的一种算法是利用自增长变量进行排序,然后再根据位置序号取。感觉有些复杂了。
一. 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
每天进步一点点