MySQL含UNION ALL的SELECT查询执行顺序解析咨询
MySQL中该SELECT查询的完整执行流程解析
测试用SQL代码
create table student ( ROLL_NO int, NAME varchar(20), ADDRESS varchar(20), PHONE varchar(20), Age int); insert into student values(1,'HARSH','DELHI','9036478414',18),(2,'PRATIK','BIHAR','9945127136',19), (3,'RIYANKA','SILIGURI','8618572816',20),(4,'DEEP','RAMNAGAR','8088648668',18), (5,'SAPTARHI','KOLKATA','8147513582',19),(6,'DHANRAJ','BARABAJAR','8123186181',20), (7,'ROHIT','BALURGHAT','9742111333',18),(8,'NIRAJ','ALIPUR','7851336571',19); create table studentcourse ( COURSE_ID int, ROLL_NO int); insert into studentcourse values(1,1),(2,2),(2,3),(3,4),(1,5),(4,9),(5,10),(4,11); select student.name,studentcourse.COURSE_ID from student right join studentcourse on studentcourse.ROLL_NO=student.ROLL_NO union all select student.NAME,studentcourse.COURSE_ID from student left join studentcourse on studentcourse.ROLL_NO=student.ROLL_NO where student.ROLL_NO is null or studentcourse.COURSE_ID is null;
问题背景
我尝试通过该查询在全连接场景下获取重复NULL值,此前认为WHERE子句在FROM与JOIN子句之后执行,但实际似乎UNION ALL先于WHERE子句执行,恳请解析该SELECT查询在MySQL中的完整执行流程。
执行流程拆解
MySQL没有原生的FULL JOIN,你这里用RIGHT JOIN + LEFT JOIN + UNION ALL模拟全连接效果,但整个查询的执行逻辑是先分别执行两个子SELECT语句,再用UNION ALL合并结果,并非你误以为的UNION ALL先于WHERE执行。具体步骤如下:
1. 执行第一个子SELECT语句
select student.name,studentcourse.COURSE_ID from student right join studentcourse on studentcourse.ROLL_NO=student.ROLL_NO
- 第一步:处理
RIGHT JOIN关联逻辑RIGHT JOIN会保留右表(studentcourse)的所有行,当左表(student)中无匹配ROLL_NO的行时,左表字段填充为NULL。studentcourse共8行:ROLL_NO为1-5的行能匹配student表数据,对应name为HARSH、PRATIK、RIYANKA、DEEP、SAPTARHI,COURSE_ID为对应值;ROLL_NO为9、10、11的行在student表中无匹配,所以name为NULL,COURSE_ID分别为4、5、4。
- 第二步:执行SELECT投影,得到8行结果集。
2. 执行第二个子SELECT语句
select student.NAME,studentcourse.COURSE_ID from student left join studentcourse on studentcourse.ROLL_NO=student.ROLL_NO where student.ROLL_NO is null or studentcourse.COURSE_ID is null;
这个子查询内部严格遵循FROM/JOIN → WHERE → SELECT的执行顺序:
- 第一步:处理
LEFT JOIN关联逻辑LEFT JOIN会保留左表(student)的所有行,当右表(studentcourse)中无匹配ROLL_NO的行时,右表字段填充为NULL。student表共8行:ROLL_NO为1-5的行能匹配studentcourse数据,COURSE_ID有对应值;ROLL_NO为6、7、8的行在studentcourse表中无匹配,所以COURSE_ID为NULL。
- 第二步:执行WHERE筛选
筛选条件student.ROLL_NO is null or studentcourse.COURSE_ID is null中,student.ROLL_NO is null永远不会命中(LEFT JOIN保留了student的所有行,ROLL_NO均有值),最终只筛选出studentcourse.COURSE_ID is null的行——即ROLL_NO为6、7、8的3行,对应name为DHANRAJ、ROHIT、NIRAJ,COURSE_ID为NULL。 - 第三步:执行SELECT投影,得到3行结果集。
3. 执行UNION ALL合并结果
将第一个子查询的8行结果和第二个子查询的3行结果直接合并(UNION ALL不会去重),最终得到11行结果,包含两类NULL场景:
name为NULL的行(来自studentcourse中无匹配的学生);COURSE_ID为NULL的行(来自student中无选课记录的学生)。
内容的提问来源于stack exchange,提问作者Vighnesh Bhat
相关产品推荐
相关产品推荐

