大家好,我是苏承栈。今天我们来聊聊如何利用SQL触发器自动记录数据修改,打造高效的审计日志。
在MySQL和PostgreSQL中,触发器的使用有一些细节需要注意。比如,MySQL触发器中禁止对正在修改的表执行SELECT或INSERT操作,必须显式引用OLD/NEW字段。而在PostgreSQL的AFTER触发器中,必须返回NEW或OLD。此外,审计时间应统一使用CURRENT_TIMESTAMP并配合适当时区类型。
MySQL触发器使用技巧
在MySQL触发器里,你不能直接使用INSERT ... SELECT直接读当前表。如果你想在一个UPDATE触发器里把旧值写进审计表,比如这样的代码:
INSERT INTO audit_log SELECT OLD.id, OLD.name FROM t_user;
会报错 ERROR 1442 (HY000): Can't update table 't_user' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.。MySQL禁止触发器里再查或改自己正在被修改的表。
实操建议:所有需要记录的字段,必须显式写出OLD.col1、OLD.col2,不能用SELECT * FROM ...。如果字段多且常变,建议在应用层拼SQL或用视图预处理,别硬塞进触发器。
PostgreSQL触发器使用技巧
在PostgreSQL中,AFTER触发器必须返回NEW或OLD。不像MySQL那样允许AFTER触发器只做日志不返回值。如果你写了个AFTER UPDATE触发器函数但末尾没写RETURN NEW;,执行更新时会报ERROR: trigger procedure did not return a value,而且整个事务会回滚。
实操建议:BEFORE触发器可修改NEW并返回它(比如自动更新updated_at);AFTER触发器只需返回原值即可,别漏掉函数体末尾统一加RETURN NEW;(UPDATE/INSERT)或RETURN OLD;(DELETE),哪怕你只记日志别在触发器里调RAISE EXCEPTION做业务校验——审计日志该记还得记,异常该抛还得抛,两者逻辑要分开。
审计字段时间戳要用CURRENT_TIMESTAMP而非NOW()(尤其跨时区部署)。很多触发器里写INSERT INTO audit_log (...) VALUES (..., NOW(), ...),上线后发现日志时间比实际操作晚8小时。问题出在NOW()返回的是会话时区时间,而数据库服务器、应用服务器、客户端可能各有时区设置;CURRENT_TIMESTAMP才是SQL标准定义的“语句开始时刻的UTC时间”,配合TIMESTAMP WITH TIME ZONE类型才能真正对齐。
总结一下,使用SQL触发器自动记录数据修改,关键在于理解并正确使用OLD/NEW字段,以及注意时区问题。
我是苏承栈,如果你对数据库编程还有其他疑问,欢迎关注极星编程网(www.jxgpc.com)了解更多内容。
