查询演员Penelope Guiness(actor_id = 1)参演过的所有电影,查询会是这么写
select * from sakila.film where film_id in(select film_id from sakila.film_actor where actor_id =1);
因为 MySQL 对 In()列表中的选项有专门的优化策略,一般会认为 MySQL 会先执行子查询返回所有包含 actor_id 为 1 的 film_id。一般来说 in ()列表查询速度很快,所以会认为上面的查询会这样执行
select GROUP_CONCAT(film_id)from sakila.film_actor where actor_id = 1;
result : 1,23,25,106,140,166,277等等
select * from sakila.film where film_id IN ( result的结果集 )。
很不幸的是,MySQL 不是这样做的。MySQL 会将相关的外层表压到子查询中,认为这样可以更高效率的查找数据行。即 MySQL 会将查询改写成下面的样子:
这时候子查询就需要根据 film_id来关联外层的 film 表了,里面子查询是 film_actor 表 和 film 表。而关联的条件是 film_id 。 因为需要 film_id 这个字段,所以 MySQL 认为无法先执行这个子查询。通过explain执行计划可以看到这个子查询是一个相关子查询:
根据这个执行计划可以知道,先执行的是 film 表,film表在外层,所以 MySQL 先对外层的film表进行全表扫描ALL,然后根据返回的 film_id 再逐个执行子查询,如果外面这个film表是一个很大的表的话,那么性能是很糟糕的。当然我们可以重写子查询为:
到这里一定认为 子查询 exist / not exist 比连表 join 慢?其实错了,不同的场景,不同的sql下,不一定子查询效率低。有可能子查询的QPS高。
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!