2016-05-20 2 views
3

У меня есть две таблицы, identities и events.Почему этот MySQL IN занимает намного больше времени, чем ГДЕ ИЛИ?

identities имеет только две колонки, identity1 и identity2 и оба имеют индекс ХАШ.

events имеет ~ 50 столбцов и столбец _p имеет ИНДЕКС ХОШ.

CREATE TABLE `identities` (
    `identity1` varchar(255) NOT NULL DEFAULT '', 
    `identity2` varchar(255) DEFAULT NULL, 
    UNIQUE KEY `uniques` (`identity1`,`identity2`), 
    KEY `index2` (`identity2`) USING HASH, 
    KEY `index1` (`identity1`) USING HASH 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

-

CREATE TABLE `events` (
    `rowid` int(11) NOT NULL AUTO_INCREMENT, 
    `_p` varchar(255) NOT NULL, 
    `_t` int(10) NOT NULL, 
    `_n` varchar(255) DEFAULT '', 
    `returning` varchar(255) DEFAULT NULL, 
    `referrer` varchar(255) DEFAULT NULL, 
    `url` varchar(255) DEFAULT NULL, 

    [...] 

    `fcc_already_sells_online` varchar(255) DEFAULT NULL, 
    UNIQUE KEY `_p` (`_p`,`_t`,`_n`), 
    KEY `rowid` (`rowid`), 
    KEY `IDX_P` (`_p`) USING HASH 
) ENGINE=InnoDB AUTO_INCREMENT=5231165 DEFAULT CHARSET=utf8; 

Итак, почему же этот вопрос:

SELECT SQL_NO_CACHE * FROM events WHERE _p IN (SELECT identity2 FROM identities WHERE identity1 = '[email protected]') ORDER BY _t 

занимает ~ 40 секунд, в то время как этот:

SELECT SQL_NO_CACHE * FROM events WHERE _p = '[email protected]' OR _p = '[email protected]' OR _p = '[email protected]' OR _p = '[email protected]' ORDER BY _t 

принимает только 20мс, когда они в основном то же самое?


редактировать:

Этот внутренний запрос занимает 3,3ms:

SELECT SQL_NO_CACHE identity2 FROM identities WHERE identity1 = '[email protected]'

+0

Ссылка: 'WHERE field IN (список значений)' может рассматриваться как 'WHERE field = value1 OR field = value2 OR ...' but 'WHERE IN (subquery)' can not, это другое для MySQL. Вы можете прочитать немного больше [здесь] (http://stackoverflow.com/questions/19654878/inner-join-select-ab-on-a-and-b-vs-where-ab-in-selecta-b- in-mysql/19655121 # 19655121) –

+0

Nice, @AlmaDo, отправьте это как ответ, и я приму это. – dmmd

+0

Кроме того, если бы вы могли подробнее рассказать об этом, это было бы здорово. – dmmd

ответ

2

Причина:

MySQL обрабатывает условия IN <static values list> и IN <sub-query> как разные вещи. В documentation хорошо указано, что второй равен = ANY() запросу, который не может использовать индекс, даже если этот индекс существует. MySQL просто не изобретателен, чтобы это сделать. Напротив, первый рассматривается как простое сканирование диапазона, когда индекс означает, что MySQL может легко использовать индекс.

Возможные пути решения:

Как я вижу, есть обходные пути, и вы уже даже упомянул один из них. Таким образом, это может быть:

  • Использование JOIN. Если есть поле для присоединения, это, скорее всего, лучший способ решить проблему. На самом деле, поскольку версия 5.6 MySQL уже tries to enforce this optimization, если это возможно, но это не работает в сложных случаях или в случае, если нет зависимого подзапроса (так что в основном, если MySQL не может «отслеживать» эту ссылку).Глядя на ваш случай, это не вариант, и на самом деле это то, что не происходит в вашем подзапросе.
  • Запрос суб-ресурса в приложении и формирование статического списка. Да, несмотря на распространенную практику, необходимо избегать нескольких запросов из-за накладных расходов на подключение/сеть/запрос, это тот случай, когда на самом деле он может работать. В вашем случае, даже если у вас есть что-то вроде 200 мс накладных расходов во всех пересчитанных материалах раньше, все равно стоит запросить под-ресурс самостоятельно и впоследствии заменить статический список на следующий запрос в приложении.
+0

Отлично, положите, спасибо! – dmmd

-1

this is already asked

легче управлять оператор IN, потому что это только конструкция, которая определяет оператор OR в нескольких условиях с = operator на одно и то же значение. Если вы используете оператор OR, оптимизатор может не учитывать, что вы всегда используете оператор = с тем же значением.

+0

Это не тот же вопрос ... Я использую внутренний запрос, это не так. – dmmd

-1

Поскольку ваш запрос вызывает этот внутренний запрос для каждой строки в таблице событий.

Во втором случае таблица indentity не используется.

Вместо этого вы должны использовать соединение.

+0

Спасибо. Я не могу использовать join, поскольку я возвращаю набор строк для запуска где-то. – dmmd

+0

Да, вы можете, например: SELECT SQL_NO_CACHE * FROM events e INNER JOIN identity i ON e._p = i.identity2 WHERE identity1 = '[email protected]' ORDER BY _t – mjpolak

+0

Не будет ли это потреблять больше ресурсов, чем используя статические значения для IN? Не уверен, что JOIN - хороший способ обхода проблемы, подумайте, пожалуйста, в своем ответе? (ps. не было меня, что ниспроверг вас) – dmmd

Смежные вопросы