Получение MySQL, чтобы использовать индекс / ключ, 1 столбец в где и 2 в порядке - PullRequest
1 голос
/ 19 апреля 2011

Как заставить MySQL использовать ключ / индекс со следующей структурой таблицы и запросом?

-- the table
CREATE TABLE `country` (
  `id` int(11) NOT NULL auto_increment,
  `expiry_date` datetime NOT NULL,
  `name` varchar(50) collate utf8_unicode_ci NOT NULL,
  `symbol` varchar(5) collate utf8_unicode_ci NOT NULL,
  `exchange_rate` decimal(11,5) NOT NULL default '1.00000',
  `code` char(3) collate utf8_unicode_ci NOT NULL,
  `currency_code` varchar(3) collate utf8_unicode_ci NOT NULL,
  `display_order` smallint(6) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `code` (`code`),
  KEY `currency_code` (`currency_code`),
  KEY `display_order` (`expiry_date`,`name`,`display_order`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- the query
SELECT `country`.* 
FROM `country` 
WHERE `country`.`expiry_date` = 0 
ORDER BY `country`.`display_order` ASC, `country`.`name` ASC;

Я пытаюсь заставить его использовать ключ, потому что запрос с 180 в результатезанимает 0,0013 с и является самым медленным запросом на странице (в 3 раза дольше, чем следующий самый медленный).Насколько я понимаю, запрос должен использовать display_order индекс / ключ.

Ответы [ 4 ]

2 голосов
/ 19 апреля 2011

Измените его на:

CREATE TABLE `country` (
  `id` int(11) NOT NULL auto_increment,
  `expiry_date` datetime NOT NULL,
  `name` varchar(50) collate utf8_unicode_ci NOT NULL,
  `symbol` varchar(5) collate utf8_unicode_ci NOT NULL,
  `exchange_rate` decimal(11,5) NOT NULL default '1.00000',
  `code` char(3) collate utf8_unicode_ci NOT NULL,
  `currency_code` varchar(3) collate utf8_unicode_ci NOT NULL,
  `display_order` smallint(6) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `code` (`code`),
  KEY `currency_code` (`currency_code`),
  KEY `expiry` (`expiry_date`,`name`,`display_order`) <<- renamed key for clarity
  /* always name compound keys for their left-most parts*/ 
  KEY `name` (`name`)  <<-- new key here
  KEY `display` (`display_order`) <<--new key here
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- the query
SELECT `country`.* 
FROM `country` 
WHERE `country`.`expiry_date` = 0 
ORDER BY `country`.`display_order` ASC, `country`.`name` ASC;

Сложные индексы хитрые
MySQL не использовал индекс для name в составном индексе, потому что name был посередине, а MySQL использует только части индекса, если эта часть является самой левой частью составного индекса .
То же самое относится к индексу порядка отображения полей. Составной индекс, в котором есть display_order, использует это поле как крайнюю правую часть и, следовательно, не будет сортировать.

Решение
Сделайте отдельный индекс для поля name,
и отдельный индекс для поля display_order.

Иногда MySQL не использует индекс, даже если он доступен. Одно из обстоятельств, при которых это происходит, - это когда оптимизатор оценивает, что использование индекса потребовало бы от MySQL доступа к очень большому проценту строк в таблице. (В этом случае сканирование таблицы, вероятно, будет намного быстрее, поскольку требует меньше запросов.) Однако, если такой запрос использует LIMIT для извлечения только некоторых строк, MySQL в любом случае использует индекс, потому что он может гораздо быстрее найти несколько строк для возврата в результате.

Также, если большой процент строк имеет одинаковое значение для поля (> 40% (IIRC)) , MySQL не будет использовать индекс.

См .: http://dev.mysql.com/doc/refman/5.1/en/mysql-indexes.html

См .: http://dev.mysql.com/doc/refman/5.1/en/index-hints.html
О том, как форсировать индексы согласно предложению FractalizeR.
Убедитесь, что ваш выбор времени после форсирования индекса
В таком простом запросе MySQL вряд ли будет неправильным, а время выбора 0,0013 секунды говорит о том, что в таблице несколько строк.
Индексы не работают так, как вы ожидаете, когда в таблице несколько строк из-за правила процентного соотношения, указанного выше.

Обратите внимание, что в этом случае форсирование индекса не сработало бы, потому что вы не можете заставить MySQL использовать самую правую часть составного индекса. Он просто не может этого сделать.

1 голос
/ 19 апреля 2011

Последнее, что вы можете сделать, это использовать "FORCE INDEX", как указано в fractalizeR

1 голос
/ 19 апреля 2011

Ваш запрос имеет ORDER BY для столбцов {display_order} + {name}, а Ваш индекс с именем "display_order" фактически определен в столбцах {expiry_date} + {name} + {display_order}.

Порядок столбцов в индексе имеет значение. Вы можете воспользоваться индексом, если вам нужна сортировка фильтрации по столбцам, которые являются началом индекса. Это станет очевидным, если учесть, что индекс - это предварительно отсортированная информация.

Если вы хотите использовать индекс {display_order} + {name}, вам нужен индекс, который начинается с {display_order} + {name}. Например, {display_order} + {name} или {display_order} + {name} + {expiry_date}.

Таким образом, чтобы оптимизировать ваш запрос, вы должны изменить свой индекс в таблице или условие SORT в запросе.

1 голос
/ 19 апреля 2011

Если вы считаете, что MySQL неразумно выбирает индексы, и вы уверены в этом, используйте FORCE INDEX подсказку индекса: http://dev.mysql.com/doc/refman/5.1/en/index-hints.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...