对于单元素索引,可以用WHERE子句中的相应条件很方便地表示索引值区间,因此我们称为范围条件而不是“区间”。
单元素索引范围条件的定义如下:
· 对于BTREE和HASH索引,当使用=、<=>、IN、IS NULL或者IS NOT NULL操作符时,关键元素与常量值的比较关系对应一个范围条件。
· 对于BTREE索引,当使用>、<、>=、<=、BETWEEN、!=或者<>,或者LIKE 'pattern'(其中 'pattern'不以通配符开始)操作符时,关键元素与常量值的比较关系对应一个范围条件。
· 对于所有类型的索引,多个范围条件结合OR或AND则产生一个范围条件。
前面描述的“常量值”系指:
· 查询字符串中的常量
· 同一联接中的const或system表中的列
· 无关联子查询的结果
· 完全从前面类型的子表达式组成的表达式
下面是一些WHERE子句中有范围条件的查询的例子:
SELECT * FROM t1 WHERE key_col > 1 AND key_col < 10; SELECT * FROM t1 WHERE key_col = 1 OR key_col IN (15,18,20); SELECT * FROM t1 WHERE key_col LIKE 'ab%' OR key_col BETWEEN 'bar' AND 'foo';
请注意在常量传播阶段部分非常量值可以转换为常数。
MySQL尝试为每个可能的索引从WHERE子句提取范围条件。在提取过程中,不能用于构成范围条件的条件被放弃,产生重叠范围的条件组合到一起,并且产生空范围的条件被删除。
例如,考虑下面的语句,其中key1是有索引的列,nonkey没有索引:
SELECT * FROM t1 WHERE (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR (key1 < 'bar' AND nonkey = 4) OR (key1 < 'uux' AND key1 > 'z');
key1的提取过程如下:
1. 用原始WHERE子句开始:
2. (key1 < 'abc' AND (key1 LIKE 'abcde%' OR key1 LIKE '%b')) OR
3. (key1 < 'bar' AND nonkey = 4) OR
4. (key1 < 'uux' AND key1 > 'z')
5. 删除nonkey = 4和key1 LIKE '%b',因为它们不能用于范围扫描。删除它们的正确途径是用TRUE替换它们,以便进行范围扫描时不会丢失匹配的记录。用TRUE替换它们后,可以得到:
6. (key1 < 'abc' AND (key1 LIKE 'abcde%' OR TRUE)) OR 7. (key1 < 'bar' AND TRUE) OR 8. (key1 < 'uux' AND key1 > 'z')
9. 取消总是为true或false的条件:
· (key1 LIKE 'abcde%' OR TRUE)总是true
· (key1 < 'uux' AND key1 > 'z')总是false
用常量替换这些条件,我们得到:
(key1 < 'abc' AND TRUE) OR (key1 < 'bar' AND TRUE) OR (FALSE)
删除不必要的TRUE和FALSE常量,我们得到
(key1 < 'abc') OR (key1 < 'bar')
10.将重叠区间组合成一个产生用于范围扫描的最终条件:
11. (key1 < 'bar')
总的来说(如前面的例子所述),用于范围扫描的条件比WHERE子句限制少。MySQL再执行检查以过滤掉满足范围条件但不完全满足WHERE子句的行。
范围条件提取算法可以处理嵌套的任意深度的AND/OR结构,并且其输出不依赖条件在WHERE子句中出现的顺序。