当使用大的IN列表进行SELECT时,首先缩小搜索范围会更快吗?
假设:
当@list_of_id为1K长,10K长或100K长时,以下查询应如何执行?
查询1:
SELECT * FROM people
WHERE id IN (@list_of_id);
查询2a(首先缩小搜索范围):
SELECT * FROM people
WHERE id <= @max_id
AND id >= @min_id
AND id IN (@list_of_id);
查询2b(可能更慢):
SELECT * FROM people
WHERE MONTH(created) = @month
AND id IN (@list_of_id);
答案 0 :(得分:2)
MySQL 5.6专门针对IN()
谓词中的长ID列表引入了一些优化器改进。阅读https://dev.mysql.com/doc/refman/5.6/en/range-optimization.html#equality-range-optimization
如果您依赖于IN()
搜索的索引,则该优化是相关的。如果您在查询2a中首先缩小搜索范围,则索引将用于不等式表达式,但不用于IN()
谓词。
通常,当您有多个搜索词时,只会使用索引优化一个范围谓词。范围谓词不是=
。
所以在你的2a示例中,BETWEEN
谓词将使用索引(我相信你使用的不等式表达式将被优化,好像它是一个BETWEEN
谓词),然后是{ {1}}谓词只会线性搜索第一个索引扫描的结果。
在您的2b示例中,尝试使用IN()
缩小搜索范围时根本无法使用索引。考虑在电话簿中查找人员 - 如果我要求您找到中间首字母为“J”的所有人那么电话簿按姓氏排序的事实对你没有帮助。
您可能希望查看我的演示文稿How to Design Indexes, Really或视频:https://www.youtube.com/watch?v=ELR7-RdU9XU
答案 1 :(得分:0)
使用查询1。
一个简单的IN(long-list)
将越过索引(在你的情况下为PRIMARY KEY
)。这比任何替代方案都更有效。
您可以通过
'证明'这一点FLUSH STATUS;
SELECT ...
SHOW SESSION STATUS LIKE 'Handler%';
当我在IN
中使用146个项目进行尝试时,我得到了
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Handler_commit | 1 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 2 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 0 |
| Handler_read_key | 146 | <-- note
... (other things with "0")
这说明它实际上只探测了146次指数。
我在IN列表中看到过70K项目。查询花了一些时间,但考虑到结果集有70K行,它并没有死,甚至相当快。
(警告:我测试了5.6.22;一些旧版本(5.1?)可能效率较低。)
如果您的ID列表已经在另一个表中,那么JOIN
到该表。这比从服务器中取出ID并将它们发送回服务器要快。