之前在執行某些專案的時候,常看到->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。
本筆記參考:
- https://learnku.com/laravel/t/30387
- https://laracasts.com/discuss/channels/laravel/laravel-join-vs-with-in-performance
- https://learnku.com/laravel/t/16565/lets-talk-about-the-comparison-between-laravel-with-and-join-welcome-to-explain
- https://segmentfault.com/q/1010000006114982