编程SQL 数据分析师

如何通过 SQL 中的窗口函数和分组实现分层过滤的有效聚合?两种方法有什么区别,开发人员常犯哪些错误?

用 Hintsage AI 助手通过面试

答案。

为了有效地构建分层过滤的聚合(例如,先过滤,然后按组计算总和,最后对整个数据集进行汇总),使用窗口函数(OVER())和嵌套的 GROUP BY。

  • 通过 GROUP BY 的分组 只对选定字段进行聚合;只有通过预先过滤(WHERE)才能排除“外部”行的影响。
  • 窗口函数 允许在特定数据片段上计算聚合,同时对结果集施加过滤(例如,仅在同组的行中)。

示例: 我们找出每个经理的最大订单总额,但仅在状态为 '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 倍。