一对多分页的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 ...
随机推荐
- 读Pyqt4教程,带你入门Pyqt4 _002
在这节教程中,我们将创建菜单和工具栏. QMainWindow 类提供应用程序主窗口,可以创建一个经典的拥有状态栏.工具栏和菜单栏的应用程序骨架. 菜单栏 菜单栏是GUI应用程序最明显的部分之一,这是 ...
- 这些Java8官方挖过的坑,你踩过几个?
导读:系统启动异常日志竟然被JDK吞噬无法定位?同样的加密方法,竟然出现部分数据解密失败?往List里面添加数据竟然提示不支持?日期明明间隔1年却输出1天,难不成这是天上人间?1582年神秘消失的10 ...
- 如何在Spring Boot应用启动之后立刻执行一段逻辑
1. 前言 不知道你有没有接到这种需求,项目启动后立马执行一些逻辑.比如简单的缓存预热,或者上线后的广播之类等等.如果你使用 Spring Boot 框架的话就可以借助其提供的接口CommandLin ...
- 关于URL优化的一些经验
URL在搜索结果列表中时显示内容之一.设计网站结构时需要对目录及文件命名系统做事先规划.总的原则是首先从用户体验出发,URL应该清晰友好.方便记忆,然后才考虑URL对排名的影响.具体可以考虑以下几个方 ...
- 对象调用 push 方法
/* Array.prototype.push = function A(val) { this[this.length] = val; // =>this.length 在原来的基础上加1 r ...
- ASP.NET MVC 数据传递进阶 从数据库拿到数据后的三种方式
目录 回顾 数据 显示 因为这个小练习有EF的一些东西,我们来回顾一下. 一.回顾 回顾一点EF的知识怎么生成数据库模型. 1.首先我们在Models文件夹右键添加新建项,在数据分栏下有ADO.NET ...
- Java实现 LeetCode 786 第 K 个最小的素数分数(大小堆)
786. 第 K 个最小的素数分数 一个已排序好的表 A,其包含 1 和其他一些素数. 当列表中的每一个 p<q 时,我们可以构造一个分数 p/q . 那么第 k 个最小的分数是多少呢? 以整数 ...
- Java实现蓝桥杯VIP算法训练 预测身高
试题 算法训练 预测身高 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述: 生理卫生老师在课堂上娓娓道来: 你能看见你未来的样子吗?显然不能.但你能预测自己成年后的身高,有公式: 男 ...
- Java实现 LeetCode 119 杨辉三角 II
119. 杨辉三角 II 给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行. 在杨辉三角中,每个数是它左上方和右上方的数的和. 示例: 输入: 3 输出: [1,3,3,1] 进阶: ...
- java实现自行车行程
** 自行车行程** 计算行程 低碳生活,有氧运动.骑自行车出行是个好主意.小明为自己的自行车装了个计数器,可以计算出轮子转动的圈数.在一次骑车旅行中,出发时计算器的示数为begin,到达目的地时的示 ...