一對多分頁的SQL到底應該怎麼寫?

1. 前言

MySQL一對多的數據分頁是很是常見的需求,好比咱們要查詢商品和商品的圖片信息。可是不少人會在這裏遇到分頁的誤區,獲得不正確的結果。今天就來分析並解決這個問題。sql

2. 問題分析

咱們先建立一個簡單商品表和對應的商品圖片關係表,它們之間是一對多的關係:bash

一對多關係

而後我分別寫入了一些商品和這些商品對應的圖片,經過下面的左鏈接查詢能夠看出它們之間具備明顯的一對多關係:mybatis

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
複製代碼

全部的一對多結果

按照傳統的思惟咱們的分頁語句會這麼寫:app

<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條),而前兩條展現的只會是杯子的數據(如上圖),合併後就只有一條結果了,這樣分頁就對不上了。那麼如何才能達到咱們指望的分頁效果呢?ui

3. 正確的方式

正確的思路是應該先對主表進行分頁,再關聯從表進行查詢。url

拋開框架,咱們的SQL應該先對產品表進行分頁查詢而後再左關聯圖片表進行查詢:spa

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配置:code

<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. 總結

大部分狀況下分頁是很容易的,可是一對多仍是有一些小小的陷阱的。一旦咱們瞭解了其中的機制,也並不難解決。固然若是你有更好的解決方案能夠留言討論,集思廣益。多多關注:碼農小胖哥,獲取更多開發技巧。cdn

關注公衆號:Felordcn獲取更多資訊

我的博客:https://felord.cn

相關文章
相關標籤/搜索