CREATE TABLE IF NOT EXISTS `a` (
`id` int(1) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL,
`aNum` char(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
1)myisam表:mysql>
explain select * from a where id=1 or uid
=2;
+----+-------------+-------+-------------+---------------+-------------+---------+------+------+---------------------------------------+
|
id | select_type | table | type |
possible_keys | key | key_len |
ref | rows |
Extra
|
+----+-------------+-------+-------------+---------------+-------------+---------+------+------+---------------------------------------+
|
1 | SIMPLE | a |
index_merge | PRIMARY,uid | PRIMARY,uid |
4,4 | NULL | 2 |
Using
union(PRIMARY,uid); Using where
|
+----+-------------+-------+-------------+---------------+-------------+---------+------+------+---------------------------------------+
1
row in set (0.00 sec)
在mysql5.0和更新的版本中,会同时使用这两个单列索引进行扫描,然后合并结果。索引合并需要耗费大量cpu和内存资源在算法的缓存,和并和排序上。
2)innodb表:(alter table a engine=innodb)
mysql> explain
select * from a where id=1 or uid
=2;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
id | select_type | table | type | possible_keys | key | key_len |
ref | rows | Extra
|
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
1 | SIMPLE | a | ALL
| PRIMARY,uid | NULL | NULL | NULL
| 5 | Using where
|
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1
row in set (0.00 sec)
2
.必须所有的or条件都必须是独立索引:
+-------+----------------------------------------------------------------------------------------------------------------------
|
Table | Create
Table
+-------+----------------------------------------------------------------------------------------------------------------------
|
a | CREATE TABLE `a` (
`id` int(1) NOT NULL
AUTO_INCREMENT,
`uid` int(11) NOT NULL,
`aNum` char(20)
DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6
DEFAULT CHARSET=latin1
|
+-------+----------------------------------------------------------------------------------------------------------------------
1
row in set (0.00 sec)
explain查看:
mysql> explain select *
from a where id=1 or uid
=2;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
id | select_type | table | type | possible_keys | key | key_len |
ref | rows | Extra
|
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|
1 | SIMPLE | a | ALL
| PRIMARY | NULL | NULL |
NULL | 5 | Using where
|
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
全表扫描了。
3. 用UNION替换OR
(适用于索引列)
通常情况下,
用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描.
注意, 以上规则只针对多个索引列有效. 如果有column没有被索引,
查询效率可能会因为你没有选择OR而降低.
在下面的例子中, LOC_ID 和REGION上都建有索引.
高效:
select loc_id , loc_desc , region from location where loc_id = 10
union
select loc_id , loc_desc , region from location where region = "melbourne" and loc_id != 10
低效:
select loc_id , loc desc , region from location where loc_id = 10 or region = "melbourne"
如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面.
通过查询表中值得分布情况(看看where条件的各个分支对应的数据基数有多大),确定那个值放在前边。
select sum(loc_id = 10),sum(region="melbourne") from location\G
************************1.row***********************
SUM(loc_id = 10): 7992
SUM(region="melbourne"): 30
通过检测发现应该把region="melbourne"放在前边。
4. 用in来替换or
这是一条简单易记的规则,但是实际的执行效果还须检验,在oracle8i下,两者的执行路径似乎是相同的.
低效:
select….
from location where loc_id = 10 or loc_id = 20 or loc_id =
30
高效
select… from location where loc_in in
(10,20,30);