MySQL не использует первичный ключ при внутреннем соединении

У меня две таблицы: Таблица 1:

CREATE TABLE `lk_transaction_types` (
  `transactionTypeID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `isActive` tinyint(2) unsigned NOT NULL,
  `code` varchar(8) NOT NULL,
  `description` varchar(150) NOT NULL,
  `isInbound` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `isOutbound` tinyint(2) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`transactionTypeID`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

Таблица 2:

CREATE TABLE `ediLoad` (
  `loadID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `processID` int(10) unsigned NOT NULL,
  `success` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `transactionTypeID` tinyint(2) unsigned DEFAULT NULL,
  `escoID` int(10) unsigned DEFAULT NULL,
  `ldcID` int(10) unsigned DEFAULT NULL,
  `commodityType` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `filename` varchar(150) NOT NULL,
  `loadDate` datetime NOT NULL,
  `processed` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `processedDate` datetime DEFAULT NULL,
  `dataApplied` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `dataAppliedDate` datetime DEFAULT NULL,
  `errorID` tinyint(3) unsigned DEFAULT NULL,
  `error` tinyint(2) unsigned DEFAULT '0',
  `warning` tinyint(2) unsigned DEFAULT '0',
  PRIMARY KEY (`loadID`),
  KEY `idx_processID` (`processID`,`transactionTypeID`,`escoID`),
  KEY `idx_escoID` (`escoID`),
  KEY `idx_filename` (`success`,`filename`),
  KEY `idx_bulk` (`processed`,`loadDate`),
  KEY `idx_loadDate` (`loadDate`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=35086005 DEFAULT CHARSET=utf8;

При попытке выполнить простой запрос он не использует первичный ключ в таблице lk_transaction_types:

SELECT COUNT(0)
FROM edi.ediLoad l
INNER JOIN edi.lk_transaction_types lk 
  ON  lk.transactionTypeID = l.transactionTypeID
WHERE l.escoID = 2
AND lk.isActive = 1
AND lk.isInbound = 1;

Запрос выполняется очень медленно. Итак, я бегу объяснять

EXPLAIN SELECT COUNT(0)
FROM edi.ediLoad l
INNER JOIN edi.lk_transaction_types lk 
  ON  lk.transactionTypeID = l.transactionTypeID
WHERE l.escoID = 2
AND lk.isActive = 1
AND lk.isInbound = 1;

Это возвращает

id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra
1   SIMPLE  lk  NULL    ALL PRIMARY NULL    NULL    NULL    31  3.23    Using where
1   SIMPLE  l   NULL    ref idx_escoID  idx_escoID  5   const   71580   10.00   Using where

Это похоже на простой запрос с одним соединением по первичному ключу. Почему не используется первичный ключ? Я даже пробовал добавить в соединение FORCE INDEX FOR JOIN (PRIMARY), но он по-прежнему не использует первичный ключ. Любая помощь была бы замечательной. Спасибо!

Несмотря на то, что концептуально, WHERE обрабатывается после соединения, MySQL пытается уменьшить количество соединяемых строк, проверяя сначала соответствующие критерии в WHERE. Поскольку ediLoad не имеет индекса, начинающегося с transactionTypeID, и представляет собой большую таблицу; он, вероятно, игнорирует относительно бесполезный pk edi.

Uueerdo 09.04.2018 22:51
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
1
1
816
1

Ответы 1

Чтобы немного подробнее рассказать о том, что @Uueerdo говорит в комментарии выше: MySQL не может использовать индекс idx_processID в этом случае, поскольку transactionTypeID является вторым столбцом в индексе, а первый столбец (processID) не является частью оператора where .

Если вы добавите новый ключ только с transactionTypeID, он будет использован.

От https://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html:

MySQL can use multiple-column indexes for queries that test all the columns in the index, or queries that test just the first column, the first two columns, the first three columns, and so on. If you specify the columns in the right order in the index definition, a single composite index can speed up several kinds of queries on the same table.

Добавление ключа ускоряет запрос, но просто за счет фильтрации ediLoad. Он по-прежнему не использует ПК для соединения

Michael Shapiro 10.04.2018 16:52

@MichaelShapiro: тип данных для ediLoad.transactionTypeID - tinyint (2), а в lk_transaction_types - int (10), что, как я полагаю, является проблемой. Сделайте их равными (предположительно, оба int (10)), и я считаю, что у вас все будет в порядке.

Dag Sondre Hansen 11.04.2018 08:24

Другие вопросы по теме