从忽略周末的日期开始减去几天

时间:2018-10-26 20:25:10

标签: mysql sql date datetime subquery

我正在尝试在某些日期进行减法运算,在我的特殊情况下,我只需要减去工作日(不包括周末,即周日和周六)

我的桌子看起来像这样:

date         | days_to_subtract   | dayName
23-04-2004   |   3                | Friday
23-05-2004   |   5                | Monday
21-04-2004   |   7                | Tuesday
25-04-2004   |   30               | Monday
01-04-2004   |   22               | Thursday

我的目标是通过从 date 中减去 days_to_sub 来计算新列(新日期),其中仅考虑工作(劳动)日并排除(或忽略)周末的日子。

我尝试过这样的事情:

SELECT 
    CASE WHEN dayName = "Monday" then date_sub(date, days_to_subtract + 2)
         WHEN dayName = "Tuesday" then date_sub(date, days_to_subtract + 1)
         ELSE date_sub(date, days_to_subtract)
    END AS newColumn
FROM
    "MyTable"

但是当减去天数大于5时,这将不起作用。

有没有建议使用简单的SQL并且没有复杂的函数来解决此问题?

1 个答案:

答案 0 :(得分:1)

让我们首先说明您的情况:

星期一)假设我们位于星期一,那么减去N(虚拟)天后,您期望(真实)减去的结果将是:

"Mon" - 1 day  => "Mon" - (1 + 2 * (1)) days => "Mon" - 3 days  /* 1 weekend affecting */
"Mon" - 2 days => "Mon" - (2 + 2 * (1)) days => "Mon" - 4 days  /* 1 weekend affecting */
"Mon" - 3 days => "Mon" - (3 + 2 * (1)) days => "Mon" - 5 days  /* 1 weekend affecting */
...
"Mon" - 6 days => "Mon" - (6 + 2 * (2)) days => "Mon" - 10 days /* 2 weekends affecting */
...

星期二)假设我们位于星期二,那么减去N(虚拟)天后,您期望(真实)减去的结果将是:

"Tue" - 1 day  => "Tue" - (1 + 2 * (0)) days => "Tue" - 1 days  /* 0 weekends affecting */
"Tue" - 2 days => "Tue" - (2 + 2 * (1)) days => "Tue" - 4 days  /* 1 weekend affecting */
"Tue" - 3 days => "Tue" - (3 + 2 * (1)) days => "Tue" - 5 days  /* 1 weekend affecting */
...
"Tue" - 6 days => "Tue" - (6 + 2 * (1)) days => "Tue" - 8 days  /* 1 weekend affecting */
"Tue" - 7 days => "Tue" - (7 + 2 * (2)) days => "Tue" - 11 days /* 2 weekends affecting */
...

星期三)假设我们位于星期三,那么减去N(虚拟)天后,您期望(真实)减去的结果将是:

"Wed" - 1 day  => "Wed" - (1 + 2 * (0)) days => "Wed" - 1 days  /* 0 weekends affecting */
"Wed" - 2 days => "Wed" - (2 + 2 * (0)) days => "Wed" - 2 days  /* 0 weekends affecting */
"Wed" - 3 days => "Wed" - (3 + 2 * (1)) days => "Wed" - 5 days  /* 1 weekend affecting */
...
"Wed" - 6 days => "Wed" - (6 + 2 * (1)) days => "Wed" - 7 days  /* 1 weekend affecting */
"Wed" - 7 days => "Wed" - (7 + 2 * (1)) days => "Wed" - 9 days  /* 1 weekend affecting */
"Wed" - 8 days => "Wed" - (8 + 2 * (2)) days => "Wed" - 12 days /* 2 weekends affecting */
...

我们可以注意到,这种逻辑中间存在一个数学演算。如果我们以这种方式索引一周中的几天:

0 <-> Monday
1 <-> Tuesday
2 <-> Wednesday
3 <-> Thursday
4 <-> Friday

然后,与以前的演算匹配的公式将是:

Week_Day - X days => Week_Day - X days + 2 * CEIL((X - INDEX_OF(Week_Day)) / 5) days

where INDEX_OF() is a function that returns the previous indexing for days.

示例1)

假设我们需要从星期三减去2天,那么以前的公式将减少为:

"Wed" - 2 days => "Wed" - 2 + 2 * CEIL((2 - 2) / 5)
"Wed" - 2 days => "Wed" - 2 + 2 * CEIL(0 / 5)
"Wed" - 2 days => "Wed" - 2 + 2 * 0
"Wed" - 2 days => "Wed" - 2 + 0
"Wed" - 2 days => "Wed" - 2 days

示例2)

假设我们需要从星期三减去8天(在这种情况下,我们在减去中间有两个周末),那么先前的公式将减少为:

"Wed" - 8 days => "Wed" - 8 + 2 * CEIL((8 - 2) / 5)
"Wed" - 8 days => "Wed" - 8 + 2 * CEIL(6 / 5)
"Wed" - 8 days => "Wed" - 8 + 2 * 2 /* Two weekends are affecting */
"Wed" - 8 days => "Wed" - 8 + 4
"Wed" - 8 days => "Wed" - 12 days

因此,基于此公式并操纵MySQL DAYOFWEEK()方法以生成我们的 INDEX_OF()函数,我们可以执行下一个查询:

SELECT
    date AS OriginalDate,
    DAYOFWEEK(date) - 2 AS IndexOfDay,
    days_to_subtract AS DaysToSubtract,
    2 * CEIL((days_to_subtract - (DAYOFWEEK(date) - 2)) / 5) AS WeekendDaysToAdd,
    DATE_SUB(
        date,
        INTERVAL days_to_subtract + (2 * CEIL((days_to_subtract -  (DAYOFWEEK(date) - 2)) / 5)) DAY
    ) AS CalucaltedDay
FROM
    myTable

最后一列 CalculatedDay 将包含您希望从减法中获得的日期,其他列仅用于检查数学演算的步骤。我希望您能理解其背后的逻辑,因为它花费了一些时间来弄清楚并尝试解释它。您可以在下一个链接上看到工作示例:

1) DB Fiddle

2) SQL Fiddle