更新於 2022/04/24閱讀時間約 9 分鐘

Laravel with() vs join()

之前在執行某些專案的時候,常看到-with()這種寫法,因此一直都很好奇到底跟-join()寫法有什麼差別,哪一種寫法效能比較好呢?

資料情境:

假設現在情境是要撈出文章跟留言,目前共有2篇文章,每篇各有4萬筆留言,也就是留言table共有8萬筆資料:

程式碼:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Services\UserService;
use App\Models\Comment;
use App\Models\Article;
use DB;
use Exception;


class ArticleController extends Controller
{
    public function query(){
        /*
        for($i = 1 ; $i <= 20000 ; $i++){
            $comment = new Comment;
            $comment->article_id = 2;
            $comment->content = substr(md5(mt_rand()), 0, 50);
            $comment->save();
        }
        */
        try{

            DB::enableQueryLog();

            //---------------------------------------------------------------------------------
        
            $start = microtime(true);
            $query = Article::join('comment', 'comment.article_id', '=', 'article.id')
            ->select(
                'article.id',
                'article.title AS article_title',
                'comment.content AS comment_content'
            );
            $join_result = $query->get();
            $join_time = microtime(true) - $start;
            
    
            //---------------------------------------------------------------------------------
    
            $start = microtime(true);
            $query = Article::with([
                'comments' => function ($query) {
                    $query->select([
                        'article_id',
                        'content'
                    ]);
                }
            ]);
            $with_result = $query->get();
            $with_time = microtime(true) - $start;

            //---------------------------------------------------------------------------------
            
            $SQL = DB::getQueryLog();
    
    
            
            return response()->json([
                'status' => 'success',
                'SQL' => $SQL,
                'join_time' => $join_time,
                'join_result' => $join_result,
                'with_time' => $with_time,
                'with_result' => $with_result,
            ]);
        }
        catch (Exception $e) {
            return response()->json([
                'status' => 'fail',
                'message' => $e->getMessage()
            ], 500);
        }
    }
}

實驗結果:

  • 從結果可以看出,with這種寫法,共下了兩次sql,分別把文章跟留言都撈出來,再把資料mapping起來,with_result如下:
  • join寫法就不多解釋了,join_result如下:
  • join執行時間6.6s,with執行時間8.4s,單從執行時間上來看,join快一些。
  • 就資料面來看,with把資料整理得比較乾淨,大致上分為兩篇文章,下面的comments key就可以直接拿到該篇文章的所有留言,如果這個query api的目的是要抓出所有文章跟對應的留言的話,用with這種寫法比較簡潔,而且前端拿到資料後也幾乎不需要再整理過,可以說是好處多多。但如果是join寫法,可能還要group by 文章id,才能整理成with這種資料結構。

結論:

  • join與with寫法,回傳的資料結構不同,得視使用情境來選擇適合的寫法。
單就討論with跟join query寫法哪個比較快的話,從實驗數據來看,join會比較快,但是個人認為這個結論比較沒有意義,因為要看資料使用情境來決定要用哪種方式query,按上面所述,如果api目標是要回傳像with這種資料結構,在這邊說join比較快沒有意義,因為還少算了將join result資料整理的時間。
假設今天的情境是前端會自己by文章id去整理資料,對後端來說當然會選擇用join寫法囉,又或者是某些統計數據,需要join後再做group計算,這時候就很適合用join。
還有一種情況是,假設今天多了第三篇文章,但是還沒有任何留言,這時候用with也會把第三篇文章的資料抓出來,只不過留言資料是空的,如果不希望把沒有留言的文章也找出來,這時候選擇用join是很直覺的,如果想達到跟with一樣的結果(把沒有留言的文章也找出來),就可以用leftJoin。其實,除了with還有has, whereHas的用法,也可以達到leftJoin的效果。
無論如何,根據api使用情境來選擇,個人認為是比較明智的做法,而不是不管怎樣都一律用一樣的方式來query。
本筆記參考:
  1. https://learnku.com/laravel/t/30387
  2. https://laracasts.com/discuss/channels/laravel/laravel-join-vs-with-in-performance
  3. https://learnku.com/laravel/t/16565/lets-talk-about-the-comparison-between-laravel-with-and-join-welcome-to-explain
  4. https://segmentfault.com/q/1010000006114982
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.