LeetCode 1126. 查询活跃业务
题目描述
题意分析
事件表记录每个业务在某种事件上的发生次数。一个业务只有在某事件上的次数严格超过该事件类型的平均次数,才算在这一类事件上达标;至少在两种不同事件类型上达标的业务,才是要返回的活跃业务。
比较门槛按事件类型分别计算,不能把所有类型混在一起求平均。等于平均值不算超过。表中同一个业务与同一种事件类型只有一行,因此达标行数可以对应达标类型数;最终只返回业务编号。
解法:SQL 查询建模
核心思路
[!blue]
第一层先按
event_type分组,对表中这一类型的全部occurrences求AVG,得到每种事件自己的门槛。这个平均值包含当前业务的原记录,不是删掉待判断业务后重新计算,也不能先过滤低次数记录再求平均。将平均值结果按
event_type等值连接回原表。聚合结果对每种类型只有一行,所以每条原记录恰好匹配到一个属于自己的平均值,不会与其他类型的门槛混合。接着在
WHERE中保留e.occurrences > a.avg_occurrences的明细行。过滤后,每一行就表示某个业务在一种事件上达标;不达标的事件行不应参与后面的种类计数。再按
business_id分组,使用HAVING COUNT(*) > 1保留至少两类达标事件的业务。由于原表的业务与类型组合唯一,连接又没有放大行数,这里的COUNT(*)就是达标事件种类数,不需要额外DISTINCT。两次分组解决不同问题:先按事件类型计算比较标准,再按业务汇总满足了多少种标准。明细是否达标由
WHERE判断,整个业务是否达到两种由HAVING判断,两层条件不能互相替代。
解题步骤
- 按事件类型分组计算平均发生次数。
- 通过相同事件类型将平均值连接回每条业务事件记录。
- 筛选发生次数严格超过对应平均值的明细。
- 按业务编号分组,仅保留达标记录数大于一的业务。
代码实现
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名直接下属的经理 | 中等 | 同样分组后按数量筛选,本题先标记超过类型均值的事件,再统计业务的合格事件种数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!