Mysql не использует индекс при самостоятельном присоединении к таблице с индексированными критериями - PullRequest
0 голосов
/ 21 января 2019

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

Схема таблицы для EmployeeDetails

EmployeeDetails | CREATE TABLE `EmployeeDetails` (
  `CompanyId` bigint(19) NOT NULL DEFAULT '0',
  `EmployeeId` bigint(19) NOT NULL DEFAULT '0',
  `ParentEmployeeId` bigint(19) DEFAULT NULL,
  PRIMARY KEY (`EmployeeId`),
  KEY `EmployeeDetails_FK1_IDX` (`CompanyId`),
  KEY `EmployeeDetails_FK2_IDX` (`ParentEmployeeId`),
  CONSTRAINT `EmployeeDetails_FK1` FOREIGN KEY (`CompanyId`) REFERENCES `CompanyDetails` (`CompanyId`) ON DELETE CASCADE,
  CONSTRAINT `EmployeeDetails_FK2` FOREIGN KEY (`ParentEmployeeId`) REFERENCES `EmployeeDetails` (`EmployeeId`) ON DELETE CASCADE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

Схема таблицы для CompanyDetails

| CompanyDetails | CREATE TABLE `CompanyDetails` (
  `CompanyId` bigint(19) NOT NULL DEFAULT '0',
  `NAME` varchar(25) NOT NULL DEFAULT '',
  `DESCRIPTION` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`CompanyId`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
|

Первый запрос - первый выборотсканирует все записи в таблице EmployeeDetails и выполнит самостоятельное объединение (не будет использовать индекс KEY EmployeeDetails_FK1_IDX (CompanyId), который используется в критериях)

mysql> select * from EmployeeDetails INNER JOIN `EmployeeDetails` `t1` ON `EmployeeDetails`.`EmployeeId`=`t1`.`ParentEmployeeId` where `EmployeeDetails`.`CompanyId` IN(123);
Empty set (0.10 sec)

mysql> explain select * from EmployeeDetails INNER JOIN `EmployeeDetails` `t1` ON `EmployeeDetails`.`EmployeeId`=`t1`.`ParentEmployeeId` where `EmployeeDetails`.`CompanyId` IN(123)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
   partitions: NULL
         type: ALL
possible_keys: EmployeeDetails_FK2_IDX
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 7999
     filtered: 100.00
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: EmployeeDetails
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY,EmployeeDetails_FK1_IDX
          key: PRIMARY
      key_len: 8
          ref: db.t1.ParentEmployeeId
         rows: 1
     filtered: 5.00
        Extra: Using where
2 rows in set, 1 warning (0.01 sec)

Второй запрос - сначала выберите записи из EmployeeDetails, используя EmployeeDetails_FK1_IDX (CompanyId`) индексировать и сканировать только те записи, которые соответствуют критериям, и самостоятельно объединяться

mysql> select * from EmployeeDetails INNER JOIN `EmployeeDetails` `t1` ON `t1`.`EmployeeId`=`EmployeeDetails`.`ParentEmployeeId` where `EmployeeDetails`.`CompanyId` IN(123);
Empty set (0.00 sec)

mysql> explain select * from EmployeeDetails INNER JOIN `EmployeeDetails` `t1` ON `t1`.`EmployeeId`=`EmployeeDetails`.`ParentEmployeeId` where `EmployeeDetails`.`CompanyId` IN(123)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: EmployeeDetails
   partitions: NULL
         type: ref
possible_keys: EmployeeDetails_FK1_IDX,EmployeeDetails_FK2_IDX
          key: EmployeeDetails_FK1_IDX
      key_len: 8
          ref: const
         rows: 54
     filtered: 100.00
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: db.EmployeeDetails.ParentEmployeeId
         rows: 1
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.00 sec)

Почему это происходит?

1 Ответ

0 голосов
/ 21 января 2019

Как видно из EXPLAIN, «EmployeeDetails_FK2_IDX» указан в качестве возможного ключа. Если оптимизатор MySQL не использует его, просто он не уменьшит время запроса.

Попробуйте заполнить таблицу данными (я проверил, и она работает как есть), и вы должны увидеть, что оптимизатор MySQL будет использовать индекс для объединения, не просматривая их порядки. Потому что порядок объединений не влияет на алгоритмы оптимизатора MySQL.

Также проверьте STRAIGHT_JOIN

...