如果按子句分组,如何在 mysql 中获得中位数?

How to get median in mysql if group by clause?

我的 mysql 版本是 8.+.

Table结构:

CREATE TABLE `loss` (
  `date` date DEFAULT NULL,
  `circle` varchar(100) DEFAULT NULL,
  `district` varchar(100) DEFAULT NULL,
  `kpi_1` int(11) DEFAULT NULL,
  `kpi_2` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

tableloss的数据:

insert  into `loss`(`date`,`circle`,`district`,`kpi_1`,`kpi_2`) values 
('2020-09-20','101','delhi',90,100),
('2020-09-20','102','Punjab',80,10),
('2020-09-20','104','delhi',90,90),
('2020-09-20','104','New Delhi',20,10),
('2020-09-20','104','Punjab',45,23),
('2020-09-20','104','New Delhi',4,13),
('2020-09-20','104','New Delhi',7,150),
('2020-09-20','104','New Delhi',80,40),
('2020-09-20','104','New Delhi',80,50),
('2020-09-20','104','New Delhi',NULL,NULL);

查询:

select date,circle,district,count(*) as total_rows,sum(kpi_1),sum(kpi_2) from loss
group by date,circle,distrcit

我可以获得所有聚合,但如何找到中位数?

不幸的是,MySQL 没有聚合中值函数或 the-like - 甚至没有像 MariaDB 中那样的 window 函数。

使用 window 函数的一种解决方法是:

select date, circle, district, count(*) cnt, sum(kpi_1) sum_kpi1, sum(kpi_2) sum_kpi2,
    avg(case when rn1 in (floor((cnt + 1)/2), floor((cnt + 2)/2)) then kpi1 end) media_kpi1,
    avg(case when rn2 in (floor((cnt + 1)/2), floor((cnt + 2)/2)) then kpi2 end) media_kpi2
from (
    select l.*, 
        row_number() over(partition by date, circle, district order by kpi1) rn1,
        row_number() over(partition by date, circle, district order by kpi2) rn2,
        count(*)     over(partition by date, circle, district) cnt
    from loss
) l
group by date, circle, district

假设您想要按日期、圈子和地区汇总记录,如您的原始查询中所示。如果您想要另一组列,则可以同时更改 group by 子句和 window 函数的 partition