如何排除周末和节假日日期并在 MySQL 中找到预期日期?

How to exclude weekends and holidays dates and find an expected date in MySQL?

我正在处理一项任务,我有 2 tables。即 ticketsholidays.

现在我也有完成工单的天数了。现在我需要通过排除假期(在 holidays table 中指定)和 weekends.

来找到预期日期

现在我可以使用 ticket created datedays to complete the ticket 查找日期。但无法通过去除节假日和周末来计算预产期。

如果机票预计到期日在节假日或周末,我们需要将预计到期日提前。

然后我们需要比较 ticket_closed_dateexpected_due_date

如果ticket_closed_date <= expected_due_date则需要returnisSlaMetYES。否则需要 return isSlaMet 作为 NO.

门票Table

节假日Table

示例: 通常,如果工单是在 2nd October,2020 上创建的,并且其完成天数是 3,那么预期的截止日期将是 5th October 我们放假了,5th October。但是在工单创建日期和预计到期日期之间有 1 个假期和 2 个周末。即 3rd, 4th and 5th of October 。所以我们需要将预产期延长 3 days(因为 2 个周末 + 1 个假期)。即 8th October。票务已于 9th October.

关闭

然后我们需要比较ticket closed date(9th october)expected due date(8th October)和returnisSlaMet作为YES

预期输入

Tickets Table
--------------------------------------------------------------------------------
tid         createdAt         apply_sla    ticket_closed_date     days_to_complete
--------------------------------------------------------------------------------
100    2020-10-02 00:00:00        1       2020-10-09 00:00:00           3       
--------------------------------------------------------------------------------
       
Holidays Table
----------------------------------------------
id        holiday_date          end_date
----------------------------------------------
20         2020-10-05          2020-10-05
----------------------------------------------

Along with the above holiday, we need to exclude Weekends.

预期输出

Tickets Table
--------------------------------------------------------------------------------------------------------------------------
tid       createdAt     apply_sla    ticket_closed_date   days_to_complete   expected_due_date    completedIn   isSlaMet
--------------------------------------------------------------------------------------------------------------------------
100  2020-10-02 00:00:00    1       2020-10-09 00:00:00             3        2020-10-08 00:00:00      4           NO
  
--------------------------------------------------------------------------------------------------------------------------

这是我到目前为止一直在使用的查询。

    SELECT 
        `t`.`tid`, `t`.`createdAt`, `t`.`days_to_complete`,
        `t`.`ticket_closed_date`,`holidays`.`holiday_date`,
        `holidays`.`end_date`, `t.apply_sla`,
        IF(ISNULL(`t`.`ticket_closed_date`),
            NULL,
            IF((`t`.`apply_sla` = 1),
                IF(((CAST(`t`.`createdAt` AS DATE) + INTERVAL (`t`.`days_to_complete` + 1) DAY) BETWEEN `holidays`.`holiday_date` AND `holidays`.`end_date`),
                    IF((CAST(`t`.`ticket_closed_date` AS DATE) <= (`holidays`.`end_date` + INTERVAL `t`.`days_to_complete` DAY)),
                        'YES',
                        'NO'),
                    IF((CAST(`t`.`ticket_closed_date` AS DATE) <= (`t`.`createdAt` + INTERVAL (`t`.`days_to_complete` + 1) DAY)),
                        'YES',
                        'NO')),
                IF(((TO_DAYS(`t`.`ticket_closed_date`) - TO_DAYS(`t`.`createdAt`)) > (`t`.`days_to_complete` + 1)),
                    'NO',
                    'YES')
            )
        ) AS `isSlaMet`
    FROM
        (`tickets` `t`
        LEFT JOIN `holidays` ON (((CAST(`t`.`createdAt` AS DATE) + INTERVAL (`t`.`days_to_complete` + 1) DAY) BETWEEN `holidays`.`holiday_date` AND `holidays`.`end_date`)))
    ORDER BY `t`.`tid` DESC;

这比看起来更复杂。最简单的方法可能是暴力破解:使用递归 CTE(仅在 MySQL 8.0 中可用)枚举工单创建日期和关闭日期之间的所有天数,然后过滤掉周末和节假日以计算 SLA 天数:

with recursive cte_tickets as (
    select tid, created_at as dt, ticket_closed_date
    from tickets 
    where apply_sla = 1
    union all
    select tid, dt + interval 1 day, ticket_closed_date
    from cte_tickets
    where dt < ticket_closed_date
)
select t.*, 
    t.created_at 
        + interval (t.days_to_complete + sum(weekday(dt) in (5, 6) or h.holiday_date is not null)) day
        as expected_due_date,    
    count(*) - sum(weekday(dt) in (5, 6) or h.holiday_date is not null) - 1 completed_in,
    t.ticket_closed_date <= t.created_at 
        + interval (t.days_to_complete + sum(weekday(dt) in (5, 6) or h.holiday_date is not null)) day
        as is_sla_met
from tickets t
inner join cte_tickets ct on ct.tid = t.tid
left join holidays h on ct.dt between h.holiday_date and h.end_date
group by t.tid

Demo on DB Fiddle:

tid | created_At          | apply_sla | ticket_closed_date  | days_to_complete | expected_due_date   | completed_in | is_sla_met
--: | :------------------ | :-------- | :------------------ | ---------------: | :------------------ | -----------: | ---------:
100 | 2020-10-02 00:00:00 | 1         | 2020-10-09 00:00:00 |                3 | 2020-10-08 00:00:00 |            4 |          0