mysql无内置工作日函数,需自建holidays表(含h_date和is_off字段)并创建workdays_between存储函数,用weekday()逐日判断周一至周五及节假日/补班日,但性能差仅适用于≤30天范围。

MySQL 里没有内置工作日天数函数,得自己写
MySQL 原生不提供类似 NETWORKDAYS() 的函数,DATEDIFF() 只算总天数,不剔除周末和节假日。想精确统计两个日期间的工作日(周一至周五,且排除法定节假日),必须用存储函数 + 循环 + 节假日表配合实现。
必须建一张节假日表,否则无法准确排除
节假日是动态的(调休、临时公告),硬编码进函数里不可维护。推荐建一张最小化结构的表:
CREATE TABLE holidays ( h_date DATE PRIMARY KEY, is_off TINYINT DEFAULT 1 -- 1 表示放假(非工作日),0 表示补班(算工作日) );
然后插入已知节假日,例如:
INSERT INTO holidays VALUES
('2024-01-28', 1), -- 春节前调休放假
('2024-02-04', 0), -- 春节后补班(周日上班,算工作日)
('2024-10-01', 1); -- 国庆当天放假
- 表名和字段名必须与函数内一致,否则查不到
-
h_date必须是DATE类型且有主键或唯一索引,否则循环中SELECT ... INTO可能返回多行报错 - 不要只存“放假日”,一定要包含“补班日”(
is_off = 0),否则周日补班会被误判为休息日
用 WHILE 循环逐日判断,避开 WEEKDAY() 和 DAYOFWEEK() 的坑
MySQL 的 WEEKDAY() 返回 0(周一)到 6(周日),而 DAYOFWEEK() 返回 1(周日)到 7(周六)——混用会导致周末判断翻车。统一用 WEEKDAY(date) 更直观:
DELIMITER $$ CREATE FUNCTION workdays_between(start_date DATE, end_date DATE) RETURNS INT READS SQL DATA DETERMINISTIC BEGIN DECLARE days INT DEFAULT 0; DECLARE current DATE DEFAULT start_date; DECLARE wd INT DEFAULT 0; DECLARE is_holiday TINYINT DEFAULT 0; <p>IF start_date > end_date THEN RETURN 0; END IF;</p><p>WHILE current </p><p>RETURN days; END$$ DELIMITER ;</p>
- 函数必须声明
READS SQL DATA,否则不能查询holidays表 - 用
IFNULL(is_off, 1)处理“查不到节假日记录”的情况:默认当它是放假(1),但实际要的是“非节假日 → 工作日”,所以这里逻辑是反的;更稳妥写法是先查是否存在,再分支,但上面已用IFNULL简化 - 循环从
start_date到end_date包含两端,符合多数业务场景(如 5.1 到 5.3 共 3 天,需判断这三天是否都算)
性能差是必然的,别在大数据量或高频场景直接调用
这个函数对每个日期都查一次 holidays 表 + 一次 WEEKDAY() 计算,N 天就要 N 次单行查询。如果传入的是跨月/跨年范围(比如 2023-01-01 到 2024-12-31),会执行 730 次循环 —— 在 WHERE 条件里用它会导致全表扫描+嵌套循环,响应可能秒变几秒甚至超时。
- 只适合单次计算少量日期(≤30 天),或离线汇总用
- 线上接口中调用前,先用
start_date和end_date做简单校验,避免传入超大范围 - 真要高性能,得把逻辑移到应用层(Python/Java),用内存缓存节假日集合,再结合
date.weekday()算;或者预生成「日期维度表」,加is_workday字段,用 JOIN 替代循环
函数写完记得测试边界:跨周末、含节假日当天、起止日相同、起始日颠倒 —— 尤其注意补班日逻辑是否被正确识别,这是最容易漏测也最常出问题的地方。











