Вычтите дни из даты, игнорируя выходные дни - PullRequest
0 голосов
/ 26 октября 2018

Я пытаюсь выполнить операцию вычитания в некоторые даты, в моем конкретном случае мне нужно вычесть только рабочие дни (исключая выходные дни, т.е. воскресенье и субботу)

моя таблица выглядит следующим образом:

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

Моя цель состоит в том, чтобы вычислить новый столбец (новую дату) путем вычитания days_to_sub из date с учетом только рабочих (рабочих) дней и исключая(или игнорируя) выходные дни.

Я пробовал что-то вроде этого:

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 голосов
/ 27 октября 2018

Давайте начнем с объяснения вашей ситуации:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...