postgresql不支持内置mode()函数,需用group by+count+order by+limit 1求单众数,或用cte+窗口函数获取全部众数,自定义聚合函数需谨慎评估维护成本。

PostgreSQL 没有内置 MODE() 聚合函数
直接写 SELECT MODE(column) FROM table 会报错:ERROR: function mode(numeric) does not exist。PostgreSQL 从 14 版本起仍未原生支持 MODE(),不像 SQL Server 或 Oracle 那样开箱即用。别急着换数据库——用现成聚合 + 窗口函数就能稳稳实现,而且更可控。
用 GROUP BY + ORDER BY COUNT(*) DESC LIMIT 1 最简求众数
适用于单值众数、数据量不大、允许任意一个众数(当多个值并列最高频时只返回其一)的场景。注意它不处理 NULL,默认忽略;若需包含 NULL,得显式用 GROUP BY column IS NULL, column。
示例:查订单表中出现最多的 status
SELECT status FROM orders GROUP BY status ORDER BY COUNT(*) DESC LIMIT 1;
- 简单可靠,兼容所有 PostgreSQL 版本(包括 9.6+)
- 不能区分“无众数”(所有频次相同)或“多众数”场景
- 若要返回全部众数(不止一个),把
LIMIT 1换成HAVING COUNT(*) = (SELECT MAX(cnt) FROM (SELECT COUNT(*) AS cnt FROM orders GROUP BY status) t)
用窗口函数 COUNT() OVER() 精确返回所有众数
当业务要求“必须列出所有最高频值”(比如风控规则里不允许模糊取一个),就得避免 LIMIT 1 的截断逻辑。核心思路是先算频次,再用窗口函数拉平最大值做筛选。
WITH freq AS ( SELECT status, COUNT(*) AS cnt FROM orders GROUP BY status ), max_cnt AS ( SELECT MAX(cnt) AS mcnt FROM freq ) SELECT status FROM freq CROSS JOIN max_cnt WHERE cnt = mcnt;
- 明确返回全部众数,NULL 值需单独
GROUP BY status IS NULL, status处理 - 比嵌套子查询更易读,执行计划通常也更优(尤其配合索引时)
- 如果表很大且只关心前 N 个高频值(非严格众数),用
ORDER BY cnt DESC LIMIT N更快
自定义聚合函数 mode_agg() 一劳永逸?谨慎评估
有人会想到用 CREATE AGGREGATE 写一个真正的 mode_agg()。技术上可行,但实际项目中往往得不偿失:
- 需要额外维护状态转换函数(
sfunc)、合并函数(combinefunc),调试成本高 - 无法天然处理多众数——返回数组还是 JSON?下游应用是否适配?
- PG 15+ 支持
ORDER BY子句的聚合(如STRING_AGG(x ORDER BY y)),但MODE语义仍需手写逻辑 - 除非团队长期高频使用众数且已标准化输出格式,否则推荐优先用 CTE 方案
真要上聚合函数,务必在测试环境验证 NULL、空集、超长字符串等边界情况——这些地方最容易漏掉,上线后才发现结果不对。










