按相关表列排序表的最快方法
Posted
技术标签:
【中文标题】按相关表列排序表的最快方法【英文标题】:Fastest way to order table by related table column 【发布时间】:2019-10-31 16:06:31 【问题描述】:我有 2 个表 cmets 和图像
我正在尝试先订购有图像的 cmets,然后再订购没有图像的 cmets。
我使用下面的这个查询来解决这个问题,但是在大数据上它非常慢,大约需要 12 秒
$data = Comment::withCount([
'images' => function ($query) use ($shop)
$query->where('shop_name', $shop);
,
])->where('shop_name', $shop)->orderBy('images_count', 'desc')->paginate(10);
我怎样才能提高性能,或者有没有其他方法可以更快地获得类似的结果?
【问题讨论】:
您可以发布表格的示例数据吗? 【参考方案1】:问题在于 Laravel 如何使 withCount()
工作 - 它会生成如下内容:
SELECT `comments`.*,
(SELECT Count(*)
FROM `images`
WHERE `comment_id`.`id` = `comments`.`id`
AND `images`.`shop_name` = 'your shop') AS `images_count`
FROM `comments`
WHERE `shop_name` = 'your_shop'
ORDER BY `images_count`
这将强制 mysql 为指定商店的每个评论执行 count()
子查询。
你需要做的是把这个 correlated 子查询(对每一行执行)变成一个 independent 查询(只执行一次),然后利用连接让 MySQL 配对:
$imagesCountQuery = DB::table('comments')
->selectRaw('comments.id AS comment_id, COUNT(comments.id) AS images_count')
->join('images', 'images.comment_id', '=', 'comments.id') /* !!! */
->where('images.shop_name', '=', $shop)
->groupBy('comments.id');
$data = Comment::joinSub($imagesCountQuery, 'images_count_sub', function ($join)
$join->on('comments.id', '=', 'images_count_sub'.'comment_id'); /* !!! */
)->where('shop_name', $shop)->orderBy('images_count_sub.images_count', 'desc')->paginate(10);
!!! - 应修改此行以表示您的评论的 images
关系。在此示例中,我只是假设它是 hasMany
关系,因为您没有在问题中指出这一点。
【讨论】:
非常感谢,但是在 laravel 5.5 或更低版本中不支持 joinSub,它在我阅读时从 5.6 开始,所以我收到一个错误,因此我将它添加到 laravel 5.5 并且查询有效,我的查询花了9.5 秒完成,而你花了 3.1 秒,这比旧方法快 2 倍,如果我会等待,如果有的话,可以减少更多,如果没有,我会标记你的答案,因为它比我能达到的更好 @ProgrammingSkills 还确保您的表使用连接/where 子句中的列的索引(shop_name
列通常是可疑的)。也许您也可以在数据库方面对其进行优化,以赢得一点时间。
shop_name 是一个索引,我会检查更多,非常感谢
我今天注意到这不起作用,因为 withCount 它只会添加关系并仅返回所有带有图像的 cmets,而 withCount 返回所有 cmets 并按具有图像的 cmets 对它们进行排序。但是由于 joinSub 的关系,您的删除了所有没有图像的 cmets
@ProgrammingSkills 那是因为joinSub
进行了内部连接。如果您需要显示每条记录,则需要进行外部联接-在您的情况下,请使用leftJoinSub
。【参考方案2】:
您需要在 Comment 模型中创建一个 hasMany 关系。
class Comment extends \Eloquent
public function images()
return $this->hasMany('Images', 'comment_id');
现在您可以使用以下查询来获取记录。
$comments = Comments::with('images')->get()->sortBy(function($comment)
return $comment->images->count();
);
【讨论】:
他已经拥有images
关系,因为他在withCount()
中使用它。您的解决方案是在执行 MySQL 查询后订购 php 端,这是:a)不好的做法 b)不是他想要的。以上是关于按相关表列排序表的最快方法的主要内容,如果未能解决你的问题,请参考以下文章