题目描述

✅ 1126. 查询活跃业务

题意分析

事件表记录每个业务在某种事件上的发生次数。一个业务只有在某事件上的次数严格超过该事件类型的平均次数,才算在这一类事件上达标;至少在两种不同事件类型上达标的业务,才是要返回的活跃业务。

比较门槛按事件类型分别计算,不能把所有类型混在一起求平均。等于平均值不算超过。表中同一个业务与同一种事件类型只有一行,因此达标行数可以对应达标类型数;最终只返回业务编号。

解法:SQL 查询建模

核心思路

[!blue]

第一层先按 event_type 分组,对表中这一类型的全部 occurrences 求 AVG,得到每种事件自己的门槛。这个平均值包含当前业务的原记录,不是删掉待判断业务后重新计算,也不能先过滤低次数记录再求平均。

将平均值结果按 event_type 等值连接回原表。聚合结果对每种类型只有一行,所以每条原记录恰好匹配到一个属于自己的平均值,不会与其他类型的门槛混合。

接着在 WHERE 中保留 e.occurrences > a.avg_occurrences 的明细行。过滤后,每一行就表示某个业务在一种事件上达标;不达标的事件行不应参与后面的种类计数。

再按 business_id 分组,使用 HAVING COUNT(*) > 1 保留至少两类达标事件的业务。由于原表的业务与类型组合唯一,连接又没有放大行数,这里的 COUNT(*) 就是达标事件种类数,不需要额外 DISTINCT。

两次分组解决不同问题:先按事件类型计算比较标准,再按业务汇总满足了多少种标准。明细是否达标由 WHERE 判断,整个业务是否达到两种由 HAVING 判断,两层条件不能互相替代。

解题步骤

  1. 按事件类型分组计算平均发生次数。
  2. 通过相同事件类型将平均值连接回每条业务事件记录。
  3. 筛选发生次数严格超过对应平均值的明细。
  4. 按业务编号分组,仅保留达标记录数大于一的业务。

代码实现

SELECT e.business_id
FROM Events e
JOIN (
    -- 每种事件单独计算平均值,作为该类明细的门槛。
    SELECT event_type, AVG(occurrences) AS avg_occurrences
    FROM Events
    GROUP BY event_type
) a ON a.event_type = e.event_type
-- 先筛出超过对应事件平均值的业务事件记录。
WHERE e.occurrences > a.avg_occurrences
GROUP BY e.business_id
-- 同一业务、同一类型唯一,行数就是达标类型数。
HAVING COUNT(*) > 1;

复杂度分析

  • 时间复杂度:执行代价取决于数据库的聚合与连接计划。使用哈希聚合和哈希连接时,处理 n 条记录的期望开销为 $O(n)$;若需要排序,需另计排序成本。
  • 空间复杂度:哈希状态约为 $O(t + b)$,t 为事件类型数,b 为业务数;还需考虑执行器可能产生的中间结果。

关键点总结

[!green]

  • 事件类型决定平均门槛,业务编号决定最终汇总对象,两个粒度不同。
  • 先筛达标明细,再统计每个业务的达标类型数。
  • 组合唯一性和正确连接共同保证 COUNT(*) 能代表事件种类数。

易错点总结

[!yellow]

  • 使用全表平均值,会让不同事件类型共用错误门槛。
  • 次数比较使用大于等于,会把恰好等于平均值的记录也当成达标。
  • 只要求一类达标,不能满足至少两种事件类型的条件。
  • 连接时遗漏事件类型条件,会让明细匹配到其他类型的平均值,并产生重复记录。
  • 先向上取整平均值,可能让原本严格高于平均值的整数次数恰好等于新门槛,从而被错误排除。
  • 在汇总业务前没有过滤不达标明细,会统计全部事件类型,而非超过平均值的类型。

相似题目

题目 难度 关联与区别
184. 部门工资最高的员工 中等 同样先按类别计算组内指标再与每条记录比较,原题指标是部门最高工资,本题是事件类型平均次数。
570. 至少有5名直接下属的经理 中等 同样分组后按数量筛选,本题先标记超过类型均值的事件,再统计业务的合格事件种数。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2024/63225408
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!