禁止MySQL对查询使用全表扫描

时间:2009-10-24 07:51:15

标签: sql mysql query-optimization

当使用索引找不到结果时,有什么办法可以禁止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之类的更复杂的东西却不会。

2 个答案:

答案 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绝对超出范围,我们不需要再看了

虽然这不是很优雅,但您必须在每次插入或更新时使用该表。