sql无内置mode()函数,需用窗口函数(如row_number()配合count() over)或自连接模拟实现众数计算,注意处理null、多众数及浮点精度问题。

SQL里没有内置的MODE()函数,得自己拼
绝大多数主流SQL引擎(PostgreSQL、MySQL 8+、SQL Server)都不提供标准MODE()聚合函数。Oracle倒是有STATS_MODE(),但用的人少,且不处理多众数场景。想算众数,本质是「找出每组中出现频次最高的值」,得靠窗口函数+分组排序组合实现。
用ROW_NUMBER()和COUNT() OVER找每组最高频次值
核心思路:先按组+值计数,再对每组内按计数降序排序,取排第一的值。注意必须用ROW_NUMBER()而非RANK(),否则遇到并列高频时会漏掉部分结果。
- MySQL 8+ / PostgreSQL / SQL Server 都支持这个写法
- 示例(求每个部门
salary的众数):SELECT dept, mode_salary FROM ( SELECT dept, salary AS mode_salary, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY COUNT(*) DESC) AS rn FROM employees GROUP BY dept, salary ) t WHERE rn = 1; - 如果某组有多个值并列最高频(比如两个值都出现5次),
ROW_NUMBER()只取其中一个;要返回全部众数,得换成RANK()并改WHERE rank = 1
SQLite和旧版MySQL用户得绕开窗口函数
SQLite 3.25+ 支持窗口函数,但老版本不行;MySQL 5.7及更早版本也不支持。这时只能用自连接或子查询模拟「每组最大频次」:
- 先算出每组每个值的出现次数(用
GROUP BY dept, salary) - 再对每组,用
(dept, count)匹配该组最大count(通过子查询或JOIN) - 注意性能:数据量大时,
JOIN可能比窗口函数慢不少,且语句更难读 - 示例(兼容MySQL 5.7):
SELECT e1.dept, e1.salary AS mode_salary FROM employees e1 JOIN ( SELECT dept, MAX(cnt) AS max_cnt FROM ( SELECT dept, salary, COUNT(*) AS cnt FROM employees GROUP BY dept, salary ) t GROUP BY dept ) e2 ON e1.dept = e2.dept JOIN ( SELECT dept, salary, COUNT(*) AS cnt FROM employees GROUP BY dept, salary ) e3 ON e1.dept = e3.dept AND e1.salary = e3.salary WHERE e3.cnt = e2.max_cnt;
NULL值和重复众数容易被忽略
众数计算默认忽略NULL——因为GROUP BY天然过滤NULL(除非显式GROUP BY …, salary IS NULL)。但如果你业务要求把NULL当有效值统计,得提前COALESCE(salary, 'NULL')处理。
- 多众数不是“错误”,而是正常情况;硬要强制单值会丢数据
- 某些BI工具或下游应用假设众数唯一,这时候得提前约定规则(如取字典序最小的)
- 如果字段类型是浮点数或高精度小数,直接
GROUP BY可能因精度问题拆散本应合并的值,建议先ROUND(x, 2)再统计










