1

I'm trying to do a subtract operation on some dates, on my particular case I need to subtract only business days (excluding the weekends days, i.e, Sunday and Saturday)

my table looks like this:

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

My goal consist on calculate a new column (new date) by subtracting days_to_sub from date taking into account only business (laboral) days and excluding (or ignoring) the weekends days.

I have tried something like this:

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"

But this will not work when the number of subtract days is greater than 5.

Any suggestion using simple SQL and no complicated functions to approach a solution to this problem?

Shidersz
  • 16,846
  • 2
  • 23
  • 48
anonymos
  • 55
  • 4

1 Answers1

2

Lets start with an explanation of your situation:

Monday) Suposse we are positioned on Monday, then the result you expect to (real) subtract when substracting N (virtual) days will be like:

"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 */
...

Tuesday) Suposse we are positioned on Tuesday, then the result you expect to (real) subtract when substracting N (virtual) days will be like:

"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 */
...

Wednesday) Suposse we are positioned on Wednesday, then the result you expect to (real) subtract when substracting N (virtual) days will be like:

"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 */
...

As we can note, there is a mathematical calculus in the middle of this logic. If we indexes the days of the week in this way:

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

Then, a formula that match the previous calculus would be:

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.

Example 1)

Suppose we need to subtract 2 days from Wednesday, then the previous formula will reduce to this:

"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

Example 2)

Suppose we need to subtract 8 days from Wednesday (in this case we have two weekends in the middle of the subtract) then the previous formula will reduce to this:

"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

So, based on this formula and manipulating the MySQL DAYOFWEEK() method for generate our INDEX_OF() function, we can do the next query:

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

The last column CalculatedDay will contain the date you expect to obtain from the subtract, the others columns are just for check the steps of the mathematical calculus. I hope you can understand the logic behind this, because it costme some time to figure it out and try to explain it. You can see working examples on next links:

1) DB Fiddle

2) SQL Fiddle

Shidersz
  • 16,846
  • 2
  • 23
  • 48