【踩坑日常】mysql查询错误排查

发布时间:2024年01月24日

背景

在生产上发现一个接口数据怎么查都为空,做的日志记录,sql语句以及参数手动执行却能返回结果

排查

刚发现问题的时候,第一时间是通过日志去查看问题,模拟下核心点就如下

2024-01-24 14:10:03,912 DEBUG selectSQL:137 - ==>  Preparing: SELECT COUNT(*) FROM a cwo   WHERE cwo.`ques` REGEXP replace(' 3.* ',' ','') 
2024-01-24 14:10:03,920 DEBUG selectSQL:137 - <==      Total: 1

mapper对应的查询语句是这么写的

  <select id="selectSQL" resultType="integer">
    SELECT COUNT(*) FROM a cwo
     <where>
       <if test="param.ques!= null and param.ques.size() > 0">
         <foreach collection="param.ques" item="que" separator=","
                  open="cwo.`ques` REGEXP replace('" close="',' ','')">
  	         ${que}
         </foreach>
       </if>
     </where>
  </select>

tips1:这里之所以是用${}而不是#{}去写,是因为这里需要一个完整的字符串去执行正则(后面想了想,其实这个不是只要在逻辑里先拼接好不就行了吗?!(摔)),而占位符只会生成一个个的字符串对象,所以用了连接符去生成一个字符串
tips2:这里用replace(data, ’ ', ‘’)是因为拼接可能会出现多余的空格,导致regexp正则匹配失败

拿上面debug的sql去数据库执行,是有数量返回的,而调用接口,只会返回0

分析

既然打印日志发现不了问题,那就进行断点调试,这里就不说断点调试过程了,网上挺多的文章的,直接断点分析到SimpleExecutor.java

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行到这里的时候,stmt是带着sql完整语句的,可以查看自己生成的真正SQL语句(stmt.h.statement.delegate)
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

然后就看到了

HikariProxyPreparedStatement@1735946981 wrapping com.mysql.cj.jdbc.ClientPreparedStatement: SELECT COUNT(DISTINCT csr.cwo_id) FROM a cwo
		 WHERE  cwo.`ques` REGEXP replace('
		                                  2.*
		                                  ',' ','')

可以看到由于平时的小强迫症写法,导致生成的字符串是带了换行的,replace没能正确生成我们预期的正则表达式,导致匹配不到数据

tips
这里断点有个小技巧,idea点击断点,选择More,
在这里插入图片描述
然后选中你调用sql的位置断点,这样就能在直到执行你想要断点的位置时,才会进入SimpleExecutor
在这里插入图片描述

解决

既然是写法问题,那就修改mapper文件,这样就可以了

  <select id="selectSQL" resultType="integer">
    SELECT COUNT(*) FROM a cwo
     <where>
       <if test="param.ques!= null and param.ques.size() > 0">
         <foreach collection="param.ques" item="que" separator=","
                  open="cwo.`ques` REGEXP replace('" close="',' ','')">${que}</foreach>
       </if>
     </where>
  </select>

总结

平时写sql多多少少会遇到各种奇奇怪怪的现象,但是大多数能够通过打印的日志发现问题,如果坚信sql是对的的话,不妨断点确认下最终送给数据库的语句是怎样的(毕竟,各种插件在便利代码的同时,可能意外给你加上各种意料外的条件 😃

文章来源:https://blog.csdn.net/Athain/article/details/135823516
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。