为什么添加OR子句时此查询会导致表扫描?

时间:2019-01-17 20:06:53

标签: sql mariadb

以下查询将对表eStationEvents)进行约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。

2 个答案:

答案 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毫无用处,因为您需要在“正确的”表上进行匹配。
  • 潜在的缺陷:t和t2都占g%的事件将被计数两次。

您需要这些索引:

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;