窗口函数不能直接生成多级小计行,必须配合GROUP BY先聚合再计算;ROLLUP可生成金字塔结构汇总行,而窗口函数仅追加列不增行;GROUPING()是判断汇总行的唯一可靠方式。

窗口函数不能直接生成多级小计行,必须和 GROUP BY 配合使用——先聚合出层级结果,再用窗口函数补计算列。
为什么不能直接用 SUM() OVER 替代 GROUP BY ... WITH ROLLUP
常见错误是写 SUM(sales) OVER (PARTITION BY region) 想替代区域小计,结果只是每行多了一列“区域总和”,但没产生新行(比如城市小计、全省总计)。ROLLUP 会压缩行数并插入汇总行;窗口函数不改变原始行数,只追加计算值。
- 输出形态不同:
ROLLUP输出的是金字塔结构(明细 + 小计 + 总计),窗口函数输出的是原表行数不变的宽表 -
PARTITION BY中字段为NULL时会自成一个分区,若原始数据有region IS NULL,这部分会被单独统计,常导致总数对不上 - 想算“每个门店占所在城市的销售占比”,必须先
GROUP BY region, city, store得到聚合结果,再套SUM(sales) OVER (PARTITION BY region, city)做分母
GROUP BY 多字段顺序直接影响 ROLLUP 层级路径
SQL Server 的 WITH ROLLUP 严格按 GROUP BY 列顺序从左到右逐级上卷。比如 GROUP BY dept, team, emp WITH ROLLUP 产出四层:(dept,team,emp) → (dept,team,NULL) → (dept,NULL,NULL) → (NULL,NULL,NULL)。
- 顺序错就丢维度:把
emp放最前,ROLLUP就不会生成team小计 - 业务层级要由粗到细:时间维度按
year, quarter, month排;组织维度按region, city, store排 -
GROUPING()是唯一可靠判断方式:GROUPING(city) = 1表示该行是城市级汇总(city列值为NULL,但不是空数据)
递归 CTE + 窗口函数处理动态树形分类
当分类是无限级类目树(如电子 > 手机 > 苹果 > iPhone 15),ROLLUP 失效,必须用递归 CTE 先摊平结构,再关联业务数据聚合,最后用窗口函数算占比或排名。
- 锚点必须只查根节点:
WHERE parent_id IS NULL,否则递归可能死循环 - 递归成员连接条件必须引用 CTE 别名:
JOIN category_tree ct ON c.parent_id = ct.id,写成c.parent_id = c.id会出错 - 聚合只能放外层:
SELECT ..., SUM(sales) OVER (PARTITION BY root_id)必须在递归 CTE 的FROM子句之后,CTE 内部不能写GROUP BY - 性能关键点:在业务表的外键字段(如
category_id)建索引,避免 JOIN 时全表扫描
真正容易被忽略的是 GROUPING() 和 IS NULL 的语义差异——所有汇总行的 NULL 都需用 GROUPING(col) = 1 判断,而不是简单 WHERE col IS NOT NULL 过滤,否则小计行直接消失。

















