sql无内置众数函数,须用窗口函数:先count() over分组计频次,再row_number()或rank()按频次降序排序,最后取rn=1(row_number仅返一个,rank可返全部并列众数)。

SQL里没有内置众数函数,得靠GROUP BY + COUNT + 排序模拟
标准SQL不提供MODE()这类聚合函数,PostgreSQL 14+虽加了mode(),但MySQL、SQL Server、SQLite等主流引擎仍需手动实现。核心思路是:先按值分组计数,再对每个分组内的频次降序取第一个——但要注意“多个值并列最高频次”时的处理逻辑。
用窗口函数避免子查询嵌套(推荐MySQL 8.0+/PostgreSQL)
直接在分组后加ROW_NUMBER()或RANK(),比多层子查询更清晰、易调试。关键区别在于:ROW_NUMBER()强制唯一排序(相同频次也强行编号),RANK()会跳号(并列第一则下一个是第三名),选哪个取决于你是否接受多个众数。
- 要返回所有最高频值(允许多个众数):用
RANK() OVER (PARTITION BY group_col ORDER BY COUNT(*) DESC) - 只要一个代表值(哪怕有并列):用
ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY COUNT(*) DESC, value_col),末尾加value_col保证排序稳定 - MySQL 5.7及更早不支持窗口函数,必须用自连接或变量模拟,性能差且易出错
兼容旧版MySQL的写法:用LEFT JOIN筛出最大频次
本质是把“找每组最大COUNT”转为“找不存在更大COUNT的记录”。常见错误是只写GROUP BY没配HAVING或关联条件,导致结果为空或错乱。
- 先算出各组各值的频次:
SELECT group_col, value_col, COUNT(*) AS cnt FROM t GROUP BY group_col, value_col - 再LEFT JOIN自身,匹配同组但频次更高的记录:
ON t1.group_col = t2.group_col AND t1.cnt - 最后
WHERE t2.cnt IS NULL,即没找到更高频次的行——就是众数 - 注意:若某组所有
value_col频次全为1,此法会返回全部值;需额外加AND t1.cnt > 1过滤才符合“众数需重复出现”的语义
ORDER BY TOP 1在不同数据库的写法差异很大
TOP 1是SQL Server语法,MySQL用LIMIT 1,PostgreSQL用LIMIT 1或FIRST_VALUE()窗口函数。直接套用会报错,而且单纯LIMIT 1无法解决“多众数”问题——它只随机返回一个,不保证可重现。
- SQL Server:
SELECT TOP 1 value_col FROM (...) ORDER BY cnt DESC - MySQL:
SELECT value_col FROM (...) ORDER BY cnt DESC LIMIT 1 - PostgreSQL:
SELECT FIRST_VALUE(value_col) OVER (ORDER BY cnt DESC, value_col) FROM (...) - 真正可靠的众数提取,必须结合窗口函数或自连接,不能只靠
LIMIT/TOP压单条
最常被忽略的是NULL值处理——COUNT(*)会忽略NULL,但COUNT(value_col)也会忽略,而分组时NULL会被单独成组。如果业务上NULL不算有效取值,记得加WHERE value_col IS NOT NULL预过滤。










