PostgreSQL用WITHIN GROUP+GROUP BY直接聚合,性能优;SQL Server必须用OVER窗口函数+DISTINCT去重,不支持聚合语法,且每次调用独立排序。

PostgreSQL 和 SQL Server 都原生支持 PERCENTILE_CONT 和 PERCENTILE_DISC,但用法、性能和陷阱差异极大——直接照搬语法大概率报错或结果错乱。
PostgreSQL 中按组算中位数:用 WITHIN GROUP 聚合最稳
PostgreSQL 允许把 PERCENTILE_CONT(0.5) 当作聚合函数用,配合 WITHIN GROUP (ORDER BY ...),可直接搭配 GROUP BY:
SELECT department,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary
FROM employees
GROUP BY department;这种写法单次扫描、一次排序,性能优于窗口函数 + 去重组合。注意以下几点:
-
WITHIN GROUP必须显式写,漏掉会报错:ERROR: window function call requires an OVER clause - 排序字段(如
salary)含NULL时会被自动忽略;若想保留NULL参与排序,得提前用COALESCE(salary, -1e9)等方式归一化 - 不能在同一个
SELECT中混用非聚合列和该函数,除非所有非聚合列都在GROUP BY中 - 大数据量时,
salary字段上建 B-tree 索引能显著加速排序阶段
SQL Server 中必须用 OVER 窗口语法,GROUP BY 会冲突
SQL Server 不支持 WITHIN GROUP 形式的聚合调用,PERCENTILE_CONT 只能作为窗口函数使用。下面这种写法是错的:
SELECT department, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) FROM employees GROUP BY department; -- 报错:'PERCENTILE_CONT' is not a recognized built-in function name
正确做法是用 OVER (PARTITION BY ... ORDER BY ...),再套一层 DISTINCT 或 GROUP BY 外层聚合:
SELECT DISTINCT
department,
PERCENTILE_CONT(0.5) OVER (PARTITION BY department ORDER BY salary) AS median_salary
FROM employees;关键限制:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
-
ORDER BY后只能跟一个列,不能是表达式(如ORDER BY salary * 1.2会报错) - 如果某
department分组只有 1 行,结果就是该行值;如果全为NULL,返回NULL(不报错但易被忽略) - 结果集行数不变——原始表 1000 行,输出仍是 1000 行,每行带所在分组的中位数;若只要一行/组,必须用
DISTINCT或 CTE 去重
跨数据库兼容性差,别硬套同一套 SQL
MySQL 8.0+ 和 SQLite 完全不支持 PERCENTILE_CONT 聚合形式,MySQL 窗口版仅限 OVER 写法且不支持 WITHIN GROUP。强行迁移会遇到:
- MySQL 报错:
FUNCTION PERCENTILE_CONT does not exist - SQLite 报错:
no such function: PERCENTILE_CONT - 即使 PostgreSQL 和 SQL Server 都支持,参数含义也不完全一致:SQL Server 的
PERCENTILE_CONT(0.5)在空分组时可能返回0(取决于版本),而 PostgreSQL 明确返回NULL
如果项目需多数据库支持,建议封装逻辑层处理,而非依赖 SQL 内置函数。
计算多个百分位时,generate_series 在 PostgreSQL 更高效
要一次性查 1%~100% 所有百分位(比如生成分布直方图),PostgreSQL 可用 generate_series 配合聚合,避免 N 次重复扫描:
SELECT k AS percentile,
PERCENTILE_DISC(k) WITHIN GROUP (ORDER BY amount) AS value
FROM transactions, generate_series(0.01, 1.0, 0.01) AS k
GROUP BY k
ORDER BY k;SQL Server 没有等价的 generate_series,只能靠临时表或递归 CTE 模拟,性能明显下降。更严重的是:每次调用 PERCENTILE_CONT 都触发独立排序,100 个百分位 = 100 次全表排序 —— 这种写法在千万级表上基本不可行。
真正容易被忽略的点是:PostgreSQL 的 PERCENTILE_CONT 和 PERCENTILE_DISC 在内部实现上共享排序缓存(9.6+),但 SQL Server 的每个 OVER 子句都独立执行排序,无法复用。所以“看起来一样”的语句,在两个系统里资源消耗可能差一个数量级。

















