如何只计算两个日期之间的工作日?

How to count only the working days between two dates?

我有以下 table 称为 假期,其中显示员工编号及其假期的开始和结束日期:

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);

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

如果我们考虑员工 10012020-12-24 休假到 2021-01 -04 并且我们把圣诞节和新年作为假期,我们应该得到以下结果:

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

在你创建了一个存储假期日期的 table 之后,你可能可以这样做:

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 table 结构是这样的:

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

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

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);