能做,但必须用BEFORE INSERT/UPDATE触发器通过SET NEW.field标准化数据,AFTER中NEW只读且赋值报错;校验失败须用SIGNAL中断,NULL需显式判断,批量插入可能绕过触发器。

能做,但必须用 BEFORE INSERT 或 BEFORE UPDATE 触发器修改 NEW,否则改不动字段值;AFTER 触发器里赋值无效,还可能静默丢数据。
MySQL 中标准化必须用 BEFORE 触发器 + SET NEW.field
MySQL 的 AFTER INSERT 里 NEW 是只读的,执行 SET NEW.email = LOWER(NEW.email) 会直接报错:ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger。只有 BEFORE 阶段才能安全写入。
- 校验失败必须用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'xxx',只写SELECT或IF判断不中断插入 - 字段允许
NULL时,得先加IF NEW.email IS NOT NULL THEN,否则REGEXP_LIKE(NULL, '...')返回NULL,条件判断失效 -
REGEXP_REPLACE()仅 MySQL 8.0+ 支持;5.7 只能嵌套REPLACE(),但别超三层,否则难维护、易误删(比如把'A-B-C'的连字符全干掉) - 批量插入(如
INSERT INTO ... SELECT)可能绕过触发器,得确认账号有TRIGGER权限且未被优化跳过
PostgreSQL 推荐用 BEFORE + REGEXP_REPLACE,别碰 AFTER
PostgreSQL 的 AFTER 触发器里对 NEW 赋值(如 NEW.email := lower(NEW.email))会被静默忽略——不是报错,而是根本没效果。必须用 BEFORE INSERT OR UPDATE,且函数末尾必须写 RETURN NEW,漏了这句整行就丢了。
-
REGEXP_REPLACE(NEW.phone, '[^\d]', '', 'g')比REPLACE安全,'g'参数不能少,否则只替换第一个匹配项 - 清空格别用
'\s+'—— PostgreSQL 默认不匹配全角空格(U+3000),得显式写[[:space:] ] - 如果字段是
JSONB或BYTEA,UPPER()会直接报错,先确认类型再操作 - 想查其他表补前缀(如根据国家码加区号)?别在触发器里
SELECT INTO,会拖慢写入,还可能死锁
标准化逻辑该放触发器里,还是用生成列 / 约束?
触发器负责“入库前强制改值”,生成列和约束各司其职:固定格式(如邮箱小写、型号大写)优先用生成列 + 唯一索引;纯校验(如手机号长度、邮箱结构)优先用 CHECK 约束——它比触发器性能好、覆盖全(包括 COPY、ON CONFLICT),且容易审计。
- MySQL 8.0+ 可建生成列:
email_lower VARCHAR(255) AS (LOWER(email)) STORED,再加唯一索引,避免每次 INSERT 都跑函数 - PostgreSQL 的
CHECK (email ~ '^[^@]+@[^@]+\.[^@]+$')比触发器更可靠,不会被批量操作绕过 - 触发器适合跨字段联动校验(如 “订单金额 × 数量 = 总价”),但这类逻辑越简单越好,别塞复杂查询
- 上线前必须手动清洗历史数据,触发器只管“将来”,不管“过去”——存量数据不处理,索引和查询照样失效
真正容易被忽略的是:触发器不是万能过滤网。它对 LOAD DATA INFILE、某些 INSERT ... ON DUPLICATE KEY UPDATE 场景、以及低版本正则锚点支持都不稳定;一旦逻辑出错,调试成本远高于应用层校验。

















