触发器需手动比对OLD/NEW值判断字段是否变更,注意NULL处理:PostgreSQL用IS DISTINCT FROM,MySQL用“!= OR (IS NULL) != (IS NULL)”,SQL Server建议用CASE或ISNULL;须防递归与误触发。

触发器如何判断具体哪个字段被修改了
SQL 触发器本身不自动知道“哪个字段变了”,必须手动比对 OLD 和 NEW(或 INSERTED/DELETED,取决于数据库)中的值。核心逻辑就是:只在 OLD.column_name != NEW.column_name 时执行后续动作。
注意 NULL 的处理——直接用 != 比较会返回 UNKNOWN,导致条件失效。必须显式用 IS DISTINCT FROM(PostgreSQL)或 IS NULL + IS NOT NULL 组合(MySQL/SQL Server)。
- PostgreSQL 推荐写法:
OLD.status IS DISTINCT FROM NEW.status - MySQL 写法:
OLD.status != NEW.status OR (OLD.status IS NULL) != (NEW.status IS NULL) - SQL Server 写法:
ISNULL(OLD.status, '') != ISNULL(NEW.status, '')(仅适用于字符串;数值类型建议用CASE WHEN OLD.status IS NULL AND NEW.status IS NULL THEN 0 ELSE ... END)
MySQL 中 UPDATE 触发器只响应 name 字段变更的写法
MySQL 不支持 IS DISTINCT FROM,且 OLD/NEW 只在 BEFORE UPDATE 和 AFTER UPDATE 中可用。常见错误是把条件写在触发器体外(语法不允许),或漏掉 NULL 判断。
CREATE TRIGGER tr_user_name_updated
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
IF OLD.name != NEW.name
OR (OLD.name IS NULL) != (NEW.name IS NULL) THEN
INSERT INTO user_name_log(user_id, old_name, new_name, updated_at)
VALUES (NEW.id, OLD.name, NEW.name, NOW());
END IF;
END;
关键点:必须用括号包裹整个 NULL 判断表达式,否则 MySQL 解析出错;!= 对 NULL 直接比较恒为 FALSE,所以必须拆开判断。
PostgreSQL 中用 WHEN 子句精简触发器逻辑
PostgreSQL 支持在 CREATE TRIGGER 语句里加 WHEN 条件,让触发器只在满足条件时调用函数,避免进入函数体再判断,性能更好也更清晰。
CREATE OR REPLACE FUNCTION log_email_change() RETURNS TRIGGER AS $$ BEGIN INSERT INTO email_change_log(user_id, old_email, new_email) VALUES (NEW.id, OLD.email, NEW.email); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tr_user_email_updated AFTER UPDATE OF email ON users FOR EACH ROW WHEN (OLD.email IS DISTINCT FROM NEW.email) EXECUTE FUNCTION log_email_change();
这里两个关键设计:AFTER UPDATE OF email 限定只监听 email 字段(但不保证它真变了),WHEN 子句才做实际值比对。两者配合,既减少触发次数,又避免误触发。
SQL Server 中避免触发器无限递归的坑
SQL Server 默认允许触发器内修改同表数据,如果没关掉 RECURSIVE_TRIGGERS 或没加防护,UPDATE 触发器里再 UPDATE 当前表,可能引发循环触发。尤其当你在触发器里更新一个字段,而这个字段恰好也是触发条件字段时,风险极高。
- 先查当前会话是否已处于该触发器中:
IF TRIGGER_NESTLEVEL(OBJECT_ID('tr_order_status')) > 1 RETURN - 或用
CONTEXT_INFO打标记(需在触发器开头设、结尾清) - 更稳妥的做法:把业务逻辑移到存储过程中,触发器只负责发消息或写日志,不反向写库
另外,SQL Server 的 UPDATE() 函数只能判断字段是否出现在 SET 子句中(哪怕值没变),不能替代值比对——这点常被误用。

















