2012-03-15 11 views
7

He notado un comportamiento extraño de FULL OUTER JOIN en Oracle 11. Estaba uniendo tablas del esquema de recursos humanos, particularmente EMPLEADOS y DEPARTAMENTOS.Comportamiento extraño de la combinación externa completa en Oracle: ¿cómo podría explicarse?

Por ejemplo, la siguiente consulta devuelve 123 filas:

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

Sin embargo, lo que es difícil de entender - cuando pongo un conjunto de columnas particulares en la cláusula select, la consulta devolverá 122 filas (una fila que falta es para un empleado que no tiene asignado departamento - la que, además, se devuelve con izquierda se unen en comparación con combinación interna):

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

Incluso cuando cuente las filas que devuelve 122 (COUNT(*)) !!! ¿QUE ESTA PASANDO? ¿Cuál es la diferencia entre SELECT * y SELECT COUNT(*)?

El plan para explicar 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 

y para 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

¿Qué ocurre si se utiliza el 'union' para aquellas columnas ? ¿obtienes algo diferente cuando usas 'union all'?¿Qué obtendrás si cuentas con 'group by first_name, last_name, department_name'? –

+0

'SELECCIONAR * FROM empleados e departamentos FULL JOIN d en e.department_id = d.department_id' devuelve 123 filas y' SELECT count (*) FROM employees e FULL JOIN departamentos d en e.department_id = d.department_id' devuelve 122 filas? –

+0

Sí, exactamente, es por eso que he publicado esta pregunta. –

Respuesta

5

El optimizador no debe ser la elección de use el índice en EMP.DEPT_ID en la segunda consulta, ya que puede tener valores NULL. Esto es lo que causa que excluya una fila de los resultados.

La única explicación que no es de error que se me ocurre en este momento es que de alguna manera has creado restricciones en el modo DISABLE RELY para que el optimizador piense que el campo no puede contener NULL. En este caso, sería correcto usar el índice dada la información incorrecta en las restricciones. Sin embargo, parece que la opción RELE no está disponible para las restricciones NOT NULL, por lo que no veo cómo este podría ser el problema. No obstante, eche un vistazo cuidadoso a todas las restricciones en las tablas.

Dejando eso de lado, hay una cantidad sorprendente de errores en el sitio de Oracle con respecto a los resultados incorrectos de las uniones externas completas. Usted podría estar golpeando a uno de ellos. En un buen número de estos casos, la solución es desactivar "nativo" externa completa se une, lo que puede hacer para su sesión actual con esta declaración:

alter session set "_optimizer_native_full_outer_join"=off; 
+0

+1: parece la explicación más plausible. Sería interesante conocer la versión de DB. –

1

(No se puede escribir esto en un comentario)

Los resultados se ajustaban a los planes de ejecución.

El plan de ejecución count (*) usa el índice EMP_DEPARTMENT_IX que contiene todos los dept_id de la tabla de empleados. Pero los índices no contienen nulos. Por lo tanto, este plan de ejecución "perderá" los emps con nulo department_id.

Sin embargo, se debe explicar por qué Oracle elegir este plan de ejecución en caso de

select first_name, last_name, department_name 

y

select count(*) 

en oposición a

select * 
+1

Sí, estoy de acuerdo. Sin embargo, este es un tipo de inconsistencia en mi opinión. La combinación externa completa no funciona como debería en todos los casos, excepto para seleccionar *. –

+0

@luckyjaca ¿Puedes hacer más pruebas con 'select emp_id',' select first_name', 'select e.department_id',' select d.department_id', 'select d.department_name', etc.? –

+0

heh ... He hecho las pruebas como sugirió. Sin embargo, todos ellos devolvieron 122 filas; la única forma de obtener 123 filas es seleccionando todas las columnas con un asterisco. Sin embargo, finalmente he logrado devolver 123 filas usando la sugerencia: '/ * + no_index (e) * /'. Es suficiente para que todas las consultas funcionen, ya sea 'COUNT (*)' o 'first_name, last_name, department_name'. –