mysql - 将 IN 与子查询一起使用不使用索引

标签 mysql sql sql-execution-plan explain

我有以下查询

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 子句中使用的列对的复合索引键。但是,当使用子查询时,主查询会像以前一样停止使用索引。您能解释一下为什么会发生这种情况吗?

尝试了 Anouar 建议的 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)

从索引和 eq_ref 类型来看,看起来使用 JOIN 比使用子查询更好?您能多解释一下连接解释表达式的结果吗?

最佳答案

您可以看到the answers for this question 你会有很好的想法。

我引用了一些答案

Sometimes MySQL does not use an index, even if one is available. One circumstance under which this occurs is when the optimizer estimates that using the index would require MySQL to access a very large percentage of the rows in the table. (In this case, a table scan is likely to be much faster because it requires fewer seeks.)

简单地说,尝试强制索引:

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;

关于mysql - 将 IN 与子查询一起使用不使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47097508/

相关文章:

java - SQL 语句失败,即使数据库中有实体

SQL查询: Group Time into timeslices then group by loginname then count distinct loginnames per timeslice

mysql - 使用列数据作为列名的查询

sql-server - SQL Server 在什么基数下切换到索引扫描(与查找相比)

php - 在 PHP 中仅获取一次 MySql-Query 列作为标题(即使有多个结果)

mysql - CentOs 6.9 Mysqld 启动失败(InnoDB Error : innodb_system data file './ibdata1' is of a different size)

php - 将 mysql 调用加载到数组中

sql - 关于学习如何在 SQL Server 2005 中解释 SQL 查询的执行计划的任何指南?

sql-server - 为什么 SQL Server 比非聚集 + 键查找更喜欢聚集索引扫描?

mysql - 如何从 WordPress 数据库中检索值?