直接用sum() over()计算分组占比需两层窗口:先用partition by分组求和作分母,再与分子(当前行原始值)相除,注意避免整数截断和误加order by导致累计和。

直接用 SUM() OVER() 算分组占比,核心是两层窗口:分组求和 + 全局或同组归一化
别想着先 GROUP BY 再除,那样会丢行;SUM() OVER() 的优势就是保留原始明细行的同时完成聚合计算。分组占比本质是「当前行所属分组的某字段和」除以「参考总量」——这个参考总量可以是整个结果集总和(全局占比),也可以是当前分组内另一个维度的和(嵌套占比)。
SUM(col) OVER(PARTITION BY group_col) 是分母基础,不是最终占比
很多人写完这句就停了,但这只是算出了每行所在分组的 col 总和,还没除。必须在同一行上下文中把分子(通常是当前行的 col 值)和分母(上式结果)放一起做除法:
- 分子永远是原始列值,比如
sales_amount - 分母用
SUM(sales_amount) OVER(PARTITION BY region)表示按region分组求和 - 注意加
1.0或显式CAST避免整数截断:写成sales_amount * 1.0 / SUM(sales_amount) OVER(PARTITION BY region) - 如果想看百分比格式,乘 100 后
ROUND(..., 2)即可,但别在窗口函数里ROUND分母——会影响精度
常见错误:误用 ORDER BY 导致累计和而非分组和
SUM() OVER(PARTITION BY x ORDER BY y) 是累计和(running total),不是分组静态和。只要没加 ORDER BY,SUM() OVER(PARTITION BY x) 才等价于该分组内所有行的固定总和。实操中容易手快多敲一个 ORDER BY,结果发现占比加起来不是 100%,就是因为分母随顺序动态变化了。
例如:
SELECT product, region, sales, sales * 100.0 / SUM(sales) OVER(PARTITION BY region) AS pct_in_region FROM sales_table;
这里去掉 ORDER BY 才对。若真需要「每个区域里按销量排序后的累计占比」,那是另一回事,得另起一层计算。
复杂点在于分母要对齐业务语义:全局、分组、还是条件分组?
占比的“整体”是谁,得看需求。有人要“各产品占全公司销量比”,分母就得是 SUM(sales) OVER()(空括号=全表);有人要“各产品在华东区销量占华东总销量比”,才用 PARTITION BY region;还有人要“各产品在华东区且为新品的销量占比”,就得用 SUM(CASE WHEN is_new = 1 THEN sales ELSE 0 END) OVER(PARTITION BY region)。分母写错,占比就完全失真,而且不容易一眼看出来。











