Использование IN с подзапросом не использует индекс

У меня есть следующий запрос

select * from mapping_channel_fqdn_virtual_host where id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

объяснение вышеуказанного запроса дает мне следующий результат:

explain select * from mapping_channel_fqdn_virtual_host where id in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+----+-------------+-----------------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table                             | partitions | type    | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | mapping_channel_fqdn_virtual_host | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |   10 |   100.00 | Using where |
+----+-------------+-----------------------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0,00 sec)

id — первичный ключ таблицы. Похоже, что это запрос диапазона, и он использует первичный ключ в качестве индекса для этого.

Когда я пытаюсь объяснить следующий запрос, я получаю разные результаты:

explain select * from mapping_channel_fqdn_virtual_host where id in (select max(id) from mapping_channel_fqdn_virtual_host group by channel_id, fqdn_virtual_host_id);     
+----+-------------+-----------------------------------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+
| id | select_type | table                             | partitions | type  | possible_keys    | key              | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-----------------------------------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | mapping_channel_fqdn_virtual_host | NULL       | ALL   | NULL             | NULL             | NULL    | NULL | 4849 |   100.00 | Using where |
|  2 | SUBQUERY    | mapping_channel_fqdn_virtual_host | NULL       | index | idx_channel_fqdn | idx_channel_fqdn | 8       | NULL | 4849 |   100.00 | Using index |
+----+-------------+-----------------------------------+------------+-------+------------------+------------------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0,00 sec)

idx_channel_fqdn — ключ составного индекса для пары столбцов, используемой в предложении groupby. Но при использовании подзапроса основной запрос перестает использовать индекс, как это было раньше. Можете ли вы объяснить, почему это происходит?

Попробовал запрос JOIN, предложенный Ануаром:

explain select *  from mapping_channel_fqdn_virtual_host as x join (select max(id) as ids from mapping_channel_fqdn_virtual_host group by channel_id, fqdn_virtual_host_id) as y on x.id=y.ids; 
+----+-------------+-----------------------------------+------------+--------+------------------+------------------+---------+-------+------+----------+-------------+
| id | select_type | table                             | partitions | type   | possible_keys    | key              | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------------------------------+------------+--------+------------------+------------------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | <derived2>                        | NULL       | ALL    | NULL             | NULL             | NULL    | NULL  | 4849 |   100.00 | Using where |
|  1 | PRIMARY     | x                                 | NULL       | eq_ref | PRIMARY          | PRIMARY          | 4       | y.ids |    1 |   100.00 | NULL        |
|  2 | DERIVED     | mapping_channel_fqdn_virtual_host | NULL       | index  | idx_channel_fqdn | idx_channel_fqdn | 8       | NULL  | 4849 |   100.00 | Using index |
+----+-------------+-----------------------------------+------------+--------+------------------+------------------+---------+-------+------+----------+-------------+
3 rows in set, 1 warning (0,00 sec)

Судя по типам index и eq_ref, лучше использовать JOIN, чем подзапрос? Можете ли вы немного подробнее объяснить результат выражения объяснения соединения g?


person Apostolos    schedule 03.11.2017    source источник
comment
Будет ли он также выбирать сканирование диапазона при запросе id in (100,101,...,110)? Мое дикое предположение состоит в том, что оптимизатор видит, что вы запрашиваете первые десять строк, поэтому нет необходимости запрашивать индекс, чтобы найти начальную запись диапазона. И id использует индекс, чтобы найти максимальное значение во втором примере.   -  person hage    schedule 03.11.2017
comment
@ Stavr00 подзапрос может возвращать более одного значения, поскольку это группа по запросу.   -  person Apostolos    schedule 03.11.2017
comment
@hage с рабочим идентификатором в (100, 101,..., 110) также выбирает сканирование диапазона.   -  person Apostolos    schedule 03.11.2017
comment
да, тогда это так, как указал Ануар в своем ответе. Оптимизатор просто решил, что запрашивать индекс дороже, чем выполнять сканирование диапазона... Однако не знаю, возможно ли увидеть предполагаемые затраты в MySQL.   -  person hage    schedule 03.11.2017
comment
Возможный дубликат MySQL не использует индексы с предложением WHERE IN?   -  person Stavr00    schedule 03.11.2017
comment
Итог: IN ( SELECT ... ) общеизвестно плохо влияет на производительность. Переключитесь на JOIN (в данном случае) или EXISTSнекоторых других случаях).   -  person Rick James    schedule 20.01.2018


Ответы (1)


Вы можете увидеть ответы на этот вопрос. Вы найдете идеи .

Я цитирую некоторые ответы

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

Вкратце, попробуйте принудительно указать индекс:

SELECT *
FROM mapping_channel_fqdn_virtual_host FORCE INDEX (name of the index you want to use)
WHERE (mapping_channel_fqdn_virtual_host.id IN (1,2,3,4,5,6,7,8,9,10));

Или используйте вместо этого JOIN и посмотрите объяснение

SELECT * FROM mapping_channel_fqdn_virtual_host mcf
JOIN (select max(id) as ids from mapping_channel_fqdn_virtual_host group by channel_id, fqdn_virtual_host_id)) AS mcfv 
ON mcf.id = mcfv.ids;
person AnouarZ    schedule 03.11.2017
comment
Использование JOIN, скорее всего, будет быстрее, поскольку оптимизатор начнет работу с «производной» таблицы (подзапроса). Это сведется к небольшому количеству строк. Тогда только это небольшое число будет использоваться для доступа к mcf. EXPLAIN не знает, сколько строк, поэтому этот столбец можно/нужно игнорировать. - person Rick James; 20.01.2018