sql触发器不支持where子句,必须在触发器体内用if判断new/old值实现条件控制;postgresql用tg_op+new/old,mysql用if+new/old,sql server用inserted/deleted表配合select。

触发器里怎么写 WHERE 条件?
SQL 触发器本身不支持 WHERE 子句——它没有“只对某几行生效”的语法糖。触发器是基于事件(INSERT、UPDATE、DELETE)触发的,只要事件发生就执行,**是否处理某行,得靠你在触发器体内部手动判断**。
常见错误是以为可以这样写:CREATE TRIGGER ... ON t AFTER UPDATE WHERE status = 'pending'——这在标准 SQL(包括 PostgreSQL、SQL Server、MySQL 8.0+)中直接报错。
正确做法是:在触发器函数/体中,用 NEW(或 OLD)伪记录做条件过滤。
PostgreSQL 中用 NEW 和 IF 控制逻辑分支
PostgreSQL 的行级触发器(FOR EACH ROW)会为每一行调用一次触发器函数,因此你可以在函数里安全地检查该行字段值。
示例:仅当更新后 status 变为 'shipped' 时才发通知:
CREATE OR REPLACE FUNCTION notify_on_shipped()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'UPDATE' AND NEW.status = 'shipped' AND OLD.status != 'shipped' THEN
PERFORM pg_notify('shipments', json_build_object('id', NEW.id)::text);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
注意点:
-
NEW和OLD在INSERT时OLD为 NULL,DELETE时NEW为 NULL,UPDATE两者都非空 - 必须显式
RETURN NEW(BEFORE)或RETURN NULL(AFTER不影响数据,但语法要求返回值) - 别漏掉
TG_OP判断操作类型,否则INSERT时访问OLD.status会报错
MySQL 8.0+ 的 BEFORE UPDATE 中用 IF 跳过逻辑
MySQL 不支持函数式触发器体,但允许在触发器中写 IF 语句。关键是:**即使不满足条件,触发器仍会执行,只是你不做任何事**。
示例:仅当 amount > 1000 时插入审计日志:
CREATE TRIGGER audit_large_payment
BEFORE UPDATE ON orders
FOR EACH ROW
BEGIN
IF NEW.amount > 1000 AND NEW.amount != OLD.amount THEN
INSERT INTO payment_audits (order_id, old_amount, new_amount, updated_at)
VALUES (NEW.id, OLD.amount, NEW.amount, NOW());
END IF;
END;
容易踩的坑:
- MySQL 触发器中不能调用存储函数以外的外部服务(比如 HTTP 请求),所以“发邮件”“调 webhook”得靠应用层或定时任务补位
- 不要在触发器里写耗时操作(如大表 JOIN 或子查询),会拖慢主 DML 事务
-
NEW字段修改只在BEFORE触发器中有效;AFTER中改NEW没意义
SQL Server 的 INSTEAD OF 和 IF UPDATE() 配合使用
SQL Server 提供 IF UPDATE(column) 检测某列是否被修改,但这是粗粒度的——它不告诉你新值是什么。真要按值过滤,还得查 inserted 和 deleted 临时表。
示例:仅当 price 上涨超过 10% 时记录预警:
CREATE TRIGGER tr_check_price_hike ON products AFTER UPDATE AS BEGIN INSERT INTO price_warnings (product_id, old_price, new_price, warning_time) SELECT i.id, d.price, i.price, GETDATE() FROM inserted i INNER JOIN deleted d ON i.id = d.id WHERE i.price > d.price * 1.1; END;
关键提醒:
-
inserted/deleted是表,不是单行,必须用SELECT+JOIN处理多行场景 - 别在触发器里用
@@ROWCOUNT判断“是否更新了”,因为可能有 0 行满足条件,但触发器仍被调用 - SQL Server 触发器默认是语句级(
AFTER),天然支持批量,这点比 MySQL/PG 更省心
最常被忽略的是:触发器无法规避约束冲突,也无法绕过权限检查。如果条件逻辑涉及跨表校验或复杂业务规则,优先考虑应用层控制——触发器适合轻量、强一致性、副作用明确的场景,比如审计、状态快照、简单派生字段维护。










