Давайте начнем с объяснения вашей ситуации:
Понедельник) Если мы позиционируемся в понедельник, то результат, который вы ожидаете вычесть (реальный) при вычитании 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