按相关表列排序表的最快方法

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)不是他想要的。

以上是关于按相关表列排序表的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

更新具有来自同一表的最新相关 id 的表列

Laravel Eloquent 按关系表列排序

Java 常用排序

链表14:单链表的排序

Mysql查询连接两个表按一个表列排序

Laravel Eloquent按关系表列排序