有没有办法获得一个月的第一个和最后一个工作日?

时间:2011-05-20 13:37:04

标签: mysql sql

我想询问是否有一种聪明或简单的方法来获取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 个答案:

答案 0 :(得分:2)

我认为这会做你要求的事情

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 :(得分:2)

如何以LAST_DAY()的方式定义函数FIRST_WDOM()(周/工作日)和LAST_WDOM()

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's answer的改编版,并假设星期六和星期日是你的周末日。我使用DATETIME而不是DATE作为输入类型,以避免在传入时发生截断警告,比如NOW()

答案 2 :(得分:1)

创建一个名为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')

编辑:这不考虑假期。但是因此,您需要有一个单独的表格,其中包含您所在地区,宗教等的所有假期。

答案 3 :(得分:1)

使用日历表,我可能会写

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并说“是的,那应该是正确的”。

答案 4 :(得分:1)

要获得指定日期的月份的第一天和最后一天(我使用今天的日期):

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

如果您需要查找的日期在指定的范围内(例如在1900年1月1日和2199年12月31日之间),那么您可能会更好地预先计​​算所有这些日期,并将它们存储在查找表(在上面的查询中使用相同的三列)。

答案 5 :(得分:0)

我会尝试类似于我在问题Select all months within given date span, including the ones with 0 values中提供的解决方案,其中包含一个包含给定期间所有日期的附加表格。我认为我的从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表方法对其他用途也很有趣,所以我去看看.. :)