我想询问是否有一种聪明或简单的方法来获取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
答案 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表方法对其他用途也很有趣,所以我去看看.. :)