必须用 IS NULL/IS NOT NULL 判断空值,= NULL 永远不生效;检测“从有到无”需对比新旧值;字符串拼接前须用 ISNULL/IFNULL 处理 NULL;阻止插入 NULL 必须用 SIGNAL 或 RAISERROR+ROLLBACK。

触发器里用 = NULL 判断永远不生效
直接写 NEW.phone = NULL 或 IF OLD.status != 'done' 时遇到 NULL,条件根本不会进分支——这不是语法错误,是 SQL 三值逻辑的必然结果:= NULL 返回 UNKNOWN,而 IF 只响应 TRUE。
必须改用 IS NULL 或 IS NOT NULL:
-
IF NEW.phone IS NULL THEN—— 正确捕获空值 -
IF NEW.phone IS NOT NULL AND NEW.phone != '' THEN—— 同时排除 NULL 和空串 - 别写
IF NEW.phone NULL,它和= NULL一样无效
UPDATE 触发器中要区分“变为空”和“本来就是空”
只查 INSERTED.code IS NULL 只能知道“现在是空”,但无法判断是否发生了变更。真正要拦截“从有到无”,必须显式对比新旧状态:
- 检测清空动作:
WHERE DELETED.code IS NOT NULL AND INSERTED.code IS NULL - 检测首次设空(非重复):单靠触发器做不到,需配合时间戳字段或版本号
- 慎用
ISNULL(DELETED.code, '') != ISNULL(INSERTED.code, '')—— 它把NULL和''当作等价,业务语义可能被掩盖
字符串拼接或数值计算前必须逐字段兜底
INSERTED.name + ' - ' + INSERTED.code 中任意一端为 NULL,整条结果就是 NULL,不是空串。这不是 bug,是标准行为。
正确做法是每个参与字段单独处理:
- SQL Server:
ISNULL(INSERTED.name, '') + ' - ' + ISNULL(INSERTED.code, '') - MySQL:
CONCAT(IFNULL(INSERTED.name, ''), ' - ', IFNULL(INSERTED.code, '')) - 数值字段别用字符串兜底:
ISNULL(age, 0)可以,ISNULL(age, 'N/A')会报类型转换错
想阻止插入 NULL,光写日志没用
在 BEFORE INSERT 触发器里只做 INSERT INTO log VALUES ('phone missing'),数据照样插进去。真正有效的拦截方式只有:
- MySQL:
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'phone cannot be NULL'; - SQL Server:
RAISERROR('phone cannot be NULL', 16, 1); ROLLBACK; - 别依赖
SELECT 'error'或往日志表写记录——它们不中断执行流
最易被忽略的一点:空字符串 '' 和 NULL 是两回事。业务若要求“既不能 NULL 也不能空串”,两个条件都得显式写,少一个都会漏检。










