应使用 DATEPART(WEEKDAY, GETDATE()) BETWEEN 2 AND 6 且 DATEPART(HOUR, GETDATE()) >= 9 AND DATEPART(HOUR, GETDATE()) < 18 来准确拦截非工作时间修改,因 GETDATE() 返回服务器本地时间,且需排除18点整之后。

SQL Server 中用 DATEPART 判断工作日与工作时间
触发器本身不区分“用户意图”,只响应 INSERT/UPDATE/DELETE 操作。要拦住非工作时间的修改,核心是获取当前服务器时间并校验:是否为周一至周五(DATEPART(WEEKDAY, GETDATE()) 返回 1=周日、2=周一…7=周六),且小时是否在 9–18 之间(含边界需明确)。注意:GETDATE() 返回的是数据库服务器本地时间,不是客户端时间——这点常被忽略,若服务器时区与业务所在地不一致,规则会失效。
常见错误是写成 DATEPART(HOUR, GETDATE()) BETWEEN 9 AND 18,但这样会放过 18:00:01 及之后的修改。更稳妥的做法是:DATEPART(HOUR, GETDATE()) >= 9 AND DATEPART(HOUR, GETDATE()) ,再配合分钟判断(如要求严格到 9:00–17:59)。
PostgreSQL 触发器中必须用 CURRENT_TIME 而非 NOW()
PostgreSQL 的 NOW() 返回带时区的时间戳(TIMESTAMP WITH TIME ZONE),而 CURRENT_TIME 直接返回不带日期的本地时间(TIME WITHOUT TIME ZONE),更适合做小时/分钟比对。若误用 NOW(),还得额外 EXTRACT(HOUR FROM NOW()),且可能因时区转换出错。
实操建议:
- 用
EXTRACT(DOW FROM CURRENT_TIMESTAMP)判断星期几(0=周日,1=周一…6=周六) - 用
CURRENT_TIME BETWEEN '09:00' AND '17:59'判断时间段,注意字符串格式必须匹配TIME类型 - 触发器函数返回
NULL可中止操作(BEFORE触发器),但需确保函数定义为RETURNS TRIGGER
MySQL 触发器里不能直接调用 NOW() 做条件判断?
可以调用,但 MySQL 在 BEFORE UPDATE 或 BEFORE INSERT 触发器中禁止修改 NEW 行以外的数据,而时间判断本身不违反这条。真正容易踩的坑是:MySQL 默认不支持在触发器中使用 SIGNAL 抛出错误(5.5+ 支持,但部分旧部署仍禁用)。若无法用 SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Off-hours edit denied';,就得退回到“静默丢弃”——即设 NEW.col = OLD.col 强制回滚字段值,但这会让应用层误以为更新成功。
更可靠的做法:
- 确认 MySQL 版本 ≥ 5.5 且
sql_mode包含STRICT_TRANS_TABLES - 用
DAYOFWEEK(NOW())(1=周日,2=周一…7=周六)配合HOUR(NOW()) - 避免用
BETWEEN,改用HOUR(NOW()) >= 9 AND HOUR(NOW()) ,再加 <code>MINUTE(NOW()) (冗余但显式)
所有数据库都绕不开的权限与调试盲区
触发器由执行 SQL 的用户身份触发,但其代码运行在数据库上下文,不受应用层 session 时区影响——所以务必确认数据库服务器系统时区已设为业务所在时区(如 Asia/Shanghai),而不是默认的 UTC。Linux 上可通过 timedatectl status 查,Windows 看服务属性。
另一个关键点:DBA 或具有 TRIGGER 权限的账号创建的触发器,普通用户无法看到或调试。一旦规则生效后出现“莫名更新失败”,排查路径只能是查 sys.triggers(SQL Server)、pg_trigger(PostgreSQL)或 information_schema.TRIGGERS(MySQL),再结合错误日志定位。别指望应用日志里会有触发器拦截的提示。

















