Laravel 框架如何优雅的写出文章的上一篇和下一篇

上一页和下一页的原理#

以 id 排序,获取排序在当前 id 的上一篇文章的信息,以及当前 id 的下一篇文章的信息。

一、简单直接方式#

$last = Article::where('id', '<', $info->id)->orderBy('id', 'desc')->first();
$next = Article::where('id', '>', $info->id)->orderBy('id', 'asc')->first();

能生成取到两个文章的数据。

二、升级方式#

$last = Article::where('id', '<', $info->id)->latest('id')->first();
$next = Article::where('id', '>', $info->id)->oldest('id')->first();

使用 laravel 内置的 latestoldest 来代替 orderBy,看起来优雅了一点,但是做的事情,跟上面的那个方式一样。分两次请求数据库,进而获取两篇文章信息。

三、再次升级#

$sql = Article::where('id', '<', $info->id)->latest('id')->take(1);
$list = Article::where('id', '>', $info->id)->oldest('id')->take(1)->union($sql)->orderBy('id', 'asc')->get();

同样能获取到上一篇和下一篇的两篇文章。在不丢失优雅姿态的同时,使用 union 方法,将两个查询联合成了一个,减少了数据库的请求压力。
同时使用 orderBy 的方法对查询到的两个数据,进行了排序。上一篇的文章信息就是下标为 0 的那只。下一篇的文章信息,就是下标为 1 的那只。

使用下面的 ->dd() 方式打印出的 mysql 的语句详情。

$list = Article::where('id', '>', $info->id)->oldest('id')->take(1)->union($sql)->orderBy('id', 'asc')->dd();

结果如下:

(select * from `articles` where `id` > ? order by `id` asc limit 1) union (select * from `articles` where `id` < ? order by `id` desc limit 1) order by `id` asc
array:2 [0 => 3
  1 => 3
]

可以看出,就是两个查询联合成的一条语句查询。

四、性能测试#

多谢大家踊跃的点赞和评论,有些小伙伴私信我说,使用 union 查询的时候,数据库不会使用索引。我也是抱着怀疑的态度,对自己的 sql 语句进行了 EXPLAIN 测试。

EXPLAIN (select * from `articles` where `id` > 3 order by `id` asc limit 1) union (select * from `articles` where `id` < 3 order by `id` desc limit 1) order by `id` asc

下面是测试的结果,虽然我的数据量不大,但是就数据库给我的反馈,我们也能看懂一二。
Laravel 框架如何优雅的写出文章的上一篇和下一篇

结论

  1. id 为 1 和 2 的 possible_keyskey 的值都是 PRIMARY,这就足以说明,查询 articles 的时候,使用的是索引查询,而且是主键索引。
  2. 也就意味着,几十在数据量很大的情况下,查出这两篇文章的速度也都相对客观。
  3. 第三条记录是最外层的 union,它是没有使用索引。因为它也没有索引可用,它表是 <union1, 2>,它是个临时生成的表,里面只有上面查询的两条记录。这也就意味着,articles 表的数据量的大小,对它的影响不大。
  4. Extra 字段中的 Using temporayr 是数据库为了解决查询,创建一个临时表来容纳结果。Using filesort 是做最后一次排序使用的,如果对结果不进行排序的话,是没有 Using filesort 显示的。
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4

结果集合并,你说实际查询几次?

4年前 评论

@keyboby 你是指数据库层面还是 php 层面?我简单的理解为 phpmysql 发送几次查询请求,就是查询几次。

4年前 评论
yybawang

union 数据量多会很慢,索引也没用 其实不管是现在还是以后考虑,都应该查两次 sql 就好了。记住任何业务都不要用 union

4年前 评论

@yybawang :joy: :joy:感觉好像 union 的联合查询一无是处 :smiley:

4年前 评论