SQL无内置众数函数,须用窗口函数:先COUNT() OVER(PARTITION BY group_col, value_col)计频次,再ROW_NUMBER() OVER(PARTITION BY group_col ORDER BY cnt DESC, value_col)排序取rn=1;MySQL 8.0+、PostgreSQL、SQL Server均适用,旧版需嵌套子查询。

SQL里没有内置众数函数,得自己拼
MySQL、PostgreSQL、SQLite 这些主流数据库都不提供 MODE() 这类聚合函数。想算每个分组的众数(出现次数最多的值),必须靠分组计数 + 排序 + 取首行。核心思路是:先按分组和值双重分组统计频次,再对每个分组内按频次降序排,取第一条。
用GROUP BY + COUNT +窗口函数更稳(推荐)
直接 GROUP BY group_col, value_col 再 ORDER BY COUNT(*) DESC LIMIT 1 看似简单,但 MySQL 8.0 前不支持在子查询中对同一层 GROUP BY 使用 LIMIT;PostgreSQL 要求子查询带 GROUP BY 才能用窗口函数;纯靠嵌套容易出错。更可靠的做法是用窗口函数:
SELECT group_col, value_col
FROM (
SELECT group_col, value_col,
COUNT(*) AS cnt,
ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY COUNT(*) DESC, value_col) AS rn
FROM t
GROUP BY group_col, value_col
) ranked
WHERE rn = 1;
-
ROW_NUMBER()确保每组只取一个结果,即使有多个值并列最高频次 - 加
value_col到ORDER BY是为了结果可重现(避免每次运行返回不同众数) - 若要保留所有并列众数,改用
RANK()或DENSE_RANK(),再把条件换成rn = 1→rank = 1
MySQL 5.7 或旧版 SQLite 怎么办?只能嵌套子查询
这些版本不支持窗口函数,只能靠两层子查询硬套。注意:外层必须对 group_col 做主分组,内层再按 group_col + value_col 分组计数:
SELECT group_col,
(SELECT value_col
FROM t t2
WHERE t2.group_col = t1.group_col
GROUP BY value_col
ORDER BY COUNT(*) DESC, value_col
LIMIT 1) AS mode_val
FROM (SELECT DISTINCT group_col FROM t) t1;
- 外层
t1提供所有分组键,避免漏组 - 内层相关子查询中,
GROUP BY value_col是关键,漏掉就变成全表统计 -
LIMIT 1在 MySQL 5.7 是允许的,但在某些旧 SQLite 版本中需确保开启enable_load_extension或改用临时表 - 性能差:每组执行一次子查询,数据量大时明显变慢
NULL 和重复值会干扰结果,得提前处理
众数计算默认把 NULL 当作一个合法值参与计数。如果业务上不希望 NULL 成为众数,必须显式过滤:
- 在
GROUP BY前加WHERE value_col IS NOT NULL - 若字段含空字符串
'',也建议统一转成NULL或剔除,避免和真实值混淆 - 某些场景下,相同
value_col因大小写或空格差异被当成不同值(如'A 'vs'a'),需提前用TRIM(UPPER(value_col))标准化
真正麻烦的是“多个值并列最高频次”——这时众数不唯一,但多数业务系统只接受单值输出。不明确说明处理策略(取最小/最大/任意一个),后续排查数据异常会很被动。

















