多个字段的左连接返回不匹配的行

时间:2014-09-11 13:21:29

标签: sql oracle join

是否有可能多个字段上的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

在此,我希望aaba始终等于: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
    

2 个答案:

答案 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提示会将结果更改为预期的结果。我不熟悉任何预期会改变除执行计划之外的任何提示 - 当然不是结果。

这些类型的"错误的结果"在具有内联性能视图的复杂查询中最常出现错误。