编程

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

1556 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 个查询用来查询a uthors(假设有 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. 渴求加载嵌入式关联模型

接上例,假设 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。