MySQL客户端执行SQL出错时,错误信息由服务端通过协议包返回错误码和消息,客户端接收后才抛异常或设错误标志;不主动检查返回值或捕获异常会导致错误被静默忽略。
MySQL客户端执行SQL出错时,错误信息从哪来
MySQL客户端(如
mysql
命令行、Python的
、PHP的
)执行SQL失败时,错误不是由SQL本身“吐出来”的,而是服务端返回的协议包里带的错误码和消息。客户端收到后才抛异常或设错误标志。
这意味着:不主动检查返回值或捕获异常,错误就静默吞掉了。
命令行默认开启
以外的行为:遇到错误直接停止后续语句(除非用
批量执行且加了
)
在存储过程中用
出错,默认中断整个过程,除非定义了
应用层如 Node.js 的
,必须监听
事件或用
包裹
如何在存储过程中捕获并处理错误
MySQL 5.5+ 支持基于 SQLSTATE 或 MySQL 错误码的异常处理机制,核心是
,但仅限于存储过程、函数、触发器内使用,不能用于普通交互式SQL。
常见写法:
覆盖大部分完整性约束错误;具体可查
MySQL 官方错误码表
触发后自动退出当前作用域(即过程体),
则继续往下执行
注意:事务状态不会自动恢复,
必须显式写,否则可能留下部分提交的数据
应用代码中怎么可靠判断SQL是否执行失败
靠
或类似接口判断“影响行数”是常见误区——它对
返回 -1,对成功执行但没改数据的
返回 0,但这两者都不是错误。真正要捕获的是执行层面的失败。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
PHP
:检查
是否非零,或调用
;面向对象风格下用
和
Python
:必须用
,
是
元组,例如
Node.js
:监听
处理连接级错误;单条查询用
并
错误对象,其
字段是类似
的符号名,
是数字(如 1062)
命令行
:检查进程退出码,非零即失败(但无法区分错误类型)
为什么 SET sql_mode 不是错误处理,而是预防手段
控制的是“什么算错误”,而不是“怎么处理错误”。比如开启
后,向
字段插
会报错;关闭它则静默转成默认值(如空字符串或 0)。
这看起来像“容错”,实则是掩盖问题源头:
在非严格模式下可能成功,但业务逻辑已偏离预期
不同环境(开发/生产)
不一致,会导致相同SQL行为不一致,调试困难
它不能替代应用层的错误捕获——即使开了严格模式,主键冲突、锁等待超时等仍需按异常处理
真正容易被忽略的是:错误码的语义依赖 MySQL 版本和配置。比如
(死锁)在 5.7 和 8.0 中都存在,但
(check constraint violation)只在 8.0.16+ 支持。写通用错误处理时,别只认数字,优先匹配
或错误消息子串。
pymysqlmysqlimysql--forcesource--forceINSERT INTO ... SELECTDECLARE ... HANDLERmysql2errortry/catchawait query()DECLARE ... HANDLERDROP PROCEDURE IF EXISTS safe_insert;
DELIMITER $$
CREATE PROCEDURE safe_insert(IN p_id INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT '发生未知错误,已回滚' AS msg;
END;
DECLARE EXIT HANDLER FOR SQLSTATE '23000' -- 如主键冲突、唯一索引冲突
BEGIN
ROLLBACK;
SELECT '主键/唯一约束冲突' AS msg;
END;
START TRANSACTION;
INSERT INTO users(id, name) VALUES(p_id, 'test');
COMMIT;
END$$
DELIMITER ;
SQLSTATE '23000'EXIT HANDLERCONTINUE HANDLERROLLBACKmysql_affected_rows()SELECTUPDATEmysqli$mysqli->errno$mysqli->error$mysqli->connect_errno$mysqli->errorpymysqltry/except pymysql.Error as e:e.args(errno, error_message)(1062, "Duplicate entry '1' for key 'PRIMARY'")mysql2connection.on('error', ...)await connection.execute(...)catchcodeER_DUP_ENTRYerrnomysql -e "..."sql_modeSTRICT_TRANS_TABLESNOT NULLNULLINSERT INTO t(a) VALUES(NULL)sql_mode12053819SQLSTATE