SQL触发器不能替代默认值约束,但能实现依赖其他字段、调用函数或跨表查询的自动补全;INSERT需在BEFORE阶段显式赋值NEW字段,UPDATE需防递归,跨字段拼接须处理NULL,且仅适用于DEFAULT无法覆盖的动态场景。

SQL触发器不能替代默认值约束,但能实现更灵活的“自动补全”逻辑——比如依赖其他字段、调用函数或跨表查询时。
INSERT 触发器里用 NEW 赋值,别用 SET DEFAULT
很多人误以为在触发器里写 SET NEW.column_name = DEFAULT 就能触发列的默认值逻辑,实际不会生效(尤其在 PostgreSQL 中会报错,在 MySQL 里也常被忽略)。正确做法是显式赋值:
- MySQL:直接给
NEW.column_name赋值,例如NEW.created_at := NOW() - PostgreSQL:同样用
NEW.column_name := now(),但注意必须在BEFORE INSERT阶段 - SQL Server:用
INSERTED是只读的,必须改用INSTEAD OF INSERT触发器才能修改插入值
UPDATE 触发器中避免无限递归
如果在 UPDATE 触发器里又执行了 UPDATE 当前表(比如补全 updated_by 同时又更新了 updated_at),可能引发递归调用。常见解法:
- 检查字段是否已非空:
IF NEW.updated_at IS NULL THEN NEW.updated_at := NOW(); END IF; - MySQL 8.0+ 可设
sql_log_bin = OFF(仅限当前会话)避免 binlog 循环,但慎用 - PostgreSQL 推荐用
pg_trigger_depth() = 1判断是否顶层触发
跨字段依赖补全要小心 NULL 传播
比如想让 full_name 自动拼接 first_name 和 last_name,但任一为 NULL 会导致整个结果为 NULL:
-- 错误(MySQL/PostgreSQL 都适用) NEW.full_name := NEW.first_name || ' ' || NEW.last_name; <p>-- 正确:用 COALESCE 或 IFNULL 防 NULL 中断 NEW.full_name := COALESCE(NEW.first_name, '') || ' ' || COALESCE(NEW.last_name, '');
另外,这种逻辑更适合放在视图或应用层;触发器里做字符串拼接,会影响批量插入性能。
真正需要触发器补全的,通常是那些无法用 DEFAULT 表达的场景:动态生成编号、根据用户 session 获取操作人、查关联表取分类名……这些地方一旦漏掉 BEFORE 关键字,或者在 AFTER 里试图改 NEW,值就再也写不进去了。

















