是否有可能多个字段上的LEFT JOIN
会选择具有不同字段值的行?请考虑以下查询:
SELECT A.a aa, A.b ab, B.a ba, B.b bb
FROM A
LEFT JOIN B
ON A.a = B.a
AND A.b = B.b
WHERE A.a = :filter
在此,我希望aa
和ba
始终等于:filter
(除非涉及空值)。
我目前遇到一个问题,我在结果中得到了我不期望的行,我必须将条件放在WHERE
子句中。我还在研究一个sqlFiddle来重现这个问题。一旦我完成它就会链接到它(但也许我会自己理解这个问题)
将LEFT JOIN
更改为INNER JOIN
也可以解决问题。但我不明白为什么,真的想知道它背后的逻辑。
有趣的是,如果我将B.a
过滤为非空(有效地将左连接转换为内连接),我会得到与内连接相同的预期结果。但是没有这个检查,我在选定的B.a
字段中有数值。为什么它们会被IS NOT NULL
过滤掉,但会显示数值? Oracle查询优化器是否做了一些可疑的事情?
添加/ * + no_query_transformation * /再次给出正确的结果。
不同版本的执行计划是:
原始查询:
SELECT STATEMENT
NESTED LOOPS OUTER
TABLE ACCESS A BY INDEX ROWID
INDEX A_B_FK RANGE SCAN
VIEW PUSHED PREDICATE B
FILTER
TABLE ACCESS B BY INDEX ROWID
INDEX B_C_FK RANGE SCAN
使用+no_query_transformation
查询:
SELECT STATEMENT
VIEW
NESTED LOOPS OUTER
VIEW A
TABLE ACCESS A BY INDEX ROWID
INDEX A_B_FK RANGE SCAN
VIEW
VIEW B
TABLE ACCESS B FULL
答案 0 :(得分:0)
这种行为听起来完全是预期的:它是LEFT JOIN的作用,并通过将其更改为INNER JOIN来修复它,证明它。
基本上,LEFT JOIN表示(在这种情况下)返回与WHERE子句匹配的所有行,并从b中返回所有可以匹配到选定a的行,但是如果没有b匹配任何选定的a然后为b中的值返回一个带有null的值。
将其更改为INNER JOIN将不会返回没有任何b的。通过在where子句中排除null b列可以获得相同的效果,如您所注意的那样。
这就是LEFT加入行为的方式。
答案 1 :(得分:0)
我冒着风险:
这是一个Oracle优化器错误,导致优化器在将查询转换为更有效的方法(合并视图等)的过程中误解了查询的意图。
我们可以说这是一个bug,因为no_query_transformation提示会将结果更改为预期的结果。我不熟悉任何预期会改变除执行计划之外的任何提示 - 当然不是结果。
这些类型的"错误的结果"在具有内联性能视图的复杂查询中最常出现错误。