MySQL的DAYOFWEEK()函数以周日为1,即1=周日、2=周一……7=周六;判断周一至周五应使用DAYOFWEEK(date) BETWEEN 2 AND 6。

DAYOFWEEK 返回值到底是周日算 1 还是周一算 1?
MySQL 的 DAYOFWEEK() 函数返回的是基于“周日为 1”的编号体系,即:1 = 周日,2 = 周一,…,7 = 周六。这点和很多人直觉(周一为 1)相反,也是最容易写错逻辑的地方。
常见错误现象:用 DAYOFWEEK(date) IN (2,3,4,5,6) 判断工作日,结果把周一到周五当成了工作日——但其实它确实就是周一到周五(对应 2–6),只是你得确认自己没把周日(1)或周六(7)误包含进去。
- 如果业务要求「周一至周五为工作日」,正确范围是
DAYOFWEEK(date) BETWEEN 2 AND 6 - 如果系统 locale 或业务规则把周日当作工作日(如某些轮班制),就不能硬套这个范围,得显式列出,比如
DAYOFWEEK(date) IN (1,2,3,4,5) - 注意:PostgreSQL、SQL Server 没有
DAYOFWEEK(),它们用EXTRACT(DOW FROM date)或DATEPART(WEEKDAY, date),且默认起始日可能不同(PostgreSQL 是周日=0)
在 WHERE 条件里直接过滤工作日数据
最常见需求:查出某张订单表里所有工作日产生的记录。直接在 WHERE 中用 DAYOFWEEK() 是可行的,但要注意索引失效风险。
示例(查 2024 年所有工作日订单):
SELECT * FROM orders WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01' AND DAYOFWEEK(order_date) BETWEEN 2 AND 6;
- 这个查询无法走
order_date上的普通 B-tree 索引,因为DAYOFWEEK()是函数调用,会阻止索引范围扫描 - 如果数据量大且查询频繁,建议建函数索引(MySQL 8.0.13+ 支持):
CREATE INDEX idx_workday ON orders ( (DAYOFWEEK(order_date)) ); - 更稳妥的做法是加一个生成列
is_weekday TINYINT AS (CASE WHEN DAYOFWEEK(order_date) BETWEEN 2 AND 6 THEN 1 ELSE 0 END),再对它建索引
用 CASE 配合 DAYOFWEEK 输出中文星期和工作日标识
报表或导出时经常要显示「星期几」和是否工作日,CASE + DAYOFWEEK() 是最直接方式。
示例:
SELECT
order_date,
CASE DAYOFWEEK(order_date)
WHEN 1 THEN '星期日'
WHEN 2 THEN '星期一'
WHEN 3 THEN '星期二'
WHEN 4 THEN '星期三'
WHEN 5 THEN '星期四'
WHEN 6 THEN '星期五'
WHEN 7 THEN '星期六'
END AS weekday_name,
CASE WHEN DAYOFWEEK(order_date) BETWEEN 2 AND 6 THEN '是' ELSE '否' END AS is_workday
FROM orders;- 别漏掉
WHEN 7,否则周六会变成 NULL - 如果只关心是否工作日,用
IF(DAYOFWEEK(order_date) BETWEEN 2 AND 6, '是', '否')更简洁 - 注意:
CASE表达式本身不提升性能,纯属展示用途;别在 JOIN 条件或子查询 WHERE 里滥用它来替代原生日期逻辑
DAYOFWEEK 和 WEEKDAY 函数有什么区别?
MySQL 还提供 WEEKDAY(),它返回「周一为 0」的编号:周一=0,周二=1,…,周日=6。两者语义不同,混用会导致逻辑翻车。
-
DAYOFWEEK('2024-06-10')(周一)→ 返回2 -
WEEKDAY('2024-06-10')(周一)→ 返回0 - 判断工作日时,用
WEEKDAY()更符合程序员直觉:WEEKDAY(date) BETWEEN 0 AND 4 - 但团队已有代码大量使用
DAYOFWEEK(),就别为了“顺眼”临时切换,一致性比编号习惯更重要
真正容易被忽略的是:这两个函数对 NULL 输入都返回 NULL,如果日期字段允许为空,记得加 IS NOT NULL 判断,否则过滤结果会意外丢失部分行。

















