应拆分为两个条件:HOUR(NOW()) >= 22 OR HOUR(NOW())
触发器里怎么判断“当前是否在禁止删除时段”
不能靠
BETWEEN '22:00' AND '06:00'这种写法——跨天范围在 MySQL 里会永远为 false。正确逻辑是拆成两个独立条件:HOUR(NOW()) >= 22或HOUR(NOW()) ,再用OR连接。更稳妥的做法是把时间判断下沉到秒级:
NOW() >= TIMESTAMP(CONCAT(CURDATE(), ' 22:00:00')) OR NOW() ,避免跨天时CURDATE()变更导致边界错位。常见错误现象:
DELETE FROM orders WHERE id = 123在 23:59 执行成功,但 00:01 又被允许——其实是没覆盖“次日 00:00–06:00”这个区间,只查了当天的 22:00 后。BEFORE DELETE 触发器中抛错必须用 SIGNAL
MySQL 5.7+ 支持
SIGNAL SQLSTATE '45000'主动中断操作;低版本只能用INSERT INTO nonexistent_table强制报错,不推荐。注意点:
SIGNAL必须配合DECLARE CONTINUE HANDLER FOR SQLEXCEPTION才能生效,否则只是抛错不中断(尤其在存储过程嵌套调用时)- 错误信息要带业务语义,比如
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Deletion prohibited between 22:00 and 06:00',方便应用层识别- 不要在触发器里写
ROLLBACK—— 触发器本身不能控制事务,SIGNAL就够了为什么不能在触发器里查当前表做权限校验
比如想检查用户是否有“夜间删除”白名单,写成
SELECT 1 FROM delete_whitelist WHERE user_id = OLD.user_id,这在BEFORE DELETE里会直接报错:ERROR 1442 (HY000): Can't update table 'orders' in stored function/trigger...。
MySQL8 CRM Schema Design Expert下载面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
根本原因是:MySQL 禁止在触发器中对“正在被 DML 操作的同一张表”执行 SELECT(即使是只读),除非该查询走的是主键或唯一索引且明确加了
FOR UPDATE(但这里不适用)。可行替代方案:
- 把白名单数据缓存在内存表(
CREATE TEMPORARY TABLE)或配置表(独立小表 + 复合索引)- 改用应用层鉴权,触发器只做时间兜底
- 用 MySQL 8.0+ 的角色权限机制,在连接层控制
DELETE权限(需配合SET ROLE动态切换)跨时区部署时 CURRENT_TIMESTAMP 和 NOW() 的区别
如果数据库服务器时区设为
+00:00,但业务面向中国用户(+08:00),直接用NOW()判断 22:00–06:00 就会全错。正确做法是统一换算到业务时区:
- 先确认 session 时区:
SELECT @@session.time_zone- 显式转时区:
CONVERT_TZ(NOW(), '+00:00', '+08:00'),再取HOUR()- 或者更简单:让应用层传入带时区的时间戳字段(如
delete_at),触发器直接查OLD.delete_at值,避免依赖服务端时钟真正容易被忽略的是:事件调度器(EVENT)、触发器、客户端连接三者的时区可能不一致,单靠
NOW()做判断,在灰度发布或分库场景下极易漏控。












