当使用索引找不到结果时,有什么办法可以禁止MySQL执行全表扫描?
例如此查询:
SELECT *
FROM a
WHERE (X BETWEEN a.B AND a.C)
ORDER BY a.B DESC
LIMIT 1;
仅在X满足条件且返回至少1行时才有效,但如果表中的任何数据无法满足条件,则将执行完全扫描,这可能非常昂贵。
我不想优化这个特定的查询,这只是一个例子。
使用范围内或范围外的X来解析此查询:
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE a range long_ip long_ip 8 \N 116183 100.00 Using where
STATUS VARIABLE显示更好的信息。对于超出范围的X:
Handler_read_prev 84181
Key_read_requests 11047
在范围内:
Handler_read_key 1
Key_read_requests 12
如果只有一种方法可以阻止Handler_read_prev从1增长到过去。
更新。我无法接受我自己的答案,因为它并没有真正回答这个问题(HANDLER是一个很棒的功能)。在我看来,没有通用的方法来阻止MySQL进行全面扫描。虽然像key ='X'这样的简单条件会被认为是“不可能的”,但BETWEEN之类的更复杂的东西却不会。
答案 0 :(得分:1)
您可以编写一个“完全覆盖”的子查询,该子查询仅使用索引中可用的数据。根据返回的主键,您可以在主表中查找行。
以下查询完全由(id),(B,id)和(C,id)上的索引覆盖:
select *
from a
where id in (
select id
from a
where x <= C
and id in (
select id
from a
where B <= X
)
)
limit 1
每个SELECT使用一个索引:最内层的索引(B,id);中间的SELECT使用索引(C,id),外部SELECT使用主键。
答案 1 :(得分:0)
以下是我最终提出的建议:
HANDLER a OPEN;
HANDLER a READ BC <= (X);
HANDLER a CLOSE;
BC是密钥(B,C)的名称。如果我们按B DESC对表进行排序,则保证结果等于
SELECT *
FROM a
WHERE (X BETWEEN a.B AND a.C)
ORDER BY a.B DESC
LIMIT 1;
现在如果X不在表a的范围内,我们只需要检查aC是否大于X,如果不是,那么X绝对超出范围,我们不需要再看了
虽然这不是很优雅,但您必须在每次插入或更新时使用该表。