当前位置: 首页 > news >正文

mysql 诡异的1054错误

前言

今天在工作中遇到一个非常坑爹的问题,有关Mysql的异常处理,花费了我好几个小时的时间,最后终于解决了,然后根据出现的问题的原因,逆向来看自己解决问题的过程,发现网上的一些文章简直是太坑了,坑的人找不着北啊,最后总结一下吧,免得又踩到这个坑。

先交代一下环境和背景:问题出现在工作的项目中,工程代码在运行的过程中会调用事先定义好的存储过程来保存数据,因为字段太多,大概有好几百个,我就想着把一些废弃的字段删掉,这样一来可以加快存储速度,二来减小数据库的存储大小,于是说干就干,为了避免不必要的麻烦,我决定先改存储过程,在参数中去想不需要存储的字段, 等检测没有问题了再删除表中的字段,就当我改完的时候问题出现了,数据存不上了…

分析过程

是的,数据存不上了,但是程序不报错,理论上如果调用存储过程失败了,程序会打印出错误代码,比如:1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xxx' at line 1,但是并没有,我是通过存储过程中的异常打印发现错误的,就是这一句DECLARE EXIT HANDLER FOR SQLEXCEPTION CALL LogError("SetBaseData:SQLEXCEPTION");,存储过程中如果有这一句,当发生异常的时候就可以调用自定义函数LogError("SetBaseData:SQLEXCEPTION")来将错误信息打印到数据库中,可是程序代码为什么不报错呢?这我就想不明白了。

还是想来先来看一下存储过程的示例:

CREATE PROCEDURE SetBaseData(
  IN paramId INT,
  IN paramA INT,
  IN paramB INT,
  IN paramC INT,
  IN paramD INT,
  IN paramE INT,
  IN paramF INT,
  IN paramG INT
)
BEGIN
  DECLARE EXIT HANDLER FOR SQLEXCEPTION CALL LogError("SetBaseData: SQLEXCEPTION");
  UPDATE dataT SET
    A=paramA,
    B=paramB,
    C=paramC,
    D=paramD,
    E=paramE,
    F=paramF,
    G=paramG,
  WHERE Id=paramId; 
END; 

看了这个存储过程是不是感觉很简单,结构是很简单,但是实际的存储过程中可是有好几百个字段需要修改,所以很难查到问题,结果问题就出在我精简字段的过程中,比如我删除无用的字段改成了下面这个样子:

CREATE PROCEDURE SetBaseData(
  IN paramId INT,
  IN paramA INT,
  IN paramB INT,
  IN paramE INT,
  IN paramG INT
)
BEGIN
  DECLARE EXIT HANDLER FOR SQLEXCEPTION CALL LogError("SetBaseData: SQLEXCEPTION");
  UPDATE dataT SET
    A=paramA,
    B=paramB,
    E=paramE,
    F=paramF,
    G=paramG,
  WHERE Id=paramId; 
END; 

怎么样,发现问题了吗?这样小一个存储过程应该一眼就能发现问题,paramF参数已经删掉了,但是在Update语句中还在使用,这就能解释为什么会触发异常导致数据无法存储了,但是想在几百个字段中找出这样的问题真的很困难,并且参数的顺序和设置的顺序是不一样的,一开始我也没发现,只能在异常结果上分析,有人可能会问我为什么不在程序中下断点,虽然没有打印信息,但是看返回值总能发现问题吧,很遗憾,底层代码被封装了,我只能看到返回了错误的结果,但是却分辨不出是什么错误,奇就奇怪在这,原来存储过程返回错误都是有打印信息的啊。

既然代码看不到,我就只能从存储过程中找原因了,于是我想到使用更细化的异常处理,但是找了半天也不能直接输出异常类型,只能单个捕获,于是上网查了错误代码的分类,发现网上千篇一律的都是下图中的错误代码总结,这正是坑人的地方,其实就是抄来的。

Mysql error

于是我对着这些错误代码挨个检测把存储过程改成了这样:

CREATE PROCEDURE SetBaseData(
  IN paramId INT,
  IN paramA INT,
  IN paramB INT,
  IN paramE INT,
  IN paramG INT
)
BEGIN
  DECLARE EXIT HANDLER FOR 1021 CALL LogError("1021 ");
  DECLARE EXIT HANDLER FOR 1022 CALL LogError("1022 ");
  DECLARE EXIT HANDLER FOR 1027 CALL LogError("1027 ");
  DECLARE EXIT HANDLER FOR 1036 CALL LogError("1036 ");
  DECLARE EXIT HANDLER FOR 1048 CALL LogError("1048 ");
  DECLARE EXIT HANDLER FOR 1062 CALL LogError("1062 ");
  DECLARE EXIT HANDLER FOR 1099 CALL LogError("1099 ");
  DECLARE EXIT HANDLER FOR 1100 CALL LogError("1100 ");
  DECLARE EXIT HANDLER FOR 1104 CALL LogError("1104 ");
  DECLARE EXIT HANDLER FOR 1106 CALL LogError("1106 ");
  DECLARE EXIT HANDLER FOR 1114 CALL LogError("1114 ");
  DECLARE EXIT HANDLER FOR 1150 CALL LogError("1150 ");
  DECLARE EXIT HANDLER FOR 1165 CALL LogError("1165 ");
  DECLARE EXIT HANDLER FOR 1242 CALL LogError("1242 ");
  DECLARE EXIT HANDLER FOR 1263 CALL LogError("1263 ");
  DECLARE EXIT HANDLER FOR 1264 CALL LogError("1264 ");
  DECLARE EXIT HANDLER FOR 1265 CALL LogError("1265 ");
  DECLARE EXIT HANDLER FOR 1312 CALL LogError("1312 ");
  DECLARE EXIT HANDLER FOR 1317 CALL LogError("1317 ");
  DECLARE EXIT HANDLER FOR 1319 CALL LogError("1319 ");
  DECLARE EXIT HANDLER FOR 1325 CALL LogError("1325 ");
  DECLARE EXIT HANDLER FOR 1326 CALL LogError("1326 ");
  DECLARE EXIT HANDLER FOR 1328 CALL LogError("1328 ");
  DECLARE EXIT HANDLER FOR 1329 CALL LogError("1329 ");
  DECLARE EXIT HANDLER FOR 1336 CALL LogError("1336 ");
  DECLARE EXIT HANDLER FOR 1337 CALL LogError("1337 ");
  DECLARE EXIT HANDLER FOR 1338 CALL LogError("1338 ");
  DECLARE EXIT HANDLER FOR 1339 CALL LogError("1339 ");
  DECLARE EXIT HANDLER FOR 1348 CALL LogError("1348 ");
  DECLARE EXIT HANDLER FOR 1357 CALL LogError("1357 ");
  DECLARE EXIT HANDLER FOR 1358 CALL LogError("1358 ");
  DECLARE EXIT HANDLER FOR 1362 CALL LogError("1362 ");
  DECLARE EXIT HANDLER FOR 1363 CALL LogError("1363 ");
  DECLARE EXIT HANDLER FOR SQLEXCEPTION CALL LogError("SetBaseData: SQLEXCEPTION");
  UPDATE dataT SET
    A=paramA,
    B=paramB,
    E=paramE,
    F=paramF,
    G=paramG,
  WHERE Id=paramId; 
END; 

看到这里是不是觉得我丧心病狂,确实是,但是我没有办法了啊,可是事与愿违,这么狠毒的代码最终也没有捕获到错误,可见网上的常见错误代码其实没什么卵用,接下来我就想既然代码中不打印错误,那么我直接在Mysql客户端调用存储过程总行了吧,一开始没这么做主要是字段太多了,好几百个呀,现在没办法了,只能试一试我对照着参数的类型,一个个输入,最后call运行存储过程,诡异的事情发生了,没有任何报错,甚至警告都没有,但是数据还是没有保存上,简直要疯了。没办法最终只能一行一行的看存储过程的定义了,那可是好几百行啊,这种定义和C++代码不同,根本就没有跳转,只能靠脑袋记忆,我又没用IDE,只能硬着头皮看了,还好功夫不负有心人,最后让我发现了问题,就是paramF参数已经删掉了,但是在Update语句中还在使用这样的情况,于是我改正了过来,发现保存数据没有问题了。

可是我那么多捕获异常的代码就白写了吗?于是我又还原了回去,结果令人震惊的一幕发生了,程序就然报错了,就是我刚才改过的那一句,1054 - Unknown column 'paramF' in 'field list',你说奇不奇怪,我都找到问题了,你居然又明确的指出来了,早干嘛去了,不过这条消息还算有点用,起码让我知道这是”1054异常”啊,于是我把这个异常在存储过程中做了处理,加了一句DECLARE EXIT HANDLER FOR 1054 CALL LogError("1054 ");这次果然在调试信息中发现了这个错误,于是上网查了一下1054的含义,就是说某个变量没有出现在参数列表中.

为此我还查了一下Mysql的官网,发现Mysql的官网上列举了错误1002到4531总共有3000多个,看来网上的Mysql常见错误代码并不常见。

总结

  1. 通过这件事让我再一次发现代码的诡异
  2. 遇到问题还得相信自己,还是自己最靠谱
  3. 网上查资料要注意甄别,我发现网上流传的常见错误代码中连“1064 sql 语法错误”都没有包含,看来它并不常见
  4. 还是想直接在存储过程中输出异常的类型,不想一个个的捕获,我还没找到方法,希望有经验的大神能给我一点点指导。

附注

我还是把Mysql官网指出的错误贴在这:Mysql Error Codes

相关文章:

  • UE4引擎取消编辑器处于后台时的降帧选项
  • Lua的函数参数为table时奇特现象
  • UE4引擎自定义插件导出类定义的简单流程
  • Lua收到C++传入的参数类型有一种userdata
  • Lua中可完美运行的三目运算符
  • Lua中 'local xxx do ... end' 到底表达了什么
  • Python树形打印目录结构
  • 排序算法系列之(二)——冒泡排序名字最为形象的一个
  • Python查找文件中包含中文的行
  • sscanf类似于正则表达式的进阶用法
  • mysql函数扩展之UDF开发
  • Python实现一个简单的图片爬虫
  • 验证mysql联合索引最左原则
  • Mysql查询时case when语句的使用
  • Vim中简单格式化代码
  • [译]如何构建服务器端web组件,为何要构建?
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 77. Combinations
  • Android 架构优化~MVP 架构改造
  • Android开源项目规范总结
  • CEF与代理
  • DOM的那些事
  • gitlab-ci配置详解(一)
  • Java IO学习笔记一
  • Java多线程(4):使用线程池执行定时任务
  • js正则,这点儿就够用了
  • k个最大的数及变种小结
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • node 版本过低
  • PAT A1120
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • ReactNativeweexDeviceOne对比
  • Sass Day-01
  • 微信支付JSAPI,实测!终极方案
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • (2022 CVPR) Unbiased Teacher v2
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (MATLAB)第五章-矩阵运算
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (附源码)计算机毕业设计高校学生选课系统
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • . Flume面试题
  • .md即markdown文件的基本常用编写语法
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .Net多线程总结
  • .NET性能优化(文摘)
  • .py文件应该怎样打开?
  • /var/log/cvslog 太大
  • @RequestParam详解
  • [20170713] 无法访问SQL Server