以下查询将对表e
(StationEvents
)进行约25万行的表扫描。
SELECT COUNT(e.Id)
FROM `StationEvents` AS `e`
LEFT JOIN `Keys` AS `t` ON `e`.`KeyId` = `t`.`Id`
LEFT JOIN Stations AS `t2` ON `e`.`StationId` = `t2`.`Id`
WHERE (t2.Name like 'g%') or (t.name like 'g%')
但是,当我删除OR
子句的左侧或右侧时,查询的性能要好得多。例如,如果我删除右侧,它将利用Stations
上的索引,仅扫描约600条记录。为什么添加OR子句会导致完整的表扫描?
仅使用一个子句,似乎首先扫描Stations索引,然后进行“反向”连接以返回到请求的结果,这很有意义。
我的期望是,使用OR子句,它应该扫描Stations.Name
索引,扫描Keys.Name
索引,并基本上返回到所选KeyId
和{的StationEvents表。 {1}},因为这些是唯一相关的结果。此联接中的所有表都配置了外键。
我在这里误会了什么吗?发生什么情况需要进行完整的表扫描?
寻找解释,以及提高此查询性能的可能方法。我正在使用MariaDB 10.3。
答案 0 :(得分:2)
我会尝试使用OR expansion
帮助查询优化器:
SELECT COUNT(*)
FROM (
SELECT e.id
FROM `StationEvents` AS `e`
LEFT JOIN `Keys` AS `t` ON `e`.`KeyId` = `t`.`Id`
LEFT JOIN AspNetUsers AS `t0` ON `t`.`UserId` = `t0`.`Id`
LEFT JOIN StationStatuses AS `t1` ON `e`.`StatusId` = `t1`.`Id`
LEFT JOIN Stations AS `t2` ON `e`.`StationId` = `t2`.`Id`
LEFT JOIN Regions AS `t3` ON `t2`.`RegionId` = `t3`.`Id`
WHERE (t2.Name like 't%')
UNION ALL
SELECT e.id
FROM `StationEvents` AS `e`
LEFT JOIN `Keys` AS `t` ON `e`.`KeyId` = `t`.`Id`
LEFT JOIN AspNetUsers AS `t0` ON `t`.`UserId` = `t0`.`Id`
LEFT JOIN StationStatuses AS `t1` ON `e`.`StatusId` = `t1`.`Id`
LEFT JOIN Stations AS `t2` ON `e`.`StationId` = `t2`.`Id`
LEFT JOIN Regions AS `t3` ON `t2`.`RegionId` = `t3`.`Id`
WHERE (t2.Name like 'g%')
) sub;
答案 1 :(得分:0)
SELECT
(
SELECT COUNT(*)
FROM `StationEvents` AS `e`
JOIN `Keys` AS `t` ON `e`.`KeyId` = `t`.`Id`
WHERE (t.Name like 'g%')
) +
(
SELECT COUNT(*)
FROM `StationEvents` AS `e`
JOIN Stations AS `t2` ON `e`.`StationId` = `t2`.`Id`
WHERE (t2.Name like 'g%')
) AS the_count;
注意:
SELECT
用作表达式(带括号)。COUNT(*)
是通常的模式。LEFT
毫无用处,因为您需要在“正确的”表上进行匹配。您需要这些索引:
On `t` and `t2`: INDEX(Name, Id)
On StationEvents: INDEX(StationId), INDEX(KeyId)
可能这会给您相同的答案吗?
SELECT
(
SELECT COUNT(*)
FROM `Keys` AS `t` ON `e`.`KeyId` = `t`.`Id`
WHERE (t.Name like 'g%')
) +
(
SELECT COUNT(*)
FROM Stations AS `t2` ON `e`.`StationId` = `t2`.`Id`
WHERE (t2.Name like 'g%')
) AS the_count;