COALESCE是标准SQL函数,返回首个非NULL值,跨数据库兼容;IFNULL仅MySQL支持、仅两参数、类型处理受限且不兼容迁移。
触发器里直接用
会丢失原始值
很多人在写
触发器时,想“保持某字段不变”就写
,但若
本身是
,而客户端传入的
是空字符串或
,这一赋值反而把有意义的值覆盖成了
。这不是 bug,是 SQL 三值逻辑(
/
/
)的自然结果:赋值语句不检查逻辑含义,只机械执行。
实操建议:
别默认“没传就是该保留”,先明确业务语义:是“未提供”还是“显式清空”?
对可能被
覆盖的关键字段(如状态、时间戳、金额),必须加判断逻辑
用
替代裸赋值,前提是确认
类型安全且非
风险字段
在触发器中不是万能兜底,类型和确定性必须提前验证
看似简单,但在触发器上下文中容易踩两个硬坑:类型冲突和非确定性。比如
在 SQL Server 索引视图中会直接报错,因为
(或
)是非确定性函数;又比如
在 PostgreSQL 中会因类型不兼容拒绝创建触发器。
实操建议:
所有参数必须同类型或可隐式转为统一类型——数值列优先用
而非
,避免整型/浮点精度突变
禁止在参数中使用运行时函数:
、
、
等一律替换成常量或触发器外预计算值
用
(PostgreSQL)或
(SQL Server)验证返回类型是否符合预期
WHERE 条件里误用
会导致逻辑错位和索引失效
有人想在触发器里写
来捕获“空分类”,这看似合理,但实际漏掉了两种情况:一是
是空字符串
(被
拦截后无法区分),二是
和
在业务上本应不同处理,却被强行归为一类。
更严重的是性能问题:
是表达式计算,数据库无法利用
列上的索引,哪怕只是做行级判断。
实操建议:
条件分支优先用原生判空:
真要合并语义,应在应用层或存储过程里做标准化,而不是在触发器里用
掩盖差异
涉及索引字段的判断,永远避免包裹函数——包括
、
、
等
聚合或运算前不逐字段
,结果必然不可靠
触发器里常见写法:
。只要其中任意一列是
,整个表达式结果就是
,且不会报错,只会静默中断后续逻辑。这不是触发器没运行,而是 SQL 的“
传染性”在起作用。
实操建议:
每个参与运算的字段都单独包裹:
字符串拼接同理:
警惕聚合函数位置:
是逐行补零再求和;
是整列全
才补一次零,二者语义完全不同
最易被忽略的一点:触发器中
的行为依赖于数据库引擎对标准 SQL 的实现程度。MySQL 5.7 对多参数类型推导比 PostgreSQL 15 更宽松,但代价是运行时才暴露类型错误;SQLite 的
允许混合类型却静默转成文本,导致下游数值计算出错。跨库迁移前,务必用真实数据集跑一遍边界 case。
NEW.field = OLD.fieldBEFORE UPDATENEW.status = OLD.statusOLD.statusNULLNEW.status0NULLTRUEFALSEUNKNOWNNULLCOALESCE(NEW.field, OLD.field)OLD.fieldNULLCOALESCECOALESCECOALESCE(NEW.updated_at, NOW())NOW()GETDATE()COALESCE(NEW.price, 'N/A')0.00NOW()NEWID()USER()SELECT pg_typeof(COALESCE(...))sp_help 'your_view'COALESCEIF COALESCE(NEW.category, '') = '' THEN ...NEW.category''COALESCENEW.category IS NULLNEW.category = ''COALESCE(NEW.category, '') = ''categoryIF NEW.category IS NULL OR NEW.category = '' THENCOALESCECOALESCETRIMUPPERCOALESCENEW.total = NEW.price * NEW.quantity + NEW.taxNULLNULLNULLCOALESCE(NEW.price, 0) * COALESCE(NEW.quantity, 1) + COALESCE(NEW.tax, 0)CONCAT(COALESCE(NEW.first_name, ''), ' ', COALESCE(NEW.last_name, ''))SUM(COALESCE(sales, 0))COALESCE(SUM(sales), 0)NULLCOALESCECOALESCE