Oracle中全外连接的奇怪行为 - 如何解释?

时间:2012-03-15 10:16:13

标签: sql oracle oracle11g

我注意到Oracle 11中出现了一个奇怪的FULL OUTER JOIN行为。我正在从HR模式加入表,特别是EMPLOYEES和DEPARTMENTS。

例如,以下查询返回123行:

    SELECT * FROM employees e
    FULL JOIN departments d ON e.department_id = d.department_id

但是,要理解的是什么 - 当我在select子句中放入一组特定的列时,查询将返回122行(缺少的行是针对没有分配部门的员工 - 另外返回的行)左连接与内连接相比):

    SELECT first_name, last_name, department_name FROM employees e
    FULL JOIN departments d on e.department_id = d.department_id

即使我计算行数,也会返回122(COUNT(*))!到底是怎么回事? SELECT *SELECT COUNT(*)之间有什么区别?

SELECT * ...的解释计划:

SELECT STATEMENT                                      122
  VIEW                 VW_FOJ_0                       122
    HASH JOIN                          FULL OUTER     122
      Access Predicates
        E.DEPARTMENT_ID = D.DEPARTMENT_ID
      TABLE ACCESS     DEPARTMENTS     FULL            27
      TABLE ACCESS     EMPLOYEES       FULL           107

SELECT COUNT(*) ...

SELECT STATEMENT                                             1
  SORT                                     AGGREGATE         1
    VIEW               VW_FOJ_0                            122
      HASH JOIN                            FULL OUTER      122
        Access Predicates
          E.DEPARTMENT_ID = D.DEPARTMENT_ID
        INDEX          DEPT_ID_PK          FAST FULL SCAN   27
        INDEX          EMP_DEPARTMENT_IX   FAST FULL SCAN  107

2 个答案:

答案 0 :(得分:6)

优化器不应该选择在第二个查询中使用EMP.DEPT_ID上的索引,因为它可以具有NULL值。这就是导致它从结果中排除一行的原因。

我现在能想到的唯一非bug解释是你在DISABLE RELY模式下以某种方式创建了约束,以便优化器认为该字段不能包含NULL。在这种情况下,在约束中给出不正确信息的情况下使用索引是正确的。但是,似乎RELY选项不适用于NOT NULL约束,所以我不知道这可能是什么问题。尽管如此,请仔细查看表格上的所有约束。

除此之外,甲骨文网站上存在大量错误,这些错误来自全外连接的错误结果。你可能会遇到其中一个。在其中的一些情况下,解决方法是禁用“本机”全外连接,您可以使用此语句为当前会话执行此操作:

alter session set "_optimizer_native_full_outer_join"=off; 

答案 1 :(得分:1)

(不能在评论中写这个)

结果符合执行计划。

count(*)执行计划使用索引EMP_DEPARTMENT_IX,其中包含来自employeesess表的所有dept_ids。但索引不包含空值。因此,此执行计划将使用null department_id“丢失”emps。

但是,应该解释为什么Oracle在

的情况下选择此执行计划
select first_name, last_name, department_name

select count(*)

反对

select *