我想返回所有在特定matches
踢足球的国家({1)}。下表中定义了数据:
竞争
date
competition_seasons
id | country_id | name
50 1 Premier League
competition_rounds
id | competition_id | name
70 50 2019
匹配
id | season_id | name
58 70 Regular Season
id | round_id | home | away | result | datetime
44 58 22 87 1 - 0 2019-03-16:00:00
表中存储了不同的比赛,然后每个比赛可以有多个competition
存储在season
中。 competition_seasons
也可以有不同的竞争者season
,这些竞争者存储在rounds
中。
所有competition_rounds
都存储在matches
表中,并按match
分组。
我为API编写了此方法:
round_id
有成千上万个按ID进行组织的记录,但是查询花费了大约6、7秒才能返回在指定日期播放的所有$app->get('/country/get_countries/{date}', function (Request $request, Response $response, array $args)
{
$start_date = $args["date"] . " 00:00";
$end_date = $args["date"] . " 23:59";
$sql = $this->db->query("SELECT n.* FROM country n
LEFT JOIN competition c ON c.country_id = n.id
LEFT JOIN competition_seasons s ON s.competition_id = c.id
LEFT JOIN competition_rounds r ON r.season_id = s.id
LEFT JOIN `match` m ON m.round_id = r.id
WHERE m.datetime BETWEEN '" . $start_date . "' AND '" . $end_date . "'
GROUP BY n.id");
$sql->execute();
$countries = $sql->fetchAll();
return $response->withJson($countries);
});
。
如何优化此过程?
性能
更新
如果我这样做,我会发现一件有趣的事情:
countries
查询的速度非常快,所以我猜SELECT round_id, DATE("2019-03-18") FROM `match`
字段会使连接部分变慢,对此有任何想法吗?
表结构
datetime
答案 0 :(得分:4)
首先,将查询写为:
SELECT n.*
FROM country n JOIN
competition c
ON c.country_id = n.id JOIN
competition_seasons s
ON s.competition_id = c.id JOIN
competition_rounds r
ON r.season_id = s.id JOIN
`match` m
ON m.round_id = r.id
WHERE m.datetime >= ? AND
m.datetime < ?
GROUP BY n.id;
此处的更改相对较小,不会影响性能。但是它们很重要:
JOIN
而不是LEFT JOIN
,因为您需要条件匹配。>=
和<
进行比较,因为它同时适用于日期和日期时间。您将需要在结束日期前增加1天,但不包括时间部分。然后,为了提高性能,您需要索引:
match(datetime, round_id)
competition_rounds(id, season_id)
competition_seasons(id, competition_id)
competition(id, country_id)
country(id)
实际上,第一个是最重要的。如果将各自的id
列声明为主键,则不需要最后四个。
答案 1 :(得分:1)
使用LEFT JOIN
,只能从上至下执行查询,这意味着将扫描最后一个表以查找之前表中条目的每个乘积。另外,使用LEFT JOIN
和GROUP BY
而不进行任何汇总都没有意义,因为它将始终返回所有国家/地区ID。话虽如此,我会这样重写它:
SELECT DISTINCT
c.country_id
FROM
competition c,
WHERE
EXISTS (
SELECT
*
FROM
competition_seasons s,
competition_rounds r,
`match` m
WHERE
s.competition_id = c.id
AND r.season_id = s.id
AND m.round_id = r.id
AND m.datetime BETWEEN ...
)
我所知道的所有RDB都将正确地对此进行优化。
请注意,(match.datetime, match.round_id)
上的2列索引(按此顺序)将对性能产生巨大影响。还是担心写入速度,建议至少在(match.datetime)
上使用单个列索引。
有关字符串索引的重要提示:在RDB中,字符串比较总是古怪的。确保为datetime列使用二进制排序规则或使用本机DATETIME格式。各种RDB可能无法在不区分大小写的列上使用索引。
请注意,我删除了n上的联接-只是添加了另一个PK查找来检查国家/地区表中仍然存在该国家/地区。如果没有任何ON DELETE CASCADE或其他可确保数据一致性的约束,则可以重新添加它,例如:
SELECT DISTINCT
n.id
FROM
country n
WHERE
EXISTS (
SELECT
*
FROM
competition c,
competition_seasons s,
competition_rounds r,
`match` m
WHERE
c.country_id=n.id
AND s.competition_id = c.id
AND r.season_id = s.id
AND m.round_id = r.id
AND m.datetime BETWEEN ...
)