一段时间内我一直认为使用or就会导致全表扫描,原因是因为大概2010年的时候写过一段数据处理的存储过程,因为在进行几张大表关联的时候or的使用不当,导致数据的查询非常缓慢。那次经历之后,一直对or的使用心存敬畏,每次在使用or的时候都觉得会导致全表扫描。当时为了解决or的性能问题,采用的方案是union all的方式来替换or。但是这同样带来一个问题——sql的简洁程度降低了。
时隔多年,近期因为一直听到DBA在公司里说很多项目因为or的使用导致查询性能下降,于是又开始问自己,or真的用了就会导致全表扫描吗?那么or这个语法存在价值又是如何呢?
带着这些疑问,觉得多年的一个疑问还是有必要自己试验一下(自己还是缺乏对技术细节深究的精神,这么久才决定自己测试,很是惭愧。)
测试数据库:mysql5.7.10
测试过程:
新建表
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `t_in_or_union_test`
-- ----------------------------
DROP TABLE IF EXISTS `t_in_or_union_test`;
CREATE TABLE `t_in_or_union_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`text_1` varchar(10) DEFAULT NULL,
`text_2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_1` (`text_1`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
-- 初始化100W条数据
begin
declare i int;
set i=0;
while i<1000000 do
insert into t_in_or_union_test(text_1,text_2) values(i+1,i+2);
set i=i+1;
end while;
end
explain
select * from t_in_or_union_test
where text_1 ='100'
or text_1 = '101'
or text_1 = '102'
or text_1 = '103'
or text_1 = '104'
or text_1 = '105'
or text_1 = '106'
or text_1 = '107'
or text_1 = '108'
or text_1 = '109'
or text_1 = '110'
此处虽然使用了or,但是并没有导致预想中全表扫描。
再创建一张类似的表进行测试:
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `t_in_or_union_test_2`
-- ----------------------------
DROP TABLE IF EXISTS `t_in_or_union_test_2`;
CREATE TABLE `t_in_or_union_test_2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`text_1` varchar(10) DEFAULT NULL,
`text_2` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_1` (`text_1`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
begin
declare i int;
set i=0;
while i<100000 do
insert into t_in_or_union_test_2(text_1,text_2) values(i+1,i+2);
set i=i+1;
end while;
end
然后分别执行以下两段sql:
EXPLAIN
select * from t_in_or_union_test_2 A
left join t_in_or_union_test B on A.text_1 = B.text_1
and B.text_1 = '100'
or B.text_1 = '101'
select * from t_in_or_union_test_2 A
left join t_in_or_union_test B on A.text_1 = B.text_1
and B.text_1 = '100'
or B.text_2 = '101'
从上面执行结果来看,两个不同的字段使用or时似乎导致了全表扫描,尽管这两个字段上都建立索引。
再写三段直接通过主键查询的sql
explain
select * from t_in_or_union_test
where 1=1 or id = 100056 or id = 10005
explain
select * from t_in_or_union_test
where 1=1 and (id = 100056 or id = 10005)
explain
select * from t_in_or_union_test
where id = 100056 or id = 10005
分别执行,发现第一段sql不走主键索引,第二、三段会走主键索引。
通过上面的几个小试验,基本可以验证在两个相同字段之间使用or不会导致全表扫描,只有出现不通字段自建使用or时会导致全表扫描。
个人认为是一个索引要定位到第1条数据,一个索引要定位到第100条数据,查询优化器无法同时满足这个需求,只能使用全表扫描来查询数据。
简单的做了一些验证,认识了一些以前不确认的点,关于or的使用,大家如果有不同的认识,多多交流。