帕累托分布是描述“少数关键因素贡献大部分结果”的幂律分布,核心为80/20法则:按指标降序排列后,累计占比首次≥80%的分组即属关键20%,须用sum() over(order by ... desc)计算动态累计和与总体和之比,而非简单取前20%行数。

什么是帕累托分布?先看关键判断
SQL里没有现成的 PARETO 函数,所谓“80/20法则”本质是:按某指标降序排列后,累计占比首次 ≥ 80% 的分组,就划入“关键的20%”。核心不是硬凑20%行数,而是看累计和占总体的比例——这个比例必须用窗口函数动态算,不能靠 GROUP BY 后再加 SUM() 模拟。
用 SUM() OVER() 算累计和,别用 COUNT(*) 累加行数
常见错误是把“前N行”当成帕累托,比如 ROW_NUMBER() 排序后取前20%,这完全偏离了80/20本意——它关注的是贡献值的集中度,不是记录数量。正确路径是:先聚合出各分组的指标值(如销售额),再按该值降序排序,最后用窗口函数求累计和与总体和的比值。
- 先做分组聚合:
SELECT category, SUM(amount) AS total_amount FROM sales GROUP BY category - 再套一层,加窗口函数:
SUM(total_amount) OVER (ORDER BY total_amount DESC)得累计和 - 总体和用
SUM(total_amount) OVER ()(空括号=全集) - 累计占比 =
累计和 * 1.0 / 总体和(乘1.0防整除截断)
怎么标出“帕累托前沿”?用布尔表达式直接标记
不需要额外查表或二次JOIN,可以在同一查询中用条件表达式判断是否进入前80%。多数数据库支持 CASE WHEN 或布尔转整型(如 PostgreSQL 的 ::int,MySQL 的 IF()),但最通用写法是直接用比较生成标志位。
CASE WHEN cumsum * 1.0 / total_sum >= 0.8 THEN 1 ELSE 0 END AS is_pareto- 注意:这个标志位是“首次达到或超过80%及之后所有行”都为1,符合帕累托定义(即“头部贡献者集合”)
- 如果只要首个突破点,可加
ROW_NUMBER() OVER (ORDER BY cumsum)再过滤=1,但通常不需要 - 排序必须用
ORDER BY total_amount DESC,升序会把小贡献者堆在前面,累计占比永远上不去
兼容性坑:MySQL 5.7 不支持窗口函数,得换思路
MySQL 5.7 及更早版本不识别 OVER(),强行运行会报错 ERROR 1064: You have an error in your SQL syntax。此时不能硬套语法,必须降级处理:
- 方案一:用自连接模拟累计和(性能差,数据量 >1k 行就明显卡顿)
- 方案二:导出到临时表 + 增量变量赋值(MySQL 8.0+ 支持变量,但行为不稳定,不推荐)
- 方案三:最稳妥——改用应用层计算(Python/Pandas 或 BI 工具),SQL只负责聚合原始分组数据
- 确认版本:执行
SELECT VERSION();,低于8.0.2基本可判定不支持标准窗口函数
窗口函数的帕累托计算看似简单,真正卡住人的往往不是逻辑,而是没意识到累计占比必须基于排序后的值累加、且总体和必须来自同一聚合层级——漏掉任何一个,结果就变成“伪80/20”。










