订阅更新

部门平均工资和公司平均工资比较

SQL 实战 123 题 · 第 41 题 困难 约 3 分钟读完

LeetCode 615 · 多粒度聚合、窗口 AVG、CASE 比较

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

这是两种粒度的聚合比较。我会分别算「月份」的公司平均和「月份 + 部门」的部门平均,可以用两个窗口 AVG,也可以两个 GROUP BY 子查询按月份关联,然后 CASE WHEN 输出 higher / lower / same。要注意公司平均是对所有工资记录求平均,不是对部门平均再求平均。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

工资表记录每个员工每次发薪的金额和日期,员工表记录员工所属部门。对每个发薪月份、每个部门,比较部门平均工资和全公司平均工资,输出 higher / lower / same。

表结构

  • salary:id(主键)、employee_id、amount、pay_date
  • employee:employee_id(主键)、department_id

思路

需要两种粒度的平均值:「月份」粒度的公司平均,和「月份 + 部门」粒度的部门平均。用两个窗口 AVG 分别按不同的分区计算,挂在同一行上,再用 CASE 比较。最后每个(月份, 部门)只保留一行。

参考解法

select 
    pay_month,
    department_id,
     (case when avgs>ts then 'higher'
            when avgs<ts then 'lower'
            else 'same' end) as comparison
from 
(
    select 
        date_format(pay_date,'%Y-%m')pay_month,
        department_id,
        avg(amount) over(partition by date_format(pay_date,'%Y-%m') )ts,
        avg(amount) over(partition by date_format(pay_date,'%Y-%m'),department_id) avgs
    from salary s
    left join employee e
    on s.employee_id = e.employee_id
   
)t1
 group by pay_month, department_id

另一种写法:先分别聚合再关联,不依赖窗口函数

SELECT d.pay_month, d.department_id,
       CASE WHEN d.avg_amt > c.avg_amt THEN 'higher'
            WHEN d.avg_amt < c.avg_amt THEN 'lower' ELSE 'same' END AS comparison
FROM (
  SELECT DATE_FORMAT(s.pay_date, '%Y-%m') AS pay_month, e.department_id, AVG(s.amount) AS avg_amt
  FROM salary s JOIN employee e ON s.employee_id = e.employee_id
  GROUP BY pay_month, e.department_id
) d
JOIN (
  SELECT DATE_FORMAT(pay_date, '%Y-%m') AS pay_month, AVG(amount) AS avg_amt
  FROM salary GROUP BY pay_month
) c ON d.pay_month = c.pay_month

易错点

  • 原解法用 GROUP BY 去重但 SELECT 了非分组列,在 ONLY_FULL_GROUP_BY 模式下会报错,改成 SELECT DISTINCT 更规范。
  • 「公司平均」要按工资记录平均,而不是先算部门平均再平均(部门人数不同时两者不等)。
  • 月份要统一格式,比如 DATE_FORMAT(pay_date, '%Y-%m')。
刷题进度保存在当前浏览器