mysql触发器中signal抛错必须指定message_text,否则客户端收不到有效错误信息;sql server推荐raiserror以兼容性和可控性;oracle的raise_application_error错误号限-20000至-20999且消息不可为空;所有数据库触发器均不支持异常捕获,重逻辑应移至应用层。

MySQL触发器里用SIGNAL抛错必须配MESSAGE_TEXT
不加SET MESSAGE_TEXT = 'xxx',客户端收到的错误是空的或只有模糊系统提示,比如ERROR 1644 (45000)后面啥都没有。你得在SIGNAL SQLSTATE '45000'后面紧跟着这句,且字符串不能为NULL或空格——否则照样报错或静默失败。
常见错误现象:写完SIGNAL SQLSTATE '45000'就结束,执行INSERT时只看到“Unknown error”,根本没法定位问题。
-
SIGNAL只能用在BEFORE触发器里;AFTER中虽语法通过,但若再操作同表会先触发Can't update table in stored function/trigger,SIGNAL根本没机会执行 - 查重逻辑别写
SELECT email INTO @tmp FROM users WHERE ...——查不到会直接触发ERROR 1329 (02000),且无法拦截 - 字段必须有索引,否则
IF EXISTS (SELECT 1 FROM t WHERE order_no = NEW.order_no)会全表扫描,拖慢所有写入
SQL Server触发器中RAISERROR和THROW的区别与选型
RAISERROR兼容老版本(2005+),THROW从2012起才有,语法更简洁但不能自定义错误号(固定用50000起)。业务系统若需统一错误分类,推荐RAISERROR配明确severity值。
关键限制:只有severity ≥ 11的错误才能被上层TRY...CATCH捕获;用severity = 10等于白抛——应用层收不到,事务也不回滚。
-
RAISERROR('订单号重复', 16, 1)比THROW 50000, '订单号重复', 1更容易和现有监控系统对齐 -
THROW不能在动态SQL里用,RAISERROR可以,但要注意参数拼接风险 - 抛错后必须显式
ROLLBACK TRANSACTION,否则数据可能已写入但错误被“吞掉”
Oracle触发器调用RAISE_APPLICATION_ERROR的硬编码限制
错误号必须是-20000到-20999之间的整数,传-1000或-30000会编译失败,报PLS-00302;消息字符串超2048字节会被静默截断,不警告也不报错。
第二参数不能为空字符串,否则Oracle报ORA-20000(只剩一个冒号),连基本校验都过不了。
- 不能在SQL表达式中调用,比如
SELECT * FROM t WHERE f() = 1里不能放RAISE_APPLICATION_ERROR - 只能放在PL/SQL块里,即
BEGIN ... END;内部 - 它能穿透到JDBC/OCI层,是唯一能让Java或Python拿到业务错误号的方式
所有数据库触发器都不支持异常捕获机制
你没法在触发器里写DECLARE HANDLER(MySQL)、TRY...CATCH嵌套(SQL Server)或EXCEPTION WHEN OTHERS(Oracle)来“兜底”。一旦出错,就是中断+回滚,没有fallback路径。
真正容易被忽略的是:校验逻辑越重,越该移到应用层——触发器只适合轻量、确定性高的拦截,比如单字段唯一性、基础范围检查。涉及多表关联、时间窗口、外部API调用的重复判断,硬塞进触发器只会让数据库变慢、变脆、难调试。











