MySQL函数查找两个date之间的工作天数

Excel有NETWORKDAYS()函数,用于查找两个date之间的工作日数。

任何人都有类似的functionMySQL? 由于节假日增加了复杂性,解决scheme不必处理节假日。

这个expression –

5 * (DATEDIFF(@E, @S) DIV 7) + MID('0123444401233334012222340111123400012345001234550', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1) 

计算开始date@S和结束date@E之间的营业日数。

假定结束date(@E)不在开始date(@S)之前。 与DATEDIFF兼容,因为相同的开始date和结束date为零个工作日。 忽略假期。

数字串构造如下。 创build开始date和结束date的表格,行必须以星期一(WEEKDAY 0)开始,列也必须以星期一开始。 填写从左上angular到右下angular的全部0的对angular线(即周一到周一,周二和周二等有0个工作日)。 对于每一天的对angular线开始(必须始终为0),并在一天一天的时间填写右边的列。 如果您在周末日(非营业日)栏登陆,营业日数不变,则从左侧开始。 否则,营业日数将增加一个。 当您到达行循环的结尾时,返回到同一行的开始位置并继续,直到再次到达对angular线。 然后继续下一行。

例如假设星期六和星期日不是工作日 –

  | MTWTFSS -|-------------- M| 0 1 2 3 4 4 4 T| 4 0 1 2 3 3 3 W| 3 4 0 1 2 2 2 T| 2 3 4 0 1 1 1 F| 1 2 3 4 0 0 0 S| 1 2 3 4 5 0 0 S| 1 2 3 4 5 5 0 

然后将表中的49个值连接到string中。

请让我知道,如果你发现任何错误。

– 修改表格:

  | MTWTFSS -|-------------- M| 0 1 2 3 4 4 4 T| 4 0 1 2 3 3 3 W| 3 4 0 1 2 2 2 T| 2 3 4 0 1 1 1 F| 1 2 3 4 0 0 0 S| 0 1 2 3 4 0 0 S| 0 1 2 3 4 4 0 

改进string:'0123444401233334012222340111123400001234000123440'

改进的expression:

 5 * (DATEDIFF(@E, @S) DIV 7) + MID('0123444401233334012222340111123400001234000123440', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1) 

由于您需要在某个地方跟踪假期,因此日历表格似乎是适当的:

 CREATE TABLE Calendar ( calendar_date DATETIME NOT NULL, is_holiday BIT NOT NULL, is_weekend BIT NOT NULL, CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED (calendar_date) ) 

您当然需要在您的应用程序中使用所有date填充任何时间段。 由于一年只有365天(或366天),从1900年到2100年并不是什么大不了的事。 只要确保你加载所有的date,而不仅仅是假期。

在这一点上,你所需要的查询变得微不足道:

 SELECT COUNT(*) FROM Calendar WHERE calendar_date BETWEEN '2009-01-01' AND '2009-10-01' AND is_holiday = 0 AND is_weekend = 0 

警告:我主要使用MS SQL,而且很长一段时间没有使用MySQL,因此您可能需要调整上述内容。 例如,我甚至不记得MySQL是否具有BIT数据types。

除了用于生成matrix的方法更复杂以外,该解决scheme基本上使用与Rodger相同的方法。 注意:此解决scheme的输出与NETWORKDAYS不兼容。

与Rodger的解决scheme一样,这将计算开始date(@S)和结束date(@E)之间的工作日数,而无需定义存储过程。 它假定结束date不在开始date之前。 使用相同的开始和结束date将产生0.节假日不考虑。

这和Rodger的解决scheme之间的主要区别在于matrix和结果string是由我没有包含的复杂algorithm构造的。 该algorithm的输出通过unit testing进行validation(请参阅下面的testinginput和输出)。 在matrix中,任何给定的x和y值(WEEKDAY(@S)和WEEKDAY(@E))的交集产生两个值之间的工作日差异。赋值顺序实际上是不重要的,因为这两个值相加绘制位置。

工作日是星期一至星期五

  | MTWTFSS -|-------------- M| 0 1 2 3 4 5 5 T| 5 0 1 2 3 4 4 W| 4 5 0 1 2 3 3 T| 3 4 5 0 1 2 2 F| 2 3 4 5 0 1 1 S| 0 1 2 3 4 0 0 S| 0 1 2 3 4 5 0 

表中的49个值连接成以下string:

 0123455501234445012333450122234501101234000123450 

最后,正确的表述是:

 5 * (DATEDIFF(@E, @S) DIV 7) + MID('0123455501234445012333450122234501101234000123450', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1) 

我已经使用此解决schemevalidation了以下input和输出:

 Sunday, 2012-08-26 -> Monday, 2012-08-27 = 0 Sunday, 2012-08-26 -> Sunday, 2012-09-02 = 5 Monday, 2012-08-27 -> Tuesday, 2012-08-28 = 1 Monday, 2012-08-27 -> Monday, 2012-09-10 = 10 Monday, 2012-08-27 -> Monday, 2012-09-17 = 15 Monday, 2012-08-27 -> Tuesday, 2012-09-18 = 16 Monday, 2012-08-27 -> Monday, 2012-09-24 = 20 Monday, 2012-08-27 -> Monday, 2012-10-01 = 25 Tuesday, 2012-08-28 -> Wednesday, 2012-08-29 = 1 Wednesday, 2012-08-29 -> Thursday, 2012-08-30 = 1 Thursday, 2012-08-30 -> Friday, 2012-08-31 = 1 Friday, 2012-08-31 -> Saturday, 2012-09-01 = 1 Saturday, 2012-09-01 -> Sunday, 2012-09-02 = 0 Sunday, 2012-09-02 -> Monday, 2012-09-03 = 0 Monday, 2012-09-03 -> Tuesday, 2012-09-04 = 1 Tuesday, 2012-09-04 -> Wednesday, 2012-09-05 = 1 Wednesday, 2012-09-05 -> Thursday, 2012-09-06 = 1 Thursday, 2012-09-06 -> Friday, 2012-09-07 = 1 Friday, 2012-09-07 -> Saturday, 2012-09-08 = 1 Saturday, 2012-09-08 -> Sunday, 2012-09-09 = 0 Monday, 2012-09-24 -> Sunday, 2012-10-07 = 10 Saturday, 2012-08-25 -> Saturday, 2012-08-25 = 0 Saturday, 2012-08-25 -> Sunday, 2012-08-26 = 0 Saturday, 2012-08-25 -> Monday, 2012-08-27 = 0 Saturday, 2012-08-25 -> Tuesday, 2012-08-28 = 1 Saturday, 2012-08-25 -> Wednesday, 2012-08-29 = 2 Saturday, 2012-08-25 -> Thursday, 2012-08-30 = 3 Saturday, 2012-08-25 -> Friday, 2012-08-31 = 4 Saturday, 2012-08-25 -> Sunday, 2012-09-02 = 0 Monday, 2012-08-27 -> Monday, 2012-08-27 = 0 Monday, 2012-08-27 -> Tuesday, 2012-08-28 = 1 Monday, 2012-08-27 -> Wednesday, 2012-08-29 = 2 Monday, 2012-08-27 -> Thursday, 2012-08-30 = 3 Monday, 2012-08-27 -> Friday, 2012-08-31 = 4 Monday, 2012-08-27 -> Saturday, 2012-09-01 = 5 Monday, 2012-08-27 -> Sunday, 2012-09-02 = 5 

只是为了进一步的参考。 以上都不是为我工作的,而是@Jeff Kooser的修改版本:

 SELECT (DATEDIFF(date_end, date_start)) - ((WEEK(date_end) - WEEK(date_start)) * 2) - (case when weekday(date_end) = 6 then 1 else 0 end) - (case when weekday(date_start) = 5 then 1 else 0 end) - (SELECT COUNT(*) FROM holidays WHERE holiday>=date_start and holiday<=data_end) 

给定一个月的第一天,这将返回该月内的平日数。 在MySQL中。 没有存储过程。

 SELECT (DATEDIFF(LAST_DAY(?),?) + 1) - ((WEEK(LAST_DAY(?)) - WEEK(?)) * 2) - (case when weekday(?) = 6 then 1 else 0 end) - (case when weekday(LAST_DAY(?)) = 5 then 1 else 0 end) 

基于Yada的上述function,这个主题上的细微变化,计算从当前date (不包括) 剩余的工作日 ,直到目标date。 它也处理在以色列不同的周末日子:-)注意, 如果目标date是过去的 (这正是我想要的),这将产生负面结果

 DELIMITER // DROP FUNCTION IF EXISTS WORKDAYS_LEFT// CREATE FUNCTION WORKDAYS_LEFT(target_date DATE, location char(2)) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN DECLARE start_date DATE; DECLARE end_date DATE; DECLARE check_date DATE; DECLARE diff INT; DECLARE extra_weekend_days INT; DECLARE weeks_diff INT; SET start_date = CURDATE(); SET end_date = target_date; SET diff = DATEDIFF(end_date, start_date); SET weeks_diff = FLOOR(diff / 7); SET end_date = DATE_SUB(end_date, INTERVAL (weeks_diff * 7) DAY); SET check_date = DATE_ADD(start_date, INTERVAL 1 DAY); SET extra_weekend_days = 0; WHILE check_date <= end_date DO SET extra_weekend_days = extra_weekend_days + IF(DAYNAME(check_date) = 'Saturday', 1, 0) + IF(DAYNAME(check_date) = IF(location = 'IL','Friday', 'Sunday'), 1, 0); SET check_date = DATE_ADD(check_date, INTERVAL 1 DAY); END WHILE; RETURN diff - weeks_diff*2 - extra_weekend_days; END// DELIMITER ; 

build议的string可能是错的吗?

DATEDIFF(从,到)不包括“到”。 这个string应该如此:

星期一 – >星期五= {星期一,星期三,星期四} = 4

星期一 – >星期六= {星期一,星期二,星期五,星期五} = 5

星期二 – >星期一= {Tu,星期三,星期五,星期六,星期六,星期日,星期一除外} = 4

等等

build议matrix:

  | MTWTFSS -|-------------- M| 0 1 2 3 4 5 5 T| 4 0 1 2 3 4 4 W| 3 4 0 1 2 3 3 T| 2 3 4 0 1 2 2 F| 1 2 3 4 0 1 1 S| 0 1 2 3 4 0 0 S| 0 1 2 3 4 5 0 

string:'0123455401234434012332340122123401101234000123450'

我在这里错过了什么? 🙂

亚达的解决scheme无法正常工作。 我的更改:

 DELIMITER $$ DROP FUNCTION IF EXISTS `catalog`.`WORKDAYS` $$ CREATE FUNCTION `catalog`.`WORKDAYS` (first_date DATETIME, second_date DATETIME) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN DECLARE start_date DATE; DECLARE end_date DATE; DECLARE diff INT; IF (first_date < second_date) THEN SET start_date = first_date; SET end_date = second_date; ELSE SET start_date = second_date; SET end_date = first_date; END IF; SET diff = DATEDIFF(end_date, start_date); RETURN (CASE WHEN DAYNAME(start_date) not in ('Saturday', 'Sunday') && DAYNAME(end_date) = 'Saturday' THEN diff WHEN DAYNAME(start_date) not in ('Saturday', 'Sunday') && DAYNAME(end_date) = 'Sunday' THEN (diff - 2) WHEN DAYNAME(start_date) = 'Saturday' && DAYNAME(end_date) = 'Sunday' THEN (diff - 1) WHEN DAYNAME(start_date) = 'Saturday' && DAYNAME(end_date) = 'Saturday' THEN (diff + 1) WHEN DAYNAME(start_date) = 'Sunday' && DAYNAME(end_date) in ('Saturday', 'Sunday') THEN (diff + 1) WHEN DAYNAME(start_date) = 'Saturday' && DAYNAME(end_date) not in ('Saturday', 'Sunday') THEN (diff -1) WHEN DAYNAME(start_date) = 'Sunday' && DAYNAME(end_date) not in ('Saturday', 'Sunday') THEN (diff + 1) WHEN DAYNAME(start_date) not in ('Saturday', 'Sunday') && DAYNAME(end_date) not in ('Saturday', 'Sunday') && WEEKDAY(start_date) > WEEKDAY(end_date) THEN (diff - 2) ELSE diff END) - (FLOOR(diff / 7) * 2) - (CASE WHEN DAYNAME(start_date) = 'Sunday' THEN 1 ELSE 0 END) - (CASE WHEN DAYNAME(end_date) = 'Saturday' THEN 1 ELSE 0 END); END $$ DELIMITER ; 

与“无视假日”相提并论的问题是每个国家都会有不同的假期。

你必须首先定义你的国家的假期,然后通过它们来看看某个date是否是假期。

我不知道你想在MySQL中做什么的generics函数

抱歉!

非周末的日子差别可以这样实现:

 CREATE FUNCTION `WDDIFF` (d0 DATE, d1 DATE) RETURNS INT DETERMINISTIC COMMENT 'Date0, Date1' BEGIN RETURN DATEDIFF(d1, d0) - (DATEDIFF(DATE_SUB(d1, INTERVAL WEEKDAY(d1) DAY), DATE_ADD(d0, INTERVAL (7 - WEEKDAY(d0)) DAY))/7+1)*2 + IF(WEEKDAY(d0)>4, 1, 0) + 1; END 

用法:从月份开始的星期几

SELECT ap.WDDIFF(DATE_SUB(CURDATE(), INTERVAL DAYOFMONTH(CURDATE()) - 1 DAY), CURDATE())

注意:函数计算开始date和结束date

Thi在Sq​​l Server 2005中工作

不知道它是否会为你工作。

 DECLARE @StartDate DATETIME, @EndDate DATETIME SELECT @StartDate = '22 Nov 2009', @EndDate = '28 Nov 2009' ;WITH CTE AS( SELECT @StartDate DateVal, DATENAME(dw, @StartDate) DayNameVal UNION ALL SELECT DateVal + 1, DATENAME(dw, DateVal + 1) FROM CTE WHERE DateVal < @EndDate ) SELECT COUNT(1) FROM ( SELECT * FROM CTE WHERE DayNameVal NOT IN ('Sunday','Saturday') ) DayVals 

我知道这是一个古老的线索,但认为我的解决scheme可能对某些人有帮助。 这是一个查询,我没有需要的functionfind商业日子。 你可以根据需要命名这些字段,我只是故意将其留空。

 SELECT @tmp_s := ept.`date_start`, @tmp_e := IF(ept.`date_end` IS NULL, NOW(),ept.`date_end`), @start := IF(DAYOFWEEK(@tmp_s)=1,@tmp_s + INTERVAL 1 DAY,(IF(DAYOFWEEK(@tmp_s)=7,@tmp_s + INTERVAL 2 DAY,@tmp_s)), @end := IF(DAYOFWEEK(@tmp_e)=1,@tmp_e - INTERVAL 2 DAY,(IF(DAYOFWEEK(@tmp_e)=7,@tmp_e - INTERVAL 1 DAY,@tmp_e)), @bizdays := CASE WHEN DATEDIFF(@end,@start)>7 THEN CEIL((DATEDIFF(@end,@start)/7)*5) WHEN DAYOFWEEK(@end)< DAYOFWEEK(@start) THEN DATEDIFF(@end,@start)-2 ELSE DATEDIFF(@end,@start) END, DATE(@start), DATE(@end), IF(@bizdays>=10,10,@bizdays) FROM `employee_points` ept WHERE ept.`date_start` > '2011-01-01' 

对于上面的NETWORKDAYS()函数,应该添加另外一个条件来涵盖开始date到结束date在7天内和周末期间的情况。

  RETURN (diff + 1) - (FLOOR(diff / 7) * 2) - (CASE WHEN DAYNAME(start_date) = 'Sunday' THEN 1 ELSE 0 END) - (CASE WHEN DAYNAME(end_date) = 'Saturday' THEN 1 ELSE 0 END) - (CASE WHEN diff<7 and WEEK(start_date)<>WEEK(end_date) THEN 2 ELSE 0 end); 

虽然非常旧的post,但帮助很多。 由于@shahcool提供的解决scheme不是返回确切的日子,例如

Workdays('2013-03-26','2013-04-01')返回3天,但实际上必须有5

下面是我testing过的解决scheme,然后重新运行确切的工作日

 DELIMITER $$ DROP FUNCTION IF EXISTS WORKDAYS $$ CREATE FUNCTION `WORKDAYS` (first_date DATETIME, second_date DATETIME) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN DECLARE start_date DATE; DECLARE end_date DATE; DECLARE diff INT; DECLARE NumberOfWeeks INT; DECLARE RemainingDays INT; DECLARE firstDayOfTheWeek INT; DECLARE lastDayOfTheWeek INT; DECLARE WorkingDays INT; IF (first_date < second_date) THEN SET start_date = first_date; SET end_date = second_date; ELSE SET start_date = second_date; SET end_date = first_date; END IF; ## Add one to include both days in interval SET diff = DATEDIFF(end_date, start_date)+1; SET NumberOfWeeks=floor(diff/7); SET RemainingDays=MOD(diff,7); SET firstDayOfTheWeek=DAYOFWEEK(start_date); SET lastDayOfTheWeek=DAYOFWEEK(end_date); IF(firstDayOfTheWeek <= lastDayOfTheWeek) THEN IF( firstDayOfTheWeek<=6 AND 6 <=lastDayOfTheWeek) THEN SET RemainingDays=RemainingDays-1; END IF; IF( firstDayOfTheWeek<=7 AND 7 <=lastDayOfTheWeek) THEN SET RemainingDays=RemainingDays-1; END IF; ELSE IF( firstDayOfTheWeek=7) THEN SET RemainingDays=RemainingDays-1; IF (lastDayOfTheWeek=6) THEN SET RemainingDays=RemainingDays-1; END IF; ELSE SET RemainingDays=RemainingDays-2; END IF; END IF; SET WorkingDays=NumberOfWeeks*5; IF(RemainingDays>0) THEN RETURN WorkingDays+RemainingDays; ELSE RETURN WorkingDays; END IF; END $$ DELIMITER ; 

MYSQL函数返回两个date(含)之间的工作日。 2和6之间是周一至周五,这可以根据您的日历/地区进行调整。


 -- Routine DDL -- Note: comments before and after the routine body will not be stored by the server -- -------------------------------------------------------------------------------- DELIMITER $$ CREATE DEFINER=`root`@`localhost` FUNCTION `fn_GetBusinessDaysBetweenDates`(d1 DATE, d2 DATE) RETURNS int(11) BEGIN DECLARE bDaysInPeriod INT; SET bDaysInPeriod=0; WHILE d1<=d2 DO IF DAYOFWEEK(d1) BETWEEN 2 AND 6 THEN SET bDaysInPeriod=bDaysInPeriod+1; END IF; SET d1=d1+INTERVAL 1 day; END WHILE; RETURN bDaysInPeriod; END 
 Below function will give you the Weekdays, Weekends, Date difference with proper results: You can call the below function like, select getWorkingday('2014-04-01','2014-05-05','day_diffs'); select getWorkingday('2014-04-01','2014-05-05','work_days'); select getWorkingday('2014-04-01','2014-05-05','weekend_days'); DROP FUNCTION IF EXISTS PREPROCESSOR.getWorkingday; CREATE FUNCTION PREPROCESSOR.`getWorkingday`(d1 datetime,d2 datetime, retType varchar(20)) RETURNS varchar(255) CHARSET utf8 BEGIN DECLARE dow1, dow2,daydiff,workdays, weekenddays, retdays,hourdiff INT; declare newstrt_dt datetime; SELECT dd.iDiff, dd.iDiff - dd.iWeekEndDays AS iWorkDays, dd.iWeekEndDays into daydiff, workdays, weekenddays FROM ( SELECT dd.iDiff, ((dd.iWeeks * 2) + IF(dd.iSatDiff >= 0 AND dd.iSatDiff < dd.iDays, 1, 0) + IF (dd.iSunDiff >= 0 AND dd.iSunDiff < dd.iDays, 1, 0)) AS iWeekEndDays FROM ( SELECT dd.iDiff, FLOOR(dd.iDiff / 7) AS iWeeks, dd.iDiff % 7 iDays, 5 - dd.iStartDay AS iSatDiff, 6 - dd.iStartDay AS iSunDiff FROM ( SELECT 1 + DATEDIFF(d2, d1) AS iDiff, WEEKDAY(d1) AS iStartDay ) AS dd ) AS dd ) AS dd ; if(retType = 'day_diffs') then set retdays = daydiff; elseif(retType = 'work_days') then set retdays = workdays; elseif(retType = 'weekend_days') then set retdays = weekenddays; end if; RETURN retdays; END; Thank You. Vinod Cyriac. Bangalore 

我需要两个function。 一个用于计算两个date之间的工作日数,另一个用于将date的x个工作日加上/减去一个工作日。 以下是我在互联网上find的例子。 它们被做成接近标准的DATEDIFF()和DATE_ADD()函数,并相互补充彼此的计算。 例如,DateDiffBusiness('2014-05-14',DateAddBusiness('2014-05-14',5))将等于5。

 DROP FUNCTION IF EXISTS DateDiffBusiness; DELIMITER & CREATE FUNCTION DateDiffBusiness( d2 DATE, d1 DATE ) RETURNS INT DETERMINISTIC COMMENT 'Calculates the number of bussiness days between two dates' BEGIN DECLARE dow1, dow2, days INT; SET dow1 = DAYOFWEEK(d1); SET dow2 = DAYOFWEEK(d2); SET days = FLOOR( DATEDIFF(d2,d1)/7 ) * 5 + CASE WHEN dow1=1 AND dow2=7 THEN 5 WHEN dow1 IN(7,1) AND dow2 IN (7,1) THEN 0 WHEN dow1=dow2 THEN 1 WHEN dow1 IN(7,1) AND dow2 NOT IN (7,1) THEN dow2-1 WHEN dow1 NOT IN(7,1) AND dow2 IN(7,1) THEN 7-dow1 WHEN dow1<=dow2 THEN dow2-dow1+1 WHEN dow1>dow2 THEN 5-(dow1-dow2-1) ELSE 0 END; RETURN days-1; END& DELIMITER ; DROP FUNCTION IF EXISTS DateAddBusiness; DELIMITER & CREATE FUNCTION DateAddBusiness(mydate DATE, numday INT) RETURNS DATE DETERMINISTIC COMMENT 'Adds bussiness days between two dates' BEGIN DECLARE num_week INT DEFAULT 0; DECLARE num_day INT DEFAULT 0; DECLARE adj INT DEFAULT 0; DECLARE total INT DEFAULT 0; SET num_week = numday DIV 5; SET num_day = MOD(numday, 5); IF (WEEKDAY(mydate) + num_day >= 5) then SET adj = 2; END IF; SET total = num_week * 7 + adj + num_day; RETURN DATE_ADD(mydate, INTERVAL total DAY); END& DELIMITER ; 

请Heloootesting。

 DELIMITER $$ DROP FUNCTION IF EXISTS `WORKDAYS` $$ CREATE FUNCTION `WORKDAYS` (first_date DATETIME, second_date DATETIME) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN DECLARE start_date DATE; DECLARE end_date DATE; DECLARE diff INT; DECLARE cnt INT; IF (first_date < second_date) THEN SET start_date = first_date; SET end_date = second_date; ELSE SET start_date = second_date; SET end_date = first_date; END IF; SELECT COUNT(*) INTO cnt FROM `holiday` WHERE (hday BETWEEN start_date AND end_date) and (DAYOFWEEK(hday) != 7 and DAYOFWEEK(hday) != 1); SET diff = DATEDIFF(end_date, start_date) ; RETURN (CASE WHEN DAYNAME(start_date) not in ('Saturday', 'Sunday') && DAYNAME(end_date) = 'Saturday' THEN (diff - cnt) WHEN DAYNAME(start_date) not in ('Saturday', 'Sunday') && DAYNAME(end_date) = 'Sunday' THEN (diff - 2 - cnt) WHEN DAYNAME(start_date) = 'Saturday' && DAYNAME(end_date) = 'Sunday' THEN (diff - 1 - cnt) WHEN DAYNAME(start_date) = 'Saturday' && DAYNAME(end_date) = 'Saturday' THEN (diff + 1 - cnt) WHEN DAYNAME(start_date) = 'Sunday' && DAYNAME(end_date) in ('Saturday', 'Sunday') THEN (diff + 1 - cnt) WHEN DAYNAME(start_date) = 'Saturday' && DAYNAME(end_date) not in ('Saturday', 'Sunday') THEN (diff -1 - cnt) WHEN DAYNAME(start_date) = 'Sunday' && DAYNAME(end_date) not in ('Saturday', 'Sunday') THEN (diff + 1 - cnt) WHEN DAYNAME(start_date) not in ('Saturday', 'Sunday') && DAYNAME(end_date) not in ('Saturday', 'Sunday') && WEEKDAY(start_date) > WEEKDAY(end_date) THEN (diff - 2 - cnt) ELSE (diff - cnt) END) - (FLOOR(diff / 7) * 2) - (CASE WHEN DAYNAME(start_date) = 'Sunday' THEN 1 ELSE 0 END) - (CASE WHEN DAYNAME(end_date) = 'Saturday' THEN 1 ELSE 0 END); END $$ 

和桌上的假期

 DROP TABLE IF EXISTS `holiday`; CREATE TABLE `holiday` ( `id` bigint(32) unsigned NOT NULL AUTO_INCREMENT, `hday` date NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO `holiday` (`id`, `hday`) VALUES (1, '2012-01-01'), (2, '2012-05-01'), (3, '2012-05-08'), (4, '2012-07-05'), (5, '2012-07-06'), (6, '2012-09-28'), (7, '2012-10-28'), (8, '2012-11-17'), (9, '2012-12-24'), (10, '2012-12-25'), (11, '2012-12-26'); etc... 

基于Rodger Bagnall解决scheme的模拟NETWORKDAYS.INTL的函数https://stackoverflow.com/a/6762805/218418

 DELIMITER // DROP FUNCTION IF EXISTS NETWORKDAYS// CREATE FUNCTION NETWORKDAYS(sd DATE, ed DATE) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN RETURN (5 * (DATEDIFF(ed, sd) DIV 7) + MID('0123444401233334012222340111123400001234000123440', 7 * WEEKDAY(sd) + WEEKDAY(ed) + 1, 1))+1; END// DELIMITER ; 

并select

 SELECT NETWORKDAYS('2015-01-01 06:00:00', '2015-01-20 06:00:00'); 

这是DATEDIFF的替代品,适用于+ ve和-ve的差异。

 DELIMITER $$ DROP FUNCTION IF EXISTS WORKDAYSDIFF$$ CREATE FUNCTION WORKDAYSDIFF(sd DATE, ed DATE) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN RETURN IF (sd >= ed, 5 * (DATEDIFF(sd, ed) DIV 7) + MID('0123455501234445012333450122234501101234000123450', 7 * WEEKDAY(ed) + WEEKDAY(sd) + 1, 1), -(5 * (DATEDIFF(ed, sd) DIV 7) + MID('0123455501234445012333450122234501101234000123450', 7 * WEEKDAY(sd) + WEEKDAY(ed) + 1, 1)) ); END$$ DELIMITER ; 

此查询轻松返回两个date之间的工作天数排除周末:

 select datediff('2016-06-19','2016-06-01') - (floor(datediff('2016-06-19','2016-06-01')/6) + floor(datediff('2016-06-19','2016-06-01')/7)); 

我有这个要求,写了一个完整的function,可以避免几个小时的周末和假期为特定的国家(使用一个单独的表)计算。 我已经把整个function和细节放在我的博客( http://mgw.dumatics.com/mysql-function-to-calculate-elapsed-working-time/ )上,同时还有解释和stream程图以及假期表的创build等。我很乐意把它放在这里,但是有点太长了….

问题解决的例子:

假设一个事件发生在“英国”的一个网站“2016年6月10日星期五12:00”,这个网站在09:00到16:00之间开放。 此事件于二零一六年六月十四日(星期二)下午二时结束。

对于上述事件函数,应计算年龄为960分钟= 16小时= [星期五(12:00到16:00)4小时+星期一(9:00到16:00)7小时+星期二5小时09:00至14:00)]

如果你想真正地忽略周末的存在,那么你需要把源于周六/周日的东西看作是源于周一; 以及星期六/星期日结束的事情,好像它真的在星期五结束。 因此,在周末开始和结束的事情,你必须忽略开始和结束。 我不认为其他答案做这个。

以下function是这样做的:

 CREATE DEFINER=`root`@`localhost` FUNCTION `weekdayDiff` ( edate datetime, sdate datetime ) RETURNS int DETERMINISTIC BEGIN if edate>sdate then return 5 * (DATEDIFF(edate, sdate) DIV 7) + MID('+0+1+2+3+4+4+4+4+0+1+2+3+3+3+3+4+0+1+2+2+2+2+3+4+0+1+1+1+1+2+3+4+0+0+0+0+1+2+3+4-1-1+0+1+2+3+4+4-1', 2*(7 * WEEKDAY(sdate) + WEEKDAY(edate)) + 1, 2); else return -(5 * (DATEDIFF(sdate, edate) DIV 7) + MID('+0+1+2+3+4+4+4+4+0+1+2+3+3+3+3+4+0+1+2+2+2+2+3+4+0+1+1+1+1+2+3+4+0+0+0+0+1+2+3+4-1-1+0+1+2+3+4+4-1', 2*(7 * WEEKDAY(edate) + WEEKDAY(sdate)) + 1, 2)); end if; -- The following works unless both start and finish date are on weekends. -- return 5 * (DATEDIFF(edate, sdate) DIV 7) + MID('0123444401233334012222340111123400001234000123440', 7 * WEEKDAY(sdate) + WEEKDAY(edate) + 1, 1); END; 

在Rodger的答案语言中,创build上面string的表格在下面(唯一的区别是,如果它是-1,而不是0,在周六/周日开始和结束):

  | MTWTFSS -|--------------------- M| +0 +1 +2 +3 +4 +4 +4 T| +4 +0 +1 +2 +3 +3 +3 W| +3 +4 +0 +1 +2 +2 +2 T| +2 +3 +4 +0 +1 +1 +1 F| +1 +2 +3 +4 +0 +0 +0 S| +0 +1 +2 +3 +4 -1 -1 S| +0 +1 +2 +3 +4 +4 -1 

由@Rodger Bagnall发布的答案不适合我,例如在2016-04。 它less了一天,它是真实的。

如果谈论计算查询 – 我使用这个:

 set @S = '2016-04-01', @E = '2016-04-30'; select case when WEEKDAY(@S) < 5 then 5 - WEEKDAY(@S) else 0 end #startweek + case when WEEKDAY(@E) < 5 then WEEKDAY(@E) + 1 else 5 end #endweek + ( DATEDIFF(@E, @S) + 1 # plus 1 day cause params is inside 1 month - (7 - WEEKDAY(@S)) # minus start week - (WEEKDAY(@E) + 1) # minus end week ) DIV 7 * 5 #rest part as work_date_count; 

查询不是优化只是为了显示数字来自哪里

 SELECT 5* (DATEDIFF(u.EndDate, u.StartDate) DIV 7) + MID('1234555512344445123333451222234511112345001234550', 7 * WEEKDAY(u.StartDate) + WEEKDAY(u.EndDate) + 1, 1) 

这是当你想考虑以下情况:

1)如果startdate = enddate,duration = 1,同样..

我使用最多投票答案中提到的逻辑计算string,并根据需要得到结果。

我使用这个解决scheme,最后请看:

 DROP FUNCTION IF EXISTS datediff_workdays; CREATE FUNCTION datediff_workdays(start_date DATE, end_date DATE) RETURNS INTEGER BEGIN RETURN 5 * (DATEDIFF(end_date, start_date) DIV 7) + MID('0123455501234445012333450122234501101234000123450', 7 * WEEKDAY(start_date) + WEEKDAY(end_date) + 1, 1); END 

您需要使用DATEDIFF才能获得MySQL中两个date之间的天数。 IE:

 DATEDIFF(t.date_column_1, t.date_column_2) 

但斯蒂芬是正确的 – 假期是联邦和地区界定的。 您需要创build一个表来存储date并在计算中引用它们。

 DELIMITER // DROP FUNCTION IF EXISTS NETWORKDAYS// CREATE FUNCTION NETWORKDAYS(first_date DATE, second_date DATE) RETURNS INT LANGUAGE SQL DETERMINISTIC BEGIN DECLARE start_date DATE; DECLARE end_date DATE; DECLARE diff INT; IF (first_date < second_date) THEN SET start_date = first_date; SET end_date = second_date; ELSE SET start_date = second_date; SET end_date = first_date; END IF; SET diff = DATEDIFF(end_date, start_date); RETURN (diff + 1) - (FLOOR(diff / 7) * 2) - (CASE WHEN DAYNAME(start_date) = 'Sunday' THEN 1 ELSE 0 END) - (CASE WHEN DAYNAME(end_date) = 'Saturday' THEN 1 ELSE 0 END); END// DELIMITER ; 

– testingSELECT Networkdays('2009-12-06','2009-12-13');