MySQL函数查找两个日期之间的工作日数

Yad*_*ada 37 mysql sql

Excel具有NETWORKDAYS()函数,可以查找两个日期之间的工作日数.

任何人都有类似MySQL的功能?由于假期增加了复杂性,解决方案不必处理假期.

小智 66

这个表达 -

5 * (DATEDIFF(@E, @S) DIV 7) + MID('0123444401233334012222340111123400012345001234550', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1)
Run Code Online (Sandbox Code Playgroud)

计算开始日期@S和结束日期@E之间的工作日数.

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

数字串的构造如下.创建一个开始日和结束日的表,行必须以星期一(WEEKDAY 0)开头,列也必须以星期一开始.从左上角到右下角填写对角线,全部为0(即星期一到星期一,星期二和星期二之间有0个工作日,等等).对于每天从对角线开始(必须始终为0)并填写右侧的列,一次一天.如果您在周末(非工作日)列登陆,则工作日数不会更改,而是从左侧开始.否则,工作日数会增加一个.当你到达行循环的末尾回到同一行的开头并继续直到你再次到达对角线.然后继续下一行.

例如,假设周六和周日不是工作日 -

 | M T W T F S S
-|--------------
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
Run Code Online (Sandbox Code Playgroud)

然后将表中的49个值连接到字符串中.

如果您发现任何错误,请告诉我.

-Edit改进表:

 | M T W T F S S
-|--------------
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
Run Code Online (Sandbox Code Playgroud)

改进的字符串:'0123444401233334012222340111123400001234000123440'

表达改善:

5 * (DATEDIFF(@E, @S) DIV 7) + MID('0123444401233334012222340111123400001234000123440', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1)
Run Code Online (Sandbox Code Playgroud)

  • 正确的字符串是`0123455501234445012333450122234501101234000123450` (5认同)
  • 这是解决这个问题的一个很好的方法,当我第一次实现它时似乎是正确的,但我发现了一些问题.实际上,当您尝试在以下日期集中使用它时,会得到不正确的结果:`(星期日,2012-08-26 - >星期一,2012-08-27),(星期五,2012-08-31 - >星期六,2012-09-01),(星期日,2012-09-02 - >星期一,2012-09-03),(星期五,2012-09-07 - >星期六,2012-09-08),(星期一, 2012-09-24 - > Sunday,2012-10-07)`基本上,周末价值没有得到妥善解释.通过对矩阵进行一点调整,您就可以对其进行定位. (3认同)
  • @ argentum47我在结尾添加了一个"+ 1"5*(DATEDIFF(@ E,@ S)DIV 7)+ MID('0123444401233334012222340111123400001234000123440',7*WEEKDAY(@S)+ WEEKDAY(@E)+ 1,1 )我从2015年7月到2015年测试它,它的功能就像一个魅力 (2认同)
  • @Yada感谢您接受它,但穴居人是正确的(方法如下),正确的答案似乎是使用0123455401234434434012332340122123401101234000123450字符串(实际上也不是Bryan)。检查原始答案的第一行:0 1 2 3 4 4 4; 这意味着从星期一到星期五有4个工作日(正确),但从星期一到星期六有4个工作日(不正确,有5个工作日,即星期一,星期二,星期三,星期四,星期五)。 (2认同)
  • @sebastien.b 您能修复更正后的字符串吗?这仍然是错误的。最后的数字应该是 5 0,而不是 4 0。并且请使矩阵的_right_版本更加突出,甚至删除错误的版本,这样人们就不会错误地复制/粘贴错误的版本,因为他们不是阅读或理解一切。 (2认同)

Bry*_*hty 14

该解决方案使用与Rodger基本相同的方法,除了生成矩阵的方法要复杂得多.注意:此解决方案的此输出与NETWORKDAYS不兼容.

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

这与Rodger的解决方案之间的主要区别在于矩阵和结果的数字串是由一个我没有包含的复杂算法构造的.该算法的输出由单元测试验证(参见下面的测试输入和输出).在矩阵中,任何给定的x和y值对(WEEKDAY(@S)和WEEKDAY(@E)的交集产生两个值之间的工作日差异.分配顺序实际上并不重要,因为两者被加在一起绘制位置.

营业日为周一至周五

 | M T W T F S S
-|--------------
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
Run Code Online (Sandbox Code Playgroud)

表中的49个值连接成以下字符串:

0123455501234445012333450122234501101234000123450
Run Code Online (Sandbox Code Playgroud)

最后,正确的表达是:

5 * (DATEDIFF(@E, @S) DIV 7) + MID('0123455501234445012333450122234501101234000123450', 7 * WEEKDAY(@S) + WEEKDAY(@E) + 1, 1)
Run Code Online (Sandbox Code Playgroud)

我已使用此解决方案验证了以下输入和输出:

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
Run Code Online (Sandbox Code Playgroud)

  • 您使用的矩阵认为一天与自身之间有0个工作日.如果要从Excel重现NETWORKDAYS函数,则要使用的字符串应为:"1234555512344445123333451222234511112345001234550". (4认同)
  • 不,这个答案是错误的。请参阅下面@Caveman 的答案以获得正确的答案。 (2认同)

Tom*_*m H 13

由于您需要在某处跟踪假期,因此Calendar表似乎合适:

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)
)
Run Code Online (Sandbox Code Playgroud)

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

那时候你需要的查询变得微不足道:

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

警告:我主要使用MS SQL并且长时间没有使用MySQL,所以你可能需要调整上面的内容.例如,我甚至不记得MySQL是否具有BIT数据类型.

  • 最好只将假期存储在表格中.通过一些计算,你总能知道哪些日子是工作日和周末. (2认同)

Cav*_*man 10

提议的字符串可能是错的吗?

DATEDIFF(from,to)排除'to'.这个字符串应该是这样的:

星期一 - >星期五= {星期一,土族,星期三,星期四} = 4

星期一 - >星期六= {星期一,涂,星期三,星期五,星期五} = 5

星期二 - >星期一= {涂,星期三,星期五,星期五,跳过星期六,跳过太阳,星期一被排除} = 4

等等

拟议矩阵:

 | M T W T F S S
-|--------------
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
Run Code Online (Sandbox Code Playgroud)

字符串:'0123455401234434012332340122123401101234000123450'

我错过了什么吗?:)


Pau*_*eno 8

仅供进一步参考.以上都不适用于我,但@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)
Run Code Online (Sandbox Code Playgroud)


小智 5

给定一个月的第一天,这将返回该月内的工作日数。在 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)
Run Code Online (Sandbox Code Playgroud)


小智 5

SELECT  5* (DATEDIFF(u.EndDate, u.StartDate) DIV 7) + MID('1234555512344445123333451222234511112345001234550', 7 * WEEKDAY(u.StartDate) + WEEKDAY(u.EndDate) + 1, 1)
Run Code Online (Sandbox Code Playgroud)

这时您需要考虑以下情况:

1) 如果开始日期 = 结束日期,持续时间 = 1 ,同样..

我使用投票最多的答案中提到的逻辑计算了字符串,并得到了我需要的结果。