前言

前几天,负责项目中clickhouse到doris的升级。最后升级完之后,在测试的过程中,有一个数据导出的功能,出现了SQL语法错误。

刚开始我以为是clickhouse和doris两个不同的数据,有些sql语法有差异导致的问题。但仔细看了一下测试环境异常日志之后发现,报错的sql语句,并没有访问doris数据库,而是直接访问的mysql数据库。

而我这次,并没有修改过mysql数据库的相关sql。

也就是说,这个问题是同事留下的老坑。

1 案发现场

有一天下午,我在st环境的portal页面,测试一些统计数据导出的功能。因为最近我将访问一些统计数据的表,从clickhouse改成了doris中,需要在st环境验证一下功能是否正常。

在测试其中一个统计数据导出功能时,出现了导出失败的提示。

刚开始,我以为是我最近该的功能有问题。

查看了一下测试环境的日志,发现有个类型转换异常:

Error attempting to getcolumn'inDate'from result set.

Cause:java.sql.SQLDataException:Cannot convert string'3' to java.sql.Timestamp value

而在返回的实体类StatModel中:

@Data
public class StatModel{

   private String name;
   private Date inDate;
   private int type;
}

inDate字段是一个Date类型的,怎么会出现类型转换错误呢?

2 问题分析

根据代码中打印的错误日志,我很快定位到了具体的错误代码行数,把日志中错误的sql复制出来了。然后把相关?替换成具体的参数值。

这条sql很长,各种join,case when,if判断,各种括号,粗略估计有300多行,一眼根本看不明白。

为了搞清楚这个sql的含义,我将这条sql在Navicat Premium中美化了一下。

搜索了一下关键字:inDate,发现了几处使用它的地方。

刚开始,我以为是字段别名搞错了,比如:在select时,把type字段 as 成InDate。

结果,查看了所有子sql,发现没有出现这种情况。

在select时,都是把in_date字段as成inDate。

不能快速查到问题,只能硬着头皮,把sql进行拆分了。

先从最内层的sql,开始执行。

执行完之后,把sql替换成真正的值。

这样可以将sql的复杂度,逐渐降低。

比如:

select id,name from a where a_code in (select code from b where name='susan')

如果下面sql的查询结果是:001和002

select code from b where name='susan'

则之前的sql改成:

select id,name from a where a_code in('001','002')

这样不断简化之后,到最外层,出现了一条这样的sql:

select name,in_date,type from a where...
union all
select name,type,in_date from a where...

该sql语句最外层,使用union all关键字,将两个查询结果进行合并。

但却出现了一个问题:两条sql语句select的字段,顺序不一样。

这样会导致部分inDate字段获取到了type字段的值,导致了文章开头的类型转换错误。

3 问题解决

解决上面这个问题,其实非常简单,只需要将上面的两条sql中select语句中返回的字段顺序保存一致即可。

在MySQL中union关键字的这个问题,是一个非常坑的问题,MySQL会将不同类型的数据在结果中合并返回,不会报任何异常,特别是在一些非常复杂的sql语句中,这个问题不容易被发现。

返回的数据可能是这样的:

苏三  2023-10-17      1
张三  2023-10-15      2
李四  2023-10-12      2
王五     2         2023-10-12
测试     1         2023-09-12

特别是如果有些查询接口做了分页功能,有可能前面的100页数据是正常的,而后面的页的数据有问题。

因此,我们在写完sql之后,一定要把该sql拿到数据库中多测试一下,特别是有union关键字的sql,加了新字段一定要多测试。

对select语句中字段的顺序,要求非常严格。

最后修改:2026 年 06 月 06 日
如果觉得我的文章对你有用,请随意赞赏