【问题标题】:How to summarize leave to without public holidays?如何总结没有公共假期的假期?
【发布时间】:2019-04-19 06:18:41
【问题描述】:

大家好(节前)。

在这种情况下,我在此表中添加了新的休假:

+--------+---------+---------+-------------+----------+--------------------------
|ID_LEAVE|ID_WORKER| FNAME   | LNAME | BEGIN_DATE         | END_DATE            | 
+--------+---------+---------+---------+-------------+--------------------+------
| 8      |   10    | MARIO   | NEED  |2019-04-22 07:00:00 |2019-04-23 15:00:00  | 
+--------+---------+---------+-------------+----------+-------------------------- 

但我知道假期是这样表示的:

我还做了什么?

我已经完成了该查询,该查询汇总了按ID_LEAVE 分组的所有员工的休假时间

SELECT leave.ID_LEAVE, leave.ID_WORKER, workers.FNAME, workers.LNAME, leave.BEGIN_DATE, leave.END_DATE,  
FROM 
(SELECT ADDDATE('1970-01-01', t4 * 10000 + t3 * 1000 + t2 * 100 + t1 * 10 + t0) AS date_value 
 FROM 
(SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0, 
 (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1, 
 (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2, 
 (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3, 
 (SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4) calendar 
INNER JOIN leave ON calendar.date_value BETWEEN DATE(leave.BEGIN_DATE) AND DATE(leave.END_DATE) 
INNER JOIN workers ON leave.ID_WORKER = workers.ID_WORKER 
WHERE NOT WEEKDAY(date_value) IN (5, 6)
GROUP BY ID_LEAVE;

现在我想要一个汇总所有员工休假时间的查询,但我在上图中表示没有假期。我应该怎么做?我应该创建新的“假期”表,然后从该表中下载该日期还是在上述查询中添加日期?

【问题讨论】:

  • 首先,您应该创建一个包含正式休假日期的新日历表。一旦你有了这个,你想到的任何查询都会相对简单。
  • 如果您准备添加一个每天一行的实际表格,并且在该行上有一些东西来标记当天是否是公共假期等,这会容易得多。您准备好了吗?这样做?
  • 我认为添加新的日历表会更容易,但是在添加“假期”表后如何考虑查询中的天数?
  • 不需要所有日期的单独表,至少在 MySQL 8.0 中不需要(假设它现在支持recursive CTEs)。因此 - OP,哪个 MySQL 版本?
  • @amadan 是的,但它让事情变得容易多了,而且我合作过的大多数专业 DBA 都提倡使用一个——它们占用的存储空间最小,作为一次性操作很容易计算,并且这比每次运行查询时动态生成数据更优化

标签: mysql datetime summarize holidays-gem


【解决方案1】:

如果您有一个列出所有日期的日历表(不仅是假期,还包括接下来 X 年中的每一天,以及该天是否为公共假期等的列),那么您的查询看起来像

SELECT l.id_leave, count(*)
FROM calendar c INNER JOIN leave l ON c.calendar_date BETWEEN l.start_date AND l.end_date
WHERE c.day_type NOT IN ('publicholiday', 'weekend')
GROUP BY l.id_leave

您可以添加更多复杂性,但这是核心。不知道为什么你的假期从早上 7 点开始到晚上 10 点结束,但是如果你允许小数天休息,你可能需要添加一些逻辑 - 将开始日期延长到午夜(这样中间就可以正常工作)然后使用 case when to根据所考虑的日期是开始日期还是结束日期或中间日期来构建分数

【讨论】:

  • 这是一个非常糟糕的主意。你不需要它。只需为每一天的假期输入一个条目,然后加入。
  • 随意提交您自己的答案,而不是仅仅批评他人。 SO 就是要对提问的人有所帮助;以“这是一个糟糕的想法”等不合理的说法攻击发布答案的人对任何人都没有帮助
  • 我认为关于内部连接“假期”表,但另一方面,ON calendar.date_value 呢?
  • 您的假期表仅包含假期。你仍然需要一些东西来将你的日期范围扩展到 N 行,这样你就可以计算它们。我提倡让你的“每天一行”日历表做到这一点(你使用了大量的联合来实现同样的效果,但这让你的查询更难阅读和维护
【解决方案2】:

没有完整日历表的变体,但在问题中有一个假期表(尽管有完整日期 - 您必须为每年填充它):

SELECT L.id_leave, COUNT(*)
FROM (
    SELECT '1970-01-01' + INTERVAL (seq) DAY AS date
    FROM seq_0_to_24836
) C
INNER JOIN leaves L
    ON C.date BETWEEN L.begin_date AND L.end_date
    AND NOT WEEKDAY(date_value) IN (5, 6)
    AND NOT EXISTS (
        SELECT * FROM holidays H WHERE H.date = C.date
    )
GROUP BY L.id_leave

注意:seq_0_to_24836 是 MariaDB 专用扩展,将在此处生成从纪元到 2037 年底的所有日期。

【讨论】:

    【解决方案3】:

    在分析所有 cmets 并回答后,我找到了解决方案:

    1) 我创建了假期表。

    2) 我创建了该查询,它选择所有并总结没有周末和节假日的 leave_time:

    SELECT *, 
    TIME_FORMAT(SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(TIME(leave.END_DATE), TIME(leave.BEGIN_DATE))))), '%H:%i:%s') AS 'LEAVE TIME' 
    FROM 
    (SELECT ADDDATE('1970-01-01', t4 * 10000 + t3 * 1000 + t2 * 100 + t1 * 10 + t0) AS date_value FROM 
    (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0, 
    (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1, 
    (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2, 
    (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3, 
    (SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4) calendar 
    INNER JOIN `leave` ON calendar.date_value BETWEEN DATE(leave.BEGIN_DATE) AND DATE(leave.END_DATE) 
    WHERE NOT WEEKDAY(date_value) IN (5, 6) AND NOT DATE(date_value) IN (SELECT DATE_HOLIDAY FROM holidays) GROUP BY ID_LEAVE;
    

    我刚刚在以下链接中进行了测试:

    https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=a25a3dc5762dba08541c002f12efc024

    有人对此有任何警告吗? 谢谢你,祝你复活节快乐;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多