EXISTS?不存在的
众所周知,范围查询 IN 是先执行子查询,然后再做主查询的条件,EXISTS 是先执行主查询,然后逐条判断子查询的返回。很多人都是从这里学到了小表驱动大表,进而总结出 IN 小表 EXISTS 大表的至理名言。但是实际情况呢?
现在有一张表 A ,大约 600 条数据,另一张表 B,大约 355w 条数据,它们的定义如下
CREATE TABLE `A` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `a` int(11) NOT NULL DEFAULT '0' COMMENT '关联列', PRIMARY KEY (`id`), KEY `id` (`id`) USING BTREE )
CREATE TABLE `B` ( `id` int(16) NOT NULL AUTO_INCREMENT, `b` int(11) NOT NULL COMMENT '关联列', `name` varchar(255) DEFAULT '' COMMENT '姓名', PRIMARY KEY (`id`) )
如果使用的是 IN 查询,这么写
SELECT * FROM A WHERE a IN (SELECT b FROM B)
很显然,子句的执行效率和 B 的数据量大小以及是否有索引有关,完事再拿来 A 中的数据,最后进行筛选,好在这个过程是在内存中完成的
如果使用的是 EXISTS ,这么写
SELECT * FROM A s WHERE EXISTS(SELECT 1 FROM B WHERE b = s.a)
很显然,先执行的 A 表查询,完事遍历其中的每一条,再去数据库中搜索,关联的上就返回真并保留数据,否则返回假丢弃数据(EXISTS 并不关心select 哪些字段,即使select null 也无所谓,这和 in 是不同的)
用 n 来表示数量,那么使用 IN 查询的时间复杂度就是 na* nb,使用 EXISTS 的时间复杂度就是 na,加之 EXISTS 每次都需要走库,所以当 nb 较小时,使用 IN 的查询效率应该更高,随着 b 表数据量的增加,EXISTS 的优势才能慢慢展现出来
下面进行实际检验,使用 IN 大概需要 7 秒钟,即无索引全表扫描 355w 以上的数据需要大概 7s(1秒钟扫描50w以上) ,完事在内存中执行 600*355w 的循环遍历,这还是很快的。使用 EXISTS 的话,相当于查了 na 次,每次都要查库,而且每次对 B 都是全表扫描,这简直就是灾难。预估需要的时间是600 * 7 s ≈ 70 min,实际执行了 5119 s 约等于 85 min
于是乎,给 B 的 b 列增加了一个索引,这样,EXISTS 子句可以执行的更快,当然,IN 子句同样也是。二者都是在 0.005 秒内查询完毕,几乎没差
总结,EXISTS 的主查询必是全表扫描,子句可以索引优化。 IN 的主句和子句都可以索引优化。EXISTS 每次查询都是查库,而 IN 查出子句以后就在内存中筛选,效率明显不同。总之,索引会抹平差异!
浙公网安备 33010602011771号