使用三个表对列表进行排序的查询的优化
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 毫秒。
所以我有一个完全按预期工作但需要大量优化的查询。我正在使用软件来跟踪我网站上的加载时间,我网站上所有加载时间的 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 毫秒。