应使用 weekday(now()) between 0 and 4 判断工作日,因 weekday 返回 0(周一)至 6(周日),语义清晰且稳定;限制 9:00–18:00 更新需用 hour(now()) between 9 and 17,避免包含 18:00 整点。

触发器里怎么判断当前是不是工作日?
MySQL 没有内置的「工作日」函数,得靠 WEEKDAY() 手动过滤:它返回 0(周一)到 6(周日),所以工作日就是 WEEKDAY(NOW()) BETWEEN 0 AND 4。别用 DAYOFWEEK(),它返回 1(周日)到 7(周六),逻辑容易反。
常见错误是写成 DAYOFWEEK(NOW()) IN (2,3,4,5,6),看似对,但周日是 1、周六是 7,中间 2–6 确实对应周一到周五——可一旦时区或系统设置异常,DAYOFWEEK() 行为可能偏移;WEEKDAY() 更稳定,且语义更直白。
怎么限制在 9:00–18:00 更新?
用 HOUR(NOW()) 提取当前小时数,配合 BETWEEN 9 AND 17(注意:18:00 是边界,更新动作发生在 18:00:00 时,HOUR() 仍返回 18,所以要写成 9 才真正卡死在 17:59:59 前)。
- 错误写法:
HOUR(NOW()) BETWEEN 9 AND 18→ 允许 18:00:00–18:59:59 的更新 - 正确写法:
HOUR(NOW()) >= 9 AND HOUR(NOW()) ,再加分钟校验更稳妥:<code>TIME(NOW()) >= '09:00:00' AND TIME(NOW()) - 注意:触发器中用
NOW()而不是CURRENT_TIMESTAMP,二者在大多数场景等价,但NOW()更明确表达“此刻”,避免某些复制环境下的歧义
UPDATE 触发器里怎么阻止非法操作?
必须用 BEFORE UPDATE,且在不满足条件时主动报错——MySQL 触发器不能用 RETURN 或跳过,只能靠 SIGNAL 中断执行。
示例片段:
DELIMITER $$
CREATE TRIGGER chk_work_hours
BEFORE UPDATE ON orders
FOR EACH ROW
BEGIN
IF WEEKDAY(NOW()) NOT BETWEEN 0 AND 4
OR TIME(NOW()) '17:59:59' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Updates allowed only on weekdays 09:00–17:59';
END IF;
END$$
DELIMITER ;
关键点:
-
SIGNAL SQLSTATE '45000'是标准用户自定义错误码,应用层能捕获并区分 - 别用
INSERT INTO ... SELECT或其他绕过方式模拟“拒绝”,那只是掩耳盗铃,数据仍可能被改 - 触发器对批量更新(如
UPDATE ... WHERE id IN (1,2,3))同样生效,每行都单独校验
这个方案有哪些隐藏风险?
最常被忽略的是时区——NOW() 返回的是 MySQL 服务器当前会话时区的时间,不是应用服务器或用户本地时间。如果 DB 部署在 UTC 时区,而业务要求按北京时间(UTC+8)控制,就必须提前统一时区:
- 建库/建表时指定
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci不解决时区问题 - 检查
SELECT @@time_zone;,确保是'+08:00'或'Asia/Shanghai' - 若无法改全局时区,可在触发器里用
CONVERT_TZ(NOW(), '+00:00', '+08:00')转换,但性能略降,且依赖时区表已加载 - 另一个坑:触发器不拦截通过
LOAD DATA INFILE或物理备份恢复的数据变更,这类操作绕过 SQL 层
真正在生产用,还得配合应用层二次校验,毕竟触发器只是最后一道闸门。











