sql无内置mode()函数,group by+max(count())会报错;须用窗口函数:先count() over分组统计频次,再rank()或row_number()按频次降序排名,最后取rn=1;rank()可返回全部并列众数,row_number()仅返回一个。

SQL 没有内置的 MODE() 聚合函数,直接用 GROUP BY + MAX(COUNT()) 会报错 —— 因为聚合函数不能嵌套使用。
用窗口函数配合子查询取每组频次最高的值
核心思路是:先按分组和值统计频次,再用 ROW_NUMBER() 或 RANK() 标出每组内频次排名,最后筛选出排名第一的记录。
注意:ROW_NUMBER() 在频次相同时会任意排序(可能漏掉多个众数),RANK() 更稳妥(并列都算第一)。
- MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持
- 需两层嵌套:外层过滤
rn = 1,内层计算频次和排名 - 若某组所有值出现次数相同,
RANK()会返回全部,ROW_NUMBER()只返回一个(不确定是哪个)
SELECT group_col, value_col
FROM (
SELECT group_col, value_col,
RANK() OVER (PARTITION BY group_col ORDER BY COUNT(*) DESC) AS rn
FROM t
GROUP BY group_col, value_col
) ranked
WHERE rn = 1;
SQLite 和旧版 MySQL(无窗口函数)的替代写法
只能靠相关子查询或自连接模拟“每组最大频次”,性能较差,且语法更绕。
关键限制:无法直接在 HAVING 中引用聚合结果的别名;必须重复 COUNT(*) 表达式或用子查询先算出各组最大频次。
- SQLite 示例(用相关子查询):
SELECT t1.group_col, t1.value_col FROM t t1 WHERE NOT EXISTS ( SELECT 1 FROM t t2 WHERE t2.group_col = t1.group_col GROUP BY t2.value_col HAVING COUNT(*) > COUNT(t1.value_col) );
- 更可靠但低效的做法:先用子查询算出每组最大频次,再关联原表匹配
- 这种写法在大数据量下容易慢,且对 NULL 值处理需额外
WHERE value_col IS NOT NULL
遇到 NULL 值或多个众数时的行为差异
COUNT(*) 统计行数,包含 NULL;COUNT(value_col) 忽略 NULL。多数场景应显式排除 NULL,否则它可能意外成为众数。
- PostgreSQL 中
NULL = NULL为UNKNOWN,所以GROUP BY value_col会把所有NULL归为同一组 - 如果业务上
NULL代表“未知”,通常不应参与众数计算 —— 加WHERE value_col IS NOT NULL - 多个众数(如 A 和 B 各出现 5 次,其他都 ≤4):用
RANK()可全返回;用LIMIT 1或ROW_NUMBER()则只取其一,且结果不可控
真正麻烦的不是写法本身,而是众数定义在 SQL 里天然模糊:是否允许并列?NULL 算不算有效值?不同数据库对空值分组和排序的默认行为也不一致 —— 动手前得先和业务方对齐这些边界。











