Есть ли способ получить первый и последний будний день месяца? - PullRequest
5 голосов
/ 20 мая 2011

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

Например:

For the period: 2011-05-01 till 2011-05-31
First weekday should be: 2011-05-02 and not 2011-05-01 as 2011-05-01 is Sunday.
Last weekday should be: 2011-05-31 as it is Tuesday.

For the period: 2011-04-01 till 2011-04-30,
First weekday: 2011-04-01
Last weekday: 2011-04-29

Ответы [ 6 ]

2 голосов
/ 20 мая 2011

Как насчет определения функций FIRST_WDOM() (неделя / рабочий день месяца) и LAST_WDOM() в стиле LAST_DAY()?

DELIMITER //
CREATE FUNCTION first_wdom (d DATETIME) -- First work/week day of month
RETURNS DATE DETERMINISTIC
BEGIN
  DECLARE first_dom DATE;
  SET first_dom = DATE_FORMAT(d, '%Y-%m-01');

  RETURN first_dom + INTERVAL (CASE DAYOFWEEK(first_dom)
                               WHEN 1 THEN 1
                               WHEN 7 THEN 2
                               ELSE 0
                               END) DAY;
END //
CREATE FUNCTION last_wdom (d DATETIME) -- Last work/week day of month
RETURNS DATE DETERMINISTIC
BEGIN
  DECLARE last_dom DATE;
  SET last_dom = LAST_DAY(d);

  RETURN last_dom - INTERVAL (CASE DAYOFWEEK(last_dom)
                              WHEN 1 THEN 2
                              WHEN 7 THEN 1
                              ELSE 0
                              END) DAY;
END //
DELIMITER ;

mysql> SELECT FIRST_WDOM('2011-05-10'), LAST_WDOM('2011-05-10');
+--------------------------+-------------------------+
| FIRST_WDOM('2011-05-10') | LAST_WDOM('2011-05-10') |
+--------------------------+-------------------------+
| 2011-05-02               | 2011-05-31              | 
+--------------------------+-------------------------+
1 row in set (0.01 sec)

Это всего лишь адаптация ответа ic3b3rg и предполагает, что суббота и воскресенье - ваши выходные дни. Я использую DATETIME, а не DATE, как тип ввода, чтобы избежать предупреждений об усечении при передаче, скажем, NOW().

2 голосов
/ 20 мая 2011

Я думаю, что это будет делать то, что вы просите

SELECT
  CASE DAYOFWEEK(date1)
    WHEN 1 THEN DATE_ADD(date1, INTERVAL 1 DAY)
    WHEN 7 THEN DATE_ADD(date1, INTERVAL 2 DAY)
    ELSE date1 END AS dateStart,
  CASE DAYOFWEEK(date2)
    WHEN 1 THEN DATE_ADD(date2, INTERVAL -2 DAY)
    WHEN 7 THEN DATE_ADD(date2, INTERVAL -1 DAY)
    ELSE date2 END AS dateEnd
FROM myTable
1 голос
/ 20 мая 2011

Чтобы получить первый и последний день месяца с заданной датой (я использовал сегодняшнюю дату):

select  
 month(current_day) as the_month,
 case
    when dayofweek(days.first_day) = 7 then date_add(days.first_day, interval 2 day)
    when dayofweek(days.first_day) = 1 then date_add(days.first_day, interval 1 day)
    else days.first_day
  end as first_weekday,
  case
    when dayofweek(days.last_day) = 7 then date_sub(days.last_day, interval 1 day)
    when dayofweek(days.last_day) = 1 then date_sub(days.last_day, interval 2 day)
    else days.last_day
  end as last_weekday
from
  ( select
      curdate() as current_day,
      concat(date_format(LAST_DAY(curdate()),'%Y-%m-'),'01') as first_day,  
      LAST_DAY(curdate()) as last_day 
  ) days

Если даты, которые необходимо найти, находятся в пределахзаданный диапазон (например, между 1 января 1900 года и 31 декабря 2199 года), тогда вам может повезти с предварительным вычислением всех этих дат и сохранением их в таблице поиска (с теми же тремя столбцами в запросе выше).

1 голос
/ 20 мая 2011

С таблицей календаря я, вероятно, напишу

select min(cal_date) first_weekday
from calendar
where cal_date >= '2011-05-01'
  and day_of_week in ('Mon', 'Tue', 'Wed', 'Thu', 'Fri')

В моей таблице тестовых календарей есть 50-летние данные календаря.Этот запрос возвращается через 68 микросекунд.

select max(cal_date) 
from calendar
where cal_date < '2011-06-01'
  and day_of_week in ('Mon', 'Tue', 'Wed', 'Thu', 'Fri')

На самом деле, я бы, вероятно, создал бы представление о днях недели или представление о рабочих днях, а затем запросил бы это.

Мне легче найти таблицу календаряиметь дело с чем различные функции даты для нескольких платформ DBMS.Это более читабельно - вы можете посмотреть на SQL и сказать: «Да, это должно быть правильно».

1 голос
/ 20 мая 2011

Создать таблицу с именем tblOffset

tblOffset
dayofweek offset
1         1
2         0
3         0
4         0
5         0
6         0
7         2

Затем, чтобы получить первый рабочий день после указанной даты (в моем примере 2011-01-01), используйте:

SELECT DATE_ADD('2011-01-01', INTERVAL `offset` DAY), from tblOffset where `dayofweek` = DAYOFWEEK('2011-01-01')

РЕДАКТИРОВАТЬ: Это не принимает во внимание праздники. Но поэтому вам необходимо иметь отдельный стол со всеми праздничными днями для ваших клиентов, регионов, религий и т. Д.

0 голосов
/ 20 мая 2011

Я бы попробовал что-то похожее на решение, которое я дал в вопросе Выберите все месяцы в пределах данного диапазона дат, включая месяцы с 0 значениями , которые включали дополнительную таблицу со всеми датами в заданном периоде.Я думаю, что мой перешел с 1970 на 2100 или аналогичный:)

По сути, вы создаете эту таблицу и добавляете пару дополнительных столбцов с именами ddFirstWorkDay и ddLastWorkDay как битовое поле, которое вы вручную заполняете с помощью '1'для первого и последнего рабочих дней каждого месяца в таблице - только двенадцать из них в год, поэтому не должно занимать слишком много времени.Вы также можете принять во внимание праздники и другие особые случаи, используя этот метод, поскольку данные вводятся вручную, а не рассчитываются.

Затем вы можете создать запрос в соответствии с приведенными ниже строками, чтобы получить даты, которые выинтересует.

SELECT ddDate from dimDates
WHERE ddDate BETWEEN 'start date' AND 'end date'
   AND ddFirstWorkDay = 1
ORDER BY ddDate
LIMIT 1

SELECT ddDate from dimDates
WHERE ddDate BETWEEN 'start date' AND 'end date'
   AND ddLastWorkDay = 1
ORDER BY ddDate DESC
LIMIT 1

Я думаю, что это даст вам то, что вам нужно, хотя вам нужно будет выполнить два запроса для каждого тестируемого диапазона дат, таблица небольшая, поэтому должна возвращаться очень быстро.

Вам может показаться, что метод таблицы dimDates интересен и для других целей, поэтому я бы посмотрел ..:)

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