MySQL查询语句执行顺序疑问:多表关联时会先生成笛卡尔乘积?

背景

之前一直以为MySQL的多表关联查询语句是首先对FROM语句的前两张表执行笛卡尔积,产生一张虚拟表,然后使用ON过滤和OUTER JOIN添加外部行,再使用过滤后的虚拟表跟第三张表进行笛卡尔乘积,重复执行上述步骤。下面是从网上搜到一些比较热门的SQL执行顺序的文章,大家应该很熟悉吧,尤其是下面那张鱼骨图。
image.png
摘自:步步深入:MySQL架构总览->查询执行流程->SQL解析顺序

image.png
摘自:Mysql - JOIN详解

问题描述

最近由于工作需要,对SQL查询性能要求比较高,阅读了《高性能MySQL(第3版)》查询优化的相关章节,在“6.4.3 查询优化处理”章节有这样一句话:

MySQL对任何关联都执行嵌套循环关联操作,即MySQL先在一个表中循环读取单条数据,然后再嵌套循环到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列。MySQL会尝试在最后一个关联表中找到所有匹配的行,如果最后一个关联表无法找到更多的行以后,MySQL返回到上一层次关联表,看是否能够找到更多的匹配记录,依次类推迭代执行。

于是对之前的认识产生怀疑,如果所有关联都是嵌套循环关联查询的话,只有当没有任何过滤条件时两张表才会产生笛卡尔乘积,而且这个笛卡尔乘积是一个结果,并不是关联查询的步骤,而且如果两张几十万、上百万的表进行笛卡尔乘积,这数据量有点巨大了。。。在 MySQL的官方手册 中也印证了嵌套循环操作:image.png

在博客园找到 神奇的 SQL 之 联表细节 → MySQL JOIN 的执行过程(一) 这篇博客对上述笛卡尔乘积也有同样的疑问,而且给出了实际的案例分析,个人比较认同该博主的观点。里面有这样一个案例:
image.png

从这个案例可以看出当查询优化器使用 Index Nested-Loop 即索引嵌套循环,WHERE条件首先通过索引过滤驱动表的数据然后再关联被驱动表,更加印证了WHERE不是在两表生成笛卡尔乘积后才进行过滤的。

如果从“嵌套循环关联”的角度看,之前的关联表先生成笛卡尔乘积再进行过滤的理论是站不住脚的。那么问题就是这种“笛卡尔乘积过滤”的理论有什么历史原因吗?为什么网上的文章大部分都是这种?还是我对“嵌套循环查询”有误解? 我相信有不少小伙伴跟我一样的疑惑吧,还望大佬给指点迷津。

阅读 4.1k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏