使用三个表对列表进行排序的查询的优化

Optimization of query that uses three tables to order a list

所以我有一个完全按预期工作但需要大量优化的查询。我正在使用软件来跟踪我网站上的加载时间,我网站上所有加载时间的 97.8% 是这一功能的结果。所以在开始查询之前先解释一下我的数据库。

首先我有一部电影 table、一场比赛 table 和一票 table。电影可以参加很多比赛,比赛有很多电影,因为这是多对多的关系,我们有一个枢轴 table 来显示他们的关系(电影竞赛)但是当加载比赛页面时,这些电影需要按顺序排列他们的投票(最多投票在顶部。最少在底部)。

现在在下面的查询中你可以看到我在做什么,从 filmsCompetition table 中抓取与当前比赛 ID $competition->id 匹配的电影,然后我按总数排序那部电影的票数。就像我说的那样,这是可行的,但效率很高,但我想不出另一种方法。

$films =  DB::select( DB::raw("SELECT f.*, COUNT(v.value) AS totalVotes
    FROM filmCompetition AS fc
    JOIN films AS f ON f.id = fc.filmId AND fc.competitionId = '$competition->id'
    LEFT JOIN votes AS v ON f.id = v.filmId AND v.competitionId = '$competition->id'
    GROUP BY f.id
    ORDER BY totalVotes DESC
    ") );

对于此查询,您需要在 filmCompetition(competitionId, filmId)films(id) 和 votes(filmId, competitionId) 上建立索引。

但是,这样写查询可能更有效:

SELECT f.*,
       (SELECT COUNT(v.value)
        FROM votes v
        WHERE v.filmId = f.id and v.competitionId = '$competition->id'
       ) AS totalVotes
FROM films f
WHERE EXISTS (SELECT 1
              FROM FilmCompetition fc
              WHERE fc.FilmId = f.Filmid AND 
                    fc.competitionId = '$competition->id'
             )
ORDER BY TotalVotes DESC

这样就省去了外部聚合,这应该是性能上的胜利。对于此版本,索引为 FilmCompetition(FilmId, CompetitionId)Votes(FilmId, CompetitionId)

我实际最终使用的解决方案有点不同,但由于戈登回答了问题,我将他标记为正确答案。

我的解决方案 为了真正解决这个问题,我采用了一种稍微不同的方法,而不是尝试在 SQL 中完成所有这些,我分别执行了三个查询,然后在 PHP 中将它们连接在一起。虽然在我的情况下这样做可能会慢一些,但我原来的方式花了大约 15 秒,戈登的方式花了大约 7 秒,而我的新方式花了大约 600 毫秒。