一对多分页的SQL到底应该怎么写?

1. 前言
MySQL一对多的数据分页是非常常见的需求,比如我们要查询商品和商品的图片信息。但是很多人会在这里遇到分页的误区,得到不正确的结果。今天就来分析并解决这个问题。
2. 问题分析
我们先创建一个简单商品表和对应的商品图片关系表,它们之间是一对多的关系:

然后我分别写入了一些商品和这些商品对应的图片,通过下面的左连接查询可以看出它们之间具有明显的一对多关系:
SELECT P.PRODUCT_ID, P.PROD_NAME, PI.IMAGE_URL
FROM PRODUCT_INFO P
LEFT JOIN PRODUCT_IMAGE PI
ON P.PRODUCT_ID = PI.PRODUCT_ID

按照传统的思维我们的分页语句会这么写:
<resultMap id="ProductDTO" type="cn.felord.mybatis.entity.ProductDTO">
<id property="productId" column="product_id"/>
<result property="prodName" column="prod_name"/>
<collection property="imageUrls" ofType="string">
<result column="image_url"/>
</collection>
</resultMap>
<select id="page" resultMap="ProductDTO">
SELECT P.PRODUCT_ID, P.PROD_NAME,PI.IMAGE_URL
FROM PRODUCT_INFO P
LEFT JOIN PRODUCT_IMAGE PI
ON P.PRODUCT_ID = PI.PRODUCT_ID
LIMIT #{current},#{size}
</select>
当我按照预想传入了(0,2)想拿到前两个产品的数据,结果并不是我期望的:
2020-06-21 23:35:54.515 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page : ==> Preparing: SELECT P.PRODUCT_ID, P.PROD_NAME,PI.IMAGE_URL FROM PRODUCT_INFO P LEFT JOIN PRODUCT_IMAGE PI ON P.PRODUCT_ID = PI.PRODUCT_ID limit ?,?
2020-06-21 23:35:54.541 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page : ==> Parameters: 0(Long), 2(Long)
2020-06-21 23:35:54.565 DEBUG 10980 --- [main] c.f.m.mappers.ProductInfoMapper.page : <== Total: 2
page = [ProductDTO{productId=1, prodName='杯子', imageUrls=[http://asset.felord.cn/cup1.png, http://asset.felord.cn/cup2.png]}]
我期望的两条数据是杯子和笔记本,但是结果却只有一条。原来当一对多映射时结果集会按照多的一侧进行输出(期望4条数据,实际上会有7条),而前两条展示的只会是杯子的数据(如上图),合并后就只有一条结果了,这样分页就对不上了。那么如何才能达到我们期望的分页效果呢?
3. 正确的方式
正确的思路是应该先对主表进行分页,再关联从表进行查询。
抛开框架,我们的SQL应该先对产品表进行分页查询然后再左关联图片表进行查询:
SELECT P.PRODUCT_ID, P.PROD_NAME, PI.IMAGE_URL
FROM (SELECT PRODUCT_ID, PROD_NAME
FROM PRODUCT_INFO
LIMIT #{current},#{size}) P
LEFT JOIN PRODUCT_IMAGE PI
ON P.PRODUCT_ID = PI.PRODUCT_ID
这种写法的好处就是通用性强一些。但是MyBatis提供了一个相对优雅的路子,思路依然是开头所说的思路。只不过我们需要改造上面的Mybatis XML配置:
<resultMap id="ProductDTO" type="cn.felord.mybatis.entity.ProductDTO">
<id property="productId" column="product_id"/>
<result property="prodName" column="prod_name"/>
<!-- 利用 collection 标签提供的 select 特性 和 column -->
<collection property="imageUrls" ofType="string" select="selectImagesByProductId" column="product_id"/>
</resultMap>
<!-- 先查询主表的分页数据 -->
<select id="page" resultMap="ProductDTO">
SELECT PRODUCT_ID, PROD_NAME
FROM PRODUCT_INFO
LIMIT #{current},#{size}
</select>
<!--根据productId 查询对应的图片-->
<select id="selectImagesByProductId" resultType="string">
SELECT IMAGE_URL
FROM PRODUCT_IMAGE
WHERE PRODUCT_ID = #{productId}
</select>
4. 总结
大部分情况下分页是很容易的,但是一对多还是有一些小小的陷阱的。一旦我们了解了其中的机制,也并不难解决。当然如果你有更好的解决方案可以留言讨论,集思广益。多多关注:码农小胖哥,获取更多开发技巧。
关注公众号:Felordcn 获取更多资讯
一对多分页的SQL到底应该怎么写?的更多相关文章
- 使用第三方分页AspNetPager实现真正分页的SQL原理
AspNetPager是一个第三方分页第三方控件,可以和数据绑定控件(GridView等)方便的结合,实现真分页. 真分页:从数据库中获取符合要求的部分数目的记录.性能较高,数据量小,网络负载小,对数 ...
- 【mysql】 mybatis实现 主从表 left join 1:n 一对多 分页查询 主表从表都有查询条件 【mybatis】count 统计+JSON查询
mybatis实现 主从表 left join 1:n 一对多 分页查询 主表从表都有查询条件+count 需求: ======================================= ...
- 数据分页处理系列之一:Oracle表数据分页检索SQL
关于Oracle数据分页检索SQL语法,网络上比比皆是,花样繁多,本篇也是笔者本人在网络上搜寻的比较有代表性的语法,绝非本人原创,贴在这里,纯粹是为了让"数据分页专题系列"看起 ...
- 说说oracle分页的sql语句
说说oracle分页的sql语句,分排序和不排序两种. 当结果集不需要进行排序时,每页显示条数为:rowPerPage,当前页数为:currentPage. 1. 相对来说,这种查询速度会快一些,因为 ...
- Mysql 分页查询sql优化
先查下数据表的总条数: SELECT COUNT(id) FROM ts_translation_send_address 执行分页界SQL 查看使用时间2.210s SELECT * FROM ts ...
- 在Excel VBA中使用SQL到底优势在哪儿
小爬在之前的博文中多次提到,可以在VBA中写SQL来操作Excel文件,实现各类数据处理和分析需求.那么,你可能有这样的疑问:Excel原生的VBA,数据透视表,数据分析功能不够吗,为啥一定要用SQL ...
- 转>>在同一个sql语句中如何写不同条件的count数量
今天在做Portal中的Dashboard展现的时候,需要对多个统计字段做展现,根据我现在的掌握水平,我只能在sql调用构建器中实现一种sql语 句返回的resultSet做展现.没有办法,只能从数据 ...
- 需要一个分页,花了一个钟写了一个,刚学js,不是很完美
<script src="js/jquery.min.js" ></script> <script type="text/javascrip ...
- 在数据库里面有这么一个表:用m代表男,用f代表女,现在我要输出格式为中文的:男和女,sql语句该怎么写
在数据库里面有这么一个表:用m代表男,用f代表女,现在我要输出格式为中文的:男和女, sql语句该怎么写 select case sex when 'm' then '男' else '女' a ...
随机推荐
- dsPIC单片机的CAN引脚设置
用单片机的引脚复用 查询芯片数据手册C1RX的寄存器为RPINR26.C1RXR=(设置为需要用到的引脚) 引脚设置为输入(C1RX),TRIS=1: C1TX需要用的引脚为RPn41,查询数据手册R ...
- HomeLede 2020.5.27更新 UPnP+NAS+多拨+网盘+DNS优化+帕斯沃/Clash 无缝集成+软件包
交流群:QQ 1030484865 电报 t.me/t_homelede 固件说明 基于Lede OpenWrt R2020.5.20版本(源码截止2020.5.27)及若干自行维护的软件包 结合 ...
- 极客手中的利器Electron
作为一个前端开发人员,你可能已经听说过Electron了,你知道VS Code是基于这个技术开发的.不但VS Code, 目前一些大热的软件:飞书.Slack.WhatsApp都是基于这个技术开发的. ...
- WEB APPLICATION PENETRATION TESTING NOTES
此文转载 XXE VALID USE CASE This is a nonmalicious example of how external entities are used: <?xml v ...
- 副业收入是我做程序媛的3倍,工作外的B面人生
到“程序员”,多数人脑海里首先想到的大约是:为人木讷.薪水超高.工作枯燥…… 然而,当离开工作岗位,撕去层层标签,脱下“程序员”这身外套,有的人生动又有趣,马上展现出了完全不同的A/B面人生! 不论是 ...
- Jmeter(六) - 从入门到精通 - 建立数据库测试计划(详解教程)
1.简介 在实际工作中,我们经常会听到数据库的性能和稳定性等等,这些有时候也需要测试工程师去评估和测试,因此这篇文章宏哥主要介绍了jmeter连接和创建数据库测试计划的过程,宏哥在文中通过示例和代码非 ...
- python初学者笔记(2):阿拉伯数字转换成中文大写
题:输入一个数字,转换成中文大写的写法 可运行的程序(Python 2.7.9): # -*- coding: utf-8 -*- #在python2的py文件里面写中文,必须要添加一行声明文件编码的 ...
- URL与URI的联系与区别
作者:daixinye链接:https://www.zhihu.com/question/21950864/answer/154309494来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...
- Java实现 LeetCode 321 拼接最大数
321. 拼接最大数 给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字.现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要 ...
- Java实现 洛谷 P1738 洛谷的文件夹
题目描述 kkksc03是个非凡的空想家!在短时间内他设想了大量网页,然后总是交给可怜的lzn去实现. 洛谷的网页端,有很多文件夹,文件夹还套着文件夹. 例如:/luogu/application/c ...