使用LEFT JOIN结合模拟FULL OUTER JOIN时查询结果异常求助
问题排查与优化方案
Hey,我来帮你梳理下这段代码里的问题,让它能正确返回你想要的结果~
1. 先简化冗余的内部子查询
你原本想用UNION ALL + DISTINCT来模拟FULL OUTER JOIN,获取B和C表的所有Role并去重,但这段子查询写得有点绕,还带了没必要的LEFT JOIN:
SELECT B.Role From B LEFT JOIN C ON(B.Role=C.Role)其实就是拿B表的所有Role,LEFT JOIN在这里完全不影响结果,因为你只取B的Role;- 同理
SELECT C.Role From C LEFT JOIN B ON(C.Role=B.Role)就是拿C表的所有Role; - 用
UNION ALL再套DISTINCT,不如直接用UNION——因为UNION本身就会自动去重,比先UNION ALL再去重高效多了。
所以内部子查询可以简化成这样:
SELECT Role FROM B UNION SELECT Role FROM C
2. 修正外部查询的可读性(避免别名冲突)
你的外部查询逻辑是对的:用A左连接B/C的合并Role集,筛选出B/C里没有的A.Role。但你把子查询的别名设成了B,和原表B重名了,虽然语法上没问题,但很容易搞混,建议改成更清晰的名字,比如BC_Roles。
修正后的完整SQL:
SELECT A.Role FROM A LEFT JOIN ( SELECT Role FROM B UNION SELECT Role FROM C ) AS BC_Roles ON A.Role = BC_Roles.Role WHERE BC_Roles.Role IS NULL
3. 去掉VBA里多余的循环
你的VBA代码里写了While Not rsD.EOF循环,但CopyFromRecordset会一次性把整个记录集的内容复制到指定单元格开始的区域,根本不需要逐行循环,去掉循环后代码更简洁,还能避免不必要的性能消耗:
Dim rsD As ADODB.Recordset Dim sqlD As String Set rsD = New ADODB.Recordset sqlD = "SELECT A.Role FROM A LEFT JOIN (SELECT Role FROM B UNION SELECT Role FROM C) AS BC_Roles ON A.Role = BC_Roles.Role WHERE BC_Roles.Role IS NULL" rsD.Open sqlD, conn, adOpenStatic wb.Worksheets(3).Range("A2").CopyFromRecordset rsD rsD.Close Set rsD = Nothing ' 这里注意:你原代码里写的Set wb = Nothing,要确保wb是已经正确打开的工作簿对象,释放前最好先关闭它,比如wb.Close SaveChanges:=True
原查询可能出错的原因
原内部子查询里的冗余LEFT JOIN,虽然语法上没问题,但可能让数据库的执行计划变得复杂,甚至如果Role字段存在NULL值,可能会导致去重逻辑出现意外结果。而简化后的UNION语句逻辑更清晰,执行效率也更高,能确保拿到B和C所有不重复的Role值。
内容的提问来源于stack exchange,提问作者urvashi
相关产品推荐
相关产品推荐

