最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
SQL如何计算每个分组内的中位数?
时间:2026-07-12 09:54:57 编辑:袖梨 来源:一聚教程网
PostgreSQL用PERCENTILE_CONT(0.5)是原生标准解法,需配合GROUP BY(聚合场景)或OVER(PARTITION BY...)(窗口场景),返回连续插值结果(如[1,2,3,4]→2.5);MySQL需ROW_NUMBER()+CTE手动模拟;SQLite仅能子查询O(n²)实现,跨库无真正兼容写法。
PostgreSQL 用 PERCENTILE_CONT 最省事
PostgreSQL 原生支持中位数计算,PERCENTILE_CONT(0.5) 就是标准解法,它在分组内做连续插值,结果是精确的浮点数(哪怕原始数据是整数)。注意必须配合 GROUP BY 和窗口函数语法区分使用场景:
- 聚合场景(每组一个中位数值):直接写在
SELECT里,搭配GROUP BY - 窗口场景(每行显示所在组的中位数):用
OVER (PARTITION BY ...)
示例(聚合):
SELECT category, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY price) AS median_priceFROM productsGROUP BY category;如果
price 全是整数,结果仍可能是小数(比如 [1,2,3,4] → 2.5),这是设计使然,不是 bug。MySQL 8.0+ 得靠 ROW_NUMBER() 手动算
MySQL 没有内置中位数函数,得靠排序后取中间位置。核心思路是:对每组按目标列排序,给每行标序号,再判断总行数奇偶——奇数取第 (n+1)/2 行,偶数取第 n/2 和 n/2+1 行的平均值。
关键陷阱:ROW_NUMBER() 必须在子查询或 CTE 中先完成排序和编号,不能直接在外部 GROUP BY 里嵌套调用;否则会报错或逻辑错乱。
示例(CTE 写法):
WITH ranked AS ( SELECT category, price, ROW_NUMBER() OVER (PARTITION BY category ORDER BY price) AS rn, COUNT(*) OVER (PARTITION BY category) AS cnt FROM products)SELECT category, AVG(price) AS median_priceFROM rankedWHERE rn IN (FLOOR((cnt + 1) / 2), CEIL((cnt + 1) / 2))GROUP BY category;注意这里用
FLOOR 和 CEIL 是为了统一处理奇偶:奇数时两个值相同,偶数时刚好取中间两行。SQLite 只能用子查询模拟,性能差但可行
SQLite 连窗口函数都直到 3.25 才支持,且不支持 ROW_NUMBER()。最稳妥的做法是用相关子查询数“小于等于当前值的个数”,再和总数对比——本质是找满足“= 它的数 ≥ 总数一半”的那个值。
实操难点在于:必须确保排序字段无重复值,否则可能匹配到多个候选值。建议加主键或唯一字段辅助去重:
- 如果
price有重复,改用(price, id)联合排序 - 子查询里
COUNT(*)要和外层同组,必须用相关子查询(带WHERE group_col = outer.group_col) - 这个写法在大数据量下是 O(n²),别在 >10k 行的表上直接跑
简化版(假设 price 唯一):
SELECT DISTINCT p1.category, (SELECT p2.price FROM products p2 WHERE p2.category = p1.category AND (SELECT COUNT(*) FROM products p3 WHERE p3.category = p1.category AND p3.price <= p2.price) >= (SELECT COUNT(*) FROM products p4 WHERE p4.category = p1.category) / 2.0 AND (SELECT COUNT(*) FROM products p3 WHERE p3.category = p1.category AND p3.price >= p2.price) >= (SELECT COUNT(*) FROM products p4 WHERE p4.category = p1.category) / 2.0 LIMIT 1) AS median_priceFROM products p1;
跨数据库兼容写法?基本不存在
不同数据库对中位数的定义和实现差异很大:PERCENTILE_CONT 在 PostgreSQL/Oracle/SQL Server 里行为一致,但在 MySQL 和 SQLite 里压根没这函数;MEDIAN() 函数只存在于 Oracle 旧版本(12c 以前),新版本也推荐用 PERCENTILE_CONT;SQLite 甚至没有 CEIL 函数,得用 CAST(... AS INTEGER) 模拟。
真正需要跨库的项目,要么在应用层算(把分组数据拉到 Python/Java 里用 statistics.median),要么接受“近似中位数”——比如用 AVG 配合 MIN/MAX 估算,但这已经不是严格意义上的中位数了。
实际选型时,优先查清你正在用的数据库版本和函数支持列表,别硬套别人家的写法。尤其是 MySQL 用户,5.x 版本连 ROW_NUMBER() 都没有,只能靠自连接或应用层处理。
相关文章
- 搜书吧最新版官网入口2026_搜书吧2026最新版官网入口地址在线阅读 07-12
- 2026毒蘑菇手机cpu性能测试最新官网入口地址 07-12
- 漫蛙2(台版)官网网页版入口2026_漫蛙manwa2正版在线观看入口2026最新 07-12
- 《背包英雄》可使用道具分享 07-12
- todesk远程控制手机教程 07-12
- 拼课-Java AI 高级全能工程师课程体系 07-12