两种LEFT JOIN写法的SELECT查询语句哪个执行速度更快?
嘿,这个问题其实戳中了很多人对SQL执行逻辑的常见误解——先别急着认定第二个语句更快,咱们得从数据库查询优化器的核心逻辑说起。
首先得明确一个关键:SQL是声明式语言。你写的语句是在告诉数据库「我要什么结果」,而不是「你要按什么步骤去做」。数据库的查询优化器会基于表的统计信息、索引情况、数据分布等,自动生成最优的执行计划,完全不会严格按照你写的语句字面顺序来执行。
咱们来看这两个待对比的语句:
第一个语句:
SELECT * FROM class t1 LEFT JOIN class_students t2 ON t1.id = t2.class_id LEFT JOIN student t3 ON t2.student_id = t3.id WHERE t1.id = 1;
第二个语句:
SELECT * FROM (SELECT * FROM class WHERE id = 1) t1 LEFT JOIN class_students t2 ON t1.id = t2.class_id LEFT JOIN student t3 ON t2.student_id = t3.id;
网上流传的「第一个语句先完成所有LEFT JOIN再执行WHERE过滤」的说法,其实是错的。主流数据库(比如MySQL、PostgreSQL、SQL Server)的优化器都会做谓词下推(Predicate Pushdown)优化——也就是把WHERE t1.id = 1这个条件,提前到对class表的扫描阶段,先过滤出class中id=1的行,再去执行后续的LEFT JOIN操作。这和第二个语句的逻辑本质上是等价的,优化器最终生成的执行计划几乎完全一致。
至于你问的有没有权威资料说明这一点?其实SQL标准并没有强制规定执行顺序,但所有主流数据库的官方文档都明确提到了优化器会进行这类等价查询变换。你也可以自己验证:用EXPLAIN(MySQL)或EXPLAIN ANALYZE(PostgreSQL)查看两个语句的执行计划,会发现它们的执行步骤完全相同——都是先快速定位class表中id=1的记录,再关联后续的表。
当然,这里有个前提:你的WHERE条件是针对左表class的过滤。如果是针对LEFT JOIN右侧表的非NULL约束(比如WHERE t3.age > 10),那情况会不一样,但这次的场景里不存在这种情况。
总结一下:这两个语句在绝大多数情况下执行效率没有区别,那个关于第一个语句先全连接再过滤的观点并不准确,优化器会自动帮你把过滤条件提前,减少连接操作量。
内容的提问来源于stack exchange,提问作者Joseph Huang

