必须用BEFORE触发器或AFTER+RAISERROR+ROLLBACK,避开BETWEEN跨零点写法;MySQL用BEFORE UPDATE+SIGNAL SQLSTATE'45000'拦截,WEEKDAY返回0-6,禁用22:00–06:00的BETWEEN写法。

直接结论:必须用 BEFORE 类型触发器(MySQL/Oracle/PostgreSQL)或 AFTER + RAISERROR + ROLLBACK TRANSACTION(SQL Server),时间判断一律避开 BETWEEN 跨零点写法,否则逻辑永远不生效。
MySQL 中用 BEFORE UPDATE 拦非工作时间,必须 SIGNAL
MySQL 触发器不能靠返回值中断,SIGNAL SQLSTATE '45000' 是唯一可靠方式。常见错误是只写 SELECT 或 SET 变量却不抛错——那等于没拦。
-
WEEKDAY(NOW())返回 0(周一)到 6(周日),别和DAYOFWEEK()混用(它返回 1=周日) - 禁止 22:00–06:00 要写成:
HOUR(NOW()) >= 22 OR HOUR(NOW()) ,不是 <code>BETWEEN 22 AND 6 - 若需精确到分钟(如 22:30 开始禁写),补上
MINUTE(NOW())判断,例如:(HOUR(NOW()) = 22 AND MINUTE(NOW()) >= 30) OR HOUR(NOW()) > 22 OR HOUR(NOW()) - 触发器内禁止查当前表(
ERROR 1442),所以时间规则只能硬编码,不能从配置表读
SQL Server 中 AFTER 触发器必须三步齐备
SQL Server 没有 BEFORE,只能在 AFTER UPDATE 里“事后补救”,但必须三者同时存在:条件判断 → 报错 → 回滚 → 显式退出,缺一不可。
-
DATEPART(HOUR, GETDATE())获取小时,注意它返回 0–23;跨零点时段(如 22:00–06:00)必须拆成:DATEPART(HOUR, GETDATE()) >= 22 OR DATEPART(HOUR, GETDATE()) -
RAISERROR('msg', 16, 1)错误级别必须是 16,级别 20+ 会断开连接,级别 10 以下不中断执行 -
ROLLBACK TRANSACTION必须紧跟RAISERROR后,且不能被ELSE包裹,否则嵌套事务可能只回滚内层 - 批量更新时,用
IF EXISTS (SELECT 1 FROM inserted WHERE ...)判断,而不是简单IF ...,否则只检查第一行
Oracle 和 PostgreSQL 时间判断要绕开时区陷阱
Oracle 用 EXTRACT(HOUR FROM SYSDATE),PostgreSQL 用 EXTRACT(HOUR FROM CURRENT_TIMESTAMP),两者都返回整数小时,但底层时区依赖数据库设置,不是客户端传入的时间。
- Oracle 的
TO_CHAR(SYSDATE, 'D')结果受NLS_DATE_LANGUAGE影响,稳妥做法是:TO_CHAR(SYSDATE, 'D', 'NLS_DATE_LANGUAGE=AMERICAN')(1=周日) - PostgreSQL 的
NOW()带时区,比对时间建议用CURRENT_TIME(只取时间部分),避免时区转换干扰 - 跨零点判断统一用
OR:Oracle 写EXTRACT(HOUR FROM SYSDATE) >= 22 OR EXTRACT(HOUR FROM SYSDATE) ,PostgreSQL 写 <code>EXTRACT(HOUR FROM CURRENT_TIME) >= 22 OR EXTRACT(HOUR FROM CURRENT_TIME) - Oracle 在
BEFORE UPDATE FOR EACH ROW中调用RAISE_APPLICATION_ERROR会自动回滚该行,但不会影响外层事务——应用层必须捕获ORA-20001并决定是否整体回滚
最容易被忽略的点:所有数据库的 GETDATE()、NOW()、SYSDATE 都返回数据库服务器本地时间,不是你电脑上的时间,也不是 UTC。如果服务器时区设错了,或者 NTP 没同步,规则就形同虚设。上线前务必用 SELECT GETDATE()(SQL Server)、SELECT NOW()(PostgreSQL)、SELECT SYSDATE FROM DUAL(Oracle)确认服务器时间是否准确。


















