Почему этот запрос приводит к сканированию таблицы, когда я добавляю предложение OR? - PullRequest
0 голосов
/ 17 января 2019

Следующий запрос приводит к сканированию таблицы в таблице e (StationEvents) на ~ 250 тыс. Строк.

  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 происходит полное сканирование таблицы?

Имея всего одно предложение, кажется, что он сначала сканирует индекс станций и выполняет "обратное" объединение, чтобы вернуться к запрошенному результату, что имеет смысл.

Я ожидаю, что с предложением OR он должен отсканировать индекс Stations.Name, отсканировать индекс Keys.Name и, в основном, присоединиться к таблице StationEvents для выбранных KeyId и StationIds, так как это только соответствующие результаты. Все таблицы в этом объединении имеют настроенные внешние ключи.

Я что-то здесь неправильно понимаю? Что происходит, что требует полного сканирования таблицы?

В поисках объяснения и возможных путей повышения производительности этого запроса. Я использую MariaDB 10.3.

Ответы [ 2 ]

0 голосов
/ 18 января 2019
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 бесполезен, так как вы требуете совпадения на «правильном» столе.
  • Потенциальный недостаток: событие с g% в t и t2 будет засчитано дважды.

И вам нужны эти индексы:

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;
0 голосов
/ 17 января 2019

Я бы попробовал помочь оптимизатору запросов с 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...