目录
场景描述
今天前端突然告诉我,调用分页查询接口时,返回的总个数比实际的总个数小。
而在这个项目中,我使用的是mybatis plus自带的分页插件,其他接口都没有问题,偏偏这个接口出现了问题。
问题分析
定位到代码所在的sql(用了mybatis自带的查询构造器)
虽然关联了五张表,不是很优雅,但因为一个列表需要展示的东西太多,似乎也没有其他更好的办法。
抛开sql本身的不规范,我们分析一下为什么出现总个数不正确的情况。
控制台打印一下执行的sql:
统计个数的时候,执行了如下语句
==> Preparing: SELECT COUNT(1) FROM tb_import_box tib WHERE (tib.deleted = 1 AND tib.status = ? AND tib.type = ?)
==> Parameters: 1(Byte), 1(Byte)
<== Columns: COUNT(1)
<== Row: 4
而真正执行的查询sql如下
==> Preparing: SELECT tib.*, tb.equip_num equipNum, tbs.box_spec_name boxSpecName, tc.categ_name categName, tp.pro_spec_no proSpecNo, tp.pro_spec_name proSpecName, tb.box_no boxNo, tt.task_no taskNo, tt.app_no appNo, tt.task_detail_type taskDetailType
FROM tb_import_box tib
LEFT OUTER JOIN tb_task tt on tt.id = tib.task_id
LEFT OUTER JOIN tb_box tb on tb.id = tib.box_id
LEFT OUTER JOIN tb_box_spec tbs on tbs.box_spec_no = tb.box_spec_no
LEFT OUTER JOIN tb_category tc on tb.categ_id = tc.id
LEFT OUTER JOIN tb_pro_spec tp on tb.pro_spec_no = tp.pro_spec_no
WHERE (tib.deleted = 1 AND tib.status = ? AND tib.type = ?)
ORDER BY tib.status LIMIT ?,?
==> Parameters: 1(Byte), 1(Byte), 0(Long), 30(Long)
再看结果,发现了完全相同的两行
解决方案
分页插件对sql做了优化,当sql使用的是左连接时,插件在统计总数量时,只会统一第一张表。
假设查询语句如下:
select * from a left join b on a.name = b.name;
如果b有两个相同的name与a对应,即a的name与b的name是一对多的关系时,会出现重复的查询结果。
这个时候 select count(*) from a left join b on a.name = b.name; 不等于 select count(*) from a; 了
查看数据库
确实有一张表出现了重复的数据,而这个字段在业务上也应该时不重复的,代码里已经做了校验了。估计是因为其他人直接通过数据库添加测试数据的,把这条重复的数据删掉即可。
—————————分割线——————————
再来谈谈分页插件,假设我们用的不是左连接,用的是内连接
计算总数时
因为内连接的条数不一定和第一张表相等,所以没有做什么优化处理,直接带入原sql计算。
其实就算是左外联,也不一定会完全省略后面的连接表
比如条件中出现了tp(也就是后面的关联表),这个时候插件就没办法忽略后面的关联表了,只能带入计算。