본문 바로가기

ORACLE

ORACLE OUTER JOIN v1.0

ORACLE OUTER JOIN

 

from

 

1.  ORACLE OUTER JOIN

OUTER JOIN JOIN 연결고리 조건을 만족하지 못하는 ROW 에 대해서도 전부 표현하기

위한 JOIN 의 또다른 방법이다.

 

흔히 사용되는 간단한 예를 이야기 하면 EMPLOYEE DEPARTMENTS 가 있을 때

아직 부서에 발령되지 않은 신입이 있다고 가정하자. 하지만 모든 EMPLOYEE

소속부서에 대해 (값의 유무를 떠나) 조회하고 싶을 때 OUTER JOIN 을 사용한다.

 

OUTER JOIN 의 특징으로는 JOIN 의 방향이 고정된다. 조인의 방향이란 DRIVING 되는

테이블을 의미하며 다시말해 조인을 위해 무엇을 먼저 엑세스하느냐란 물음에 항상 고정된 답변을 하게 된다란 이야기이다. 앞서 사용한 EMPLOYEE, DEPARTMENTS 를 예로들면

EMPLOYEE 를 전부 표현하기 위한 OUTER JOIN 을 해야 한다 할 때 반드시

EMPLOYEE 가 먼저 엑세스 된다.

 

OUTER JOIN 의 방법으로는 ANSI 방법이 있고 ORACLE 방법이 있다. 전자는 LEFT|RIGHT|FULL OUTER JOIN 과 같이 구문을 사용하며 후자는 + 기호를 사용한다.

 

select count(*) from(

select e.first_name,d.department_name

from employees e, departments d

where e.department_id(+) = d.department_id

)

/

 

select count(*) from(

select e.first_name,d.department_name

from employees e right outer join departments d

on e.department_id = d.department_id

)

/

 

위 두개의 쿼리는 같은 결과값을 가진다. 단지 그 결과를 얻기위한 표현이 다를 뿐이다.

DRIVING TABLE 이 어떤것이냐란 물음에 대해 상/하 쿼리는 각기 다음과 같이 이야기 할 수 있다.

 

: (+) 기호가 없는 테이블이 DRIVING 되는 테이블

: ( LEFT | RIGHT ) 방향에 있는 테이블이 DRIVING 되는 테이블

 

2.  OUTER JOIN 예제

<OUTER JOIN 표현과 EQUI JOIN 이 섞여있을 때>

 

select e.employee_id,e.first_name,d.department_name

from employees e,departments d

where e.employee_id = d.manager_id

and e.department_id = d.department_id(+)

/

 

outer join 은 무시된다.

 

--------------------------------------------------------------------------------------------

| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT             |             |    11 |   363 |     3   (0)| 00:00:01 |

|   1 |  NESTED LOOPS                |             |    11 |   363 |     3   (0)| 00:00:01 |

|   2 |   TABLE ACCESS FULL          | EMPLOYEES   |   107 |  1498 |     2   (0)| 00:00:01 |

|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |     1 |    19 |     1   (0)| 00:00:01 |

|*  4 |    INDEX UNIQUE SCAN         | DEPT_ID_PK  |     1 |       |     0   (0)| 00:00:01 |

--------------------------------------------------------------------------------------------

 

단순히 equi join 으로 풀린다.

 

<OUTER JOIN JOIN 대상에 대해 일관되게 표현했을 때>

 

select e.employee_id,e.first_name,d.department_name

from employees e,departments d

where e.employee_id = d.manager_id(+)

and e.department_id = d.department_id(+)

/

 

--------------------------------------------------------------------------------------------

| Id  | Operation                    | Name        | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT             |             |   107 |  3531 |     3   (0)| 00:00:01 |

|   1 |  NESTED LOOPS OUTER          |             |   107 |  3531 |     3   (0)| 00:00:01 |

|   2 |   TABLE ACCESS FULL          | EMPLOYEES   |   107 |  1498 |     2   (0)| 00:00:01 |

|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |     1 |    19 |     1   (0)| 00:00:01 |

|*  4 |    INDEX UNIQUE SCAN         | DEPT_ID_PK  |     1 |       |     0   (0)| 00:00:01 |

--------------------------------------------------------------------------------------------

 

OUTER JOIN 으로 풀린다.

 

<OUTER JOIN JOIN 대상에 대해 비일관하게 표현했을 때>

 

where e.employee_id = d.manager_id(+)

and e.department_id(+) = d.department_id

 

앞서 본 쿼리의 조인조건을 위와 같이 바꾸면 아래와 같은 에러를 볼 수 있다. 조인은 두 대상에

이루어지는 것이고 DRIVING 대상은 택일이다. 때문에 에러가 난다.

 

ERROR at line 3:

ORA-01416: two tables cannot be outer-joined to each other

 

<OUTER JOIN 의 변형>

OUTER JOIN SCALAR SUB QUERY 로 변형할 수 있다.

하지만 제약사항이 있다. (SCALAR SUB QUERY 문서 참고)

 

 그 제약에 대해 무엇이 드라이빙 되는가에 따라 살펴보겠다.

 

<CASE1>

select e.first_name,d.department_name

from employees e, departments d

where e.department_id = d.department_id (+)

 

 

CASE1 DRIVING TABLE EMPLOYEES 이다. 결과적으로 의미를 보면 소속불명의 EMPLOYEE 까지

전부 표현하게 되고 이는 EMPLOYEES 수와 동일하다. 이 경우 SCALAR SUB QUERY 로 변경가능하다.

SCALAR SUB QUERY FUNCTION 과도 같아서 입력에 대해 하나의 결과를 얻어야 한다는 제약이 있다.

 

select e.first_name,

(select department_name

from departments

where department_id = e.department_id)

department_name

from employees e

/

 

소속불명 ‘Kimberely’ department_name null 로 나온다.

 

 위와 같이 변경이 가능한 것은 outer join 을 하든 scalar sub query 로 풀든 각employees.department_id 에 대해 그 결과 건수 1:1 로 변하지 않기 때문이다.

 

<CASE2>

select e.first_name,d.department_name

from employees e, departments d

where e.department_id(+) = d.department_id

 

위 쿼리의 결과건수는 122건으로 employees, departments 어느 테이블의 결과건과도 일치하지

않는다. Department_id 에 대해 JOIN 에 성공한 모든 테이블 (106) + employee 가 없는 department (16) => 122 건이다.

 

 이 쿼리를 스칼라서브쿼리로 변경한다고 할 때 문제가 발생한다. CASE1 같이 스칼라서브쿼리의 메인쿼리에 DRIVING TABLE(여기서는 DEPARTMENT_ID) 를 넣으면 결과건수는 DEPARTMENTS ROWS 만큼이다.

결과건수의 불일치를 볼 수 있으며 이는 DRIVING EMPLOYEES 를 가정해도 마찬가지이다.

추가적인 DEPARTMENTS DRIVING 이면 생길 수 있는 문제는 하나의 DEPARTMENT_ID 에 대해 N

만큼의 EMPLOYEE 가 있을 수 있다는 것이다. 이는 스칼라서브쿼리의 하나의 입력에 하나의 출력이란

법칙에 어긋난다.

 

 

<CASE1>,<CASE2> 를 보았을 때 스칼라 서브쿼리로 변경가능한 것은 1:N 관계에서 N 쪽임을 알 수

있다.

 

*정말 오랜만에 쓰는 기술문서? 이다. 정신오염도(수면욕)가 심한 상태라 글에 헛소리가 있을까 걱정된다;*