【问题标题】:How to count only the working days between two dates?如何只计算两个日期之间的工作日?
【发布时间】:2021-11-02 18:12:56
【问题描述】:

我有一个名为假期的表格,其中显示了员工编号以及假期的开始和结束日期:

id_employe start end
1001 2020-12-24 2021-01-04

我正在寻找的是可视化每个员工的休假天数,但按员工编号、月份、年份和天数将它们分开;不考虑非工作日(周六、周日和节假日)。

我有以下查询,它设法从帖子中省略了周六和周日:

SELECT id_employee, 
       EXTRACT(YEAR FROM t.Date) AS year, 
       EXTRACT(MONTH FROM t.Date) AS month, 
       SUM(WEEKDAY(`Date`) < 5) AS days 
FROM (SELECT v.id_employee, 
             DATE_ADD(v.start, interval s.seq - 1 DAY) AS Date 
FROM vacations v CROSS JOIN seq_1_to_100 s 
WHERE DATE_ADD(v.start, interval s.seq - 1 DAY) <= v.end 
ORDER BY v.id_employee, v.start, s.seq ) t 
GROUP BY id_employee, EXTRACT(YEAR_MONTH FROM t.Date);

我的问题是,我怎么能除了跳过周末,也跳过假期?我想我应该建立另一个表来存储这些假期的日期,但是我的 * 查询 * 如何适应执行比较?

如果我们认为员工 10012020-12-242021-01-04 休假,我们过圣诞节和新年作为假期,我们应该得到以下结果:

id_employee month year days
1001 12 2020 5
1001 1 2021 1

【问题讨论】:

  • 您使用的是哪个 MariaDB 版本?可以运行SELECT version();查看吗?
  • @FanoFN 10.4.11-MariaDB

标签: sql mariadb


【解决方案1】:

在您创建了一个存储假期日期的表之后,您可能可以执行以下操作:

SELECT id_employee, 
       EXTRACT(YEAR FROM t.Date) AS year, 
       EXTRACT(MONTH FROM t.Date) AS month, 
       SUM(CASE WHEN h.holiday_date IS NULL THEN WEEKDAY(`Date`) < 5 END) AS days 
FROM (SELECT v.id_employee, 
             DATE_ADD(v.start, interval s.seq - 1 DAY) AS Date 
FROM vacations v CROSS JOIN seq_1_to_100 s 
WHERE DATE_ADD(v.start, interval s.seq - 1 DAY) <= v.end 
ORDER BY v.id_employee, v.start, s.seq ) t 
LEFT JOIN holidays h ON t.date=h.holiday_date
GROUP BY id_employee, EXTRACT(YEAR_MONTH FROM t.Date);

假设holidays 表结构是这样的:

CREATE TABLE holidays (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
holiday_date DATE,
holiday_description VARCHAR(255));

然后 LEFT JOIN 将其添加到您当前的查询中,并通过添加 CASE 表达式来稍微更改 SUM() 以进行检查。如果左连接中的ON t.date=h.holiday_date 匹配,则有h.holiday_date 字段的结果,否则为NULL,因此只考虑CASE h.holiday_date WHEN IS NULL ..

Demo fiddle

添加此解决方案兼容MariaDB和支持common table expression的MySQL版本:

WITH RECURSIVE cte AS
(SELECT id_employee, start, start lvdt, end FROM vacations 
    UNION ALL
 SELECT id_employee, start, lvdt+INTERVAL 1 DAY, end FROM cte 
   WHERE lvdt+INTERVAL 1 DAY <=end)

SELECT  id_employee,
        YEAR(v.lvdt) AS year, 
        MONTH(v.lvdt) AS month,
        SUM(CASE WHEN h.holiday_date IS NULL THEN WEEKDAY(v.lvdt) < 5 END) AS days
  FROM cte v 
  LEFT JOIN holidays h
     ON v.lvdt=h.holiday_date
  GROUP BY id_employee,
        YEAR(v.lvdt), 
        MONTH(v.lvdt);

【讨论】:

  • 我尝试使用NOT INNOT EXISTS,但我知道我的问题有一个更简单的解决方案。非常感谢!
猜你喜欢
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 2018-08-03
  • 2014-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多