编程

18条优化Laravel 数据库查询的建议(4-6)

663 2021-12-25 00:31:47

4. Count 查询使用query 而非 collection

计算数据库表的记录数,我们可能会这么做:

$posts = Post::all()->count(); //When using eloquent
$posts = DB::table('posts')->get()->count(); //When using query builder

这会产生以下查询

select * from posts

以上查询会先查询所有记录,将他们加载到collection对象中,再计算结果。如果记录少还好,随着表中记录增加,我们可能会耗尽内存。

因此,应该直接在数据库中计算总数:

$posts = Post::count(); //When using eloquent
$posts = DB::table('posts')->count(); //When using query builder

生成如下查询语句

select count(*) from posts

在sql中计算行数是慢处理过程,当数据库吉林市很多时表现不佳。所以需要尽量避免计算行数。

5. 通过渴求式加载避免N+1查询

你可能听过很多次这条建议。因此,长话短说。假设有以下场景:

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::all();
        return view('posts.index', ['posts' => $posts ]);
    }
 }
// posts/index.blade.php file
 
@foreach($posts as $post)
    <li>
        <h3>{{ $post->title }}</h3>
        <p>Author: {{ $post->author->name }}</p>
    </li>
@endforeach

以上代码查询所有记录,在网页中显示标题和作者。假设你的post模型有一个关联模型author。执行以上代码会返回以下查询:

select * from posts // Assume this query returned 5 posts
select * from authors where id = { post1.author_id }
select * from authors where id = { post2.author_id }
select * from authors where id = { post3.author_id }
select * from authors where id = { post4.author_id }
select * from authors where id = { post5.author_id }

如你所见,我们用一个查询来查询posts还有5个查询用来查询authors(假设有 5个 posts)。因此每个post 会单独作出一个查询来查找作者。 

因此如果有N个post, 会产生N+1次查询(1次post, N次查post的作者author)。这就是常见的N+1查询问题。要避免这个,通过如下方式渴求式(eager load)加载:

$posts = Post::all(); // Avoid doing this
$posts = Post::with(['author'])->get(); // Do this instead

执行以上代码会生成以下查询

select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )

6. 渴求加载嵌入式关联模型Eager load nested relationship

接上例,假设author隶属于某个团队team, 你同时要显示团队名称,试图如下:

@foreach($posts as $post)
    <li>
        <h3>{{ $post->title }}</h3>
        <p>Author: {{ $post->author->name }}</p>
        <p>Author's Team: {{ $post->author->team->name }}</p>
    </li>
@endforeach

执行以下代码

$posts = Post::with(['author'])->get();

会产生以下查询:

select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id = { author1.team_id }
select * from teams where id = { author2.team_id }
select * from teams where id = { author3.team_id }
select * from teams where id = { author4.team_id }
select * from teams where id = { author5.team_id }

如你所见,即便你渴求式加载关联模型authors,仍然会在team上有多的查询。我们可以按照以下方式解决:

$posts = Post::with(['author.team'])->get();

执行以上代码生成查询语句:

select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id in( { author1.team_id }, { author2.team_id }, { author3.team_id }, { author4.team_id }, { author5.team_id } )

通过渴求式加载嵌入式关联模型,我们可以将查询总数从11减少到3。