为了有效地构建分层过滤的聚合(例如,先过滤,然后按组计算总和,最后对整个数据集进行汇总),使用窗口函数(OVER())和嵌套的 GROUP BY。
示例: 我们找出每个经理的最大订单总额,但仅在状态为 'paid' 的订单中,然后输出在所有订单中绝对最大值的经理姓名。
WITH PaidOrders AS ( SELECT ManagerID, SUM(OrderAmount) AS TotalPaid FROM Orders WHERE Status = 'paid' GROUP BY ManagerID ), WithMax AS ( SELECT *, MAX(TotalPaid) OVER() AS MaxTotalPaid FROM PaidOrders ) SELECT ManagerID, TotalPaid FROM WithMax WHERE TotalPaid = MaxTotalPaid;
这一方法(CTE + 窗口函数)能够实现多层次的过滤和聚合。
陷阱: "在分组(GROUP BY)之前应用 WHERE 过滤与在之后应用 HAVING 的区别是什么?这在报告中常常会导致什么?"
答案: WHERE 在分组之前就丢弃了行,因此提供了严格的输入集。HAVING 对聚合组进行过滤——因此如果过滤条件逻辑不一致,可能暂时“保留”多余的行。错误的过滤位置常常导致最终聚合错误或报告结果不准确。
-- 通过 WHERE 获取仅 'paid' 的总和 SELECT ManagerID, SUM(OrderAmount) FROM Orders WHERE Status = 'paid' GROUP BY ManagerID; -- 或者对所有进行求和,然后用 HAVING 切掉 SELECT ManagerID, SUM(OrderAmount) FROM Orders GROUP BY ManagerID HAVING SUM(OrderAmount) > 1000;
故事
项目: 销售报告,审计检查。
错误: 开发人员使用了 HAVING Status='paid' 而不是 WHERE,聚合错误地包括了未付款的订单,从而不正确地计算了员工的年度 KPI。
故事
项目: 银行分析。
错误: 在复杂聚合上试图应用没有 PARTITION BY 的窗口函数,导致聚合在整个表中计算,而不是在组内。部门预算计算不正确——必须手动恢复。
故事
项目: 网上商店,订单统计。
错误: 在优化时未考虑将窗口函数嵌套在子查询中——因此服务器进行了多次数据处理,查询的速度比简单的双重 GROUP BY 慢了 20 倍。