必须用after insert, update, delete触发器+getdate()判断小时段,因hour(getdate()) between 22 and 6逻辑矛盾(22≤x≤6无解),正确应为hour(getdate())>=22 or hour(getdate())

直接结论:必须用 AFTER INSERT, UPDATE, DELETE 触发器 + GETDATE() 判断小时段,不能依赖应用层或简单 BETWEEN 表达式。
为什么 HOUR(GETDATE()) BETWEEN 22 AND 6 永远不生效
这是最常踩的坑。SQL Server 的 HOUR() 返回 0–23 的整数,BETWEEN 22 AND 6 要求值同时 ≥22 且 ≤6——这在数学上不可能成立。实际想表达的是“22:00 至次日 06:00”,逻辑上是跨天的两个区间:HOUR(GETDATE()) >= 22 OR HOUR(GETDATE()) 。
其他易错点包括:
- 用
DATEPART(HOUR, GETDATE())和HOUR(GETDATE())效果一致,但别混用不同函数名造成可读混乱 - 忽略时区:确保 SQL Server 实例的系统时区与业务时区一致(查
SELECT SYSDATETIMEOFFSET()) - 没覆盖
UPDATE场景中的部分列更新——触发器仍会触发,必须全量拦截
如何在 INSERT/UPDATE/DELETE 触发器里中止操作
SQL Server 不支持 RETURN 或布尔中断,唯一可靠方式是抛出错误并回滚当前事务。推荐用 RAISERROR 配合 ROLLBACK TRANSACTION:
CREATE TRIGGER tr_block_offhours ON Orders
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
IF DATEPART(HOUR, GETDATE()) >= 22 OR DATEPART(HOUR, GETDATE())
<p>注意:</p>
- 错误级别设为 16(用户可修复错误),避免用 20+ 级别导致连接中断
- 必须显式写
ROLLBACK TRANSACTION,仅RAISERROR不会自动回滚 - 不要用
PRINT或SELECT替代——它们不中断执行
如何验证触发器是否真正生效且不影响正常业务
上线前务必实测三类边界场景:
- 在 21:59 执行
UPDATE→ 应成功;22:00 执行 → 应报错并回滚 - 用不同客户端测试:SSMS、
sqlcmd、应用程序直连,确认绕不过 - 检查事务嵌套:若触发器在存储过程中被调用,确保外层事务能感知到
ROLLBACK
临时禁用可用:DISABLE TRIGGER tr_block_offhours ON Orders;启用则用 ENABLE TRIGGER。禁用后记得验证 sys.triggers.is_disabled = 1。
比触发器更隐蔽的风险点:登录触发器可能干扰维护操作
如果同时部署了基于时间的登录触发器(如限制 sa 只能在工作时间登录),它会在连接建立时就拦截,比 DML 触发器更早生效。这时候即使你用 DAC(专用管理员连接)进去了,也可能因登录触发器未放行而无法操作。
真正麻烦的是:这类触发器一旦写错,可能导致全员无法登录。所以务必提前留好白名单 IP + 本地 <local machine></local>,并用 sqlcmd -A -S servername 验证 DAC 是否可用。










