计算给定日期范围的每个月的平均值

Dan*_*Dan 8 mysql sql group-by date aggregate-functions

我有employees表,其中每个员工都有一个相关的start_date,end_date和一个工资.

注意:在底部,您可以找到导入结构和数据的SQL代码.

+----+-------+------------+------------+---------+
| id | name  | start_date | end_date   | salary  |
+----+-------+------------+------------+---------+
|  1 | Mark  | 2017-05-01 | 2020-01-31 | 2000.00 |
|  2 | Tania | 2018-02-01 | 2019-08-31 | 5000.00 |
|  3 | Leo   | 2018-02-01 | 2018-09-30 | 3000.00 |
|  4 | Elsa  | 2018-12-01 | 2020-05-31 | 4000.00 |
+----+-------+------------+------------+---------+
Run Code Online (Sandbox Code Playgroud)

问题

对于给定的日期范围,我想提取给定日期范围内每个月的工资平均值.

更新:我想拥有MySQL 5.6的解决方案,但是拥有MySQL 8+的解决方案(仅用于个人知识)会很棒.

如果日期范围是2018-08-01 - 2019-01-31,则SQL语句应从2018年8月到2019年1月循环,并且必须计算每个月的平均工资:

  • 20188月,活跃的员工是Mark,Tania,Leo(因为2018年8月是他们的start_dateend_date之间)所以平均值是3333.33
  • 20189月,活跃的员工是Mark,Tania,Leo(因为2018年9月是他们的start_dateend_date之间)所以平均值是3333.33
  • 201810月,活跃的员工是Mark,Tania所以平均是3500.00
  • 201811月,活跃的员工是马克,塔尼亚所以平均是3500.00
  • 201812月,活跃的员工是Mark,Tania,Elsa所以平均是3666.6667
  • 2019年1月,活跃的员工是Mark,Tania,Elsa所以平均是3666.6667

您可以看到日期范围2018-08-01 - 2019-01-31的预期结果

+------+-------+------------+
| year | month | avg_salary |
+------+-------+------------+
| 2018 | 08    | 3333.33    |
| 2018 | 09    | 3333.33    |
| 2018 | 10    | 3500.00    |
| 2018 | 11    | 3500.00    |
| 2018 | 12    | 3666.67    |
| 2019 | 01    | 3666.67    |
+------+-------+------------+
Run Code Online (Sandbox Code Playgroud)

注意:我解决了将MySQL与PHP代码混合的问题,但对于大日期范围,它必须执行太多查询(每月一个).所以我想只使用MySQL的解决方案.

SQL导入结构和数据

CREATE TABLE `employees` (
  `id` int(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `start_date` date NOT NULL,
  `end_date` date NOT NULL,
  `salary` decimal(10,2) DEFAULT NULL
);

INSERT INTO `employees` (`id`, `name`, `start_date`, `end_date`, `salary`) VALUES
(1, 'Mark', '2017-05-01', '2020-01-31', '2000.00'),
(2, 'Tania', '2018-02-01', '2019-08-31', '5000.00'),
(3, 'Leo', '2018-02-01', '2018-09-30', '3000.00'),
(4, 'Elsa', '2018-12-01', '2020-05-31', '4000.00');
Run Code Online (Sandbox Code Playgroud)

Sal*_*n A 2

您可以简单地输入所需的月份(或使用 PHP 代码生成它们)并加入它:

SELECT ym, AVG(salary)
FROM (
    SELECT '2018-08-01' + INTERVAL 0 MONTH AS ym UNION ALL
    SELECT '2018-08-01' + INTERVAL 1 MONTH UNION ALL
    SELECT '2018-08-01' + INTERVAL 2 MONTH UNION ALL
    SELECT '2018-08-01' + INTERVAL 3 MONTH UNION ALL
    SELECT '2018-08-01' + INTERVAL 4 MONTH UNION ALL
    SELECT '2018-08-01' + INTERVAL 5 MONTH
) AS yearmonths
JOIN employees ON ym BETWEEN start_date AND end_date
GROUP BY ym
Run Code Online (Sandbox Code Playgroud)

如果您有一个包含数字 0、1、... 的表,那么您可以使用它。您甚至可以使用任何具有足够行数的表:

SELECT ym, AVG(salary)
FROM (
    SELECT '2018-08-01' + INTERVAL @n := @n + 1 MONTH AS ym
    FROM anytable, (SELECT @n := -1) x
    LIMIT 100
) AS yearmonths
JOIN employees ON ym BETWEEN start_date AND end_date
WHERE ym <= '2019-01-01'
GROUP BY ym
Run Code Online (Sandbox Code Playgroud)