더북(TheBook)

안티 조인

안티란 말에서 유추할 수 있듯이 안티 조인(ANTI-JOIN)은 서브 쿼리의 B 테이블에는 없는 메인 쿼리의 A 테이블의 데이터만 추출하는 조인 방법이다. 한쪽 테이블에만 있는 데이터를 추출하는 것이므로 조회 조건에서 NOT IN이나 NOT EXISTS 연산자를 사용한다. 즉 세미 조인과 반대 개념이다.

입력

    SELECT a.employee_id, a.emp_name, a.department_id, b.department_name
      FROM employees a,
           departments b
     WHERE a.department_id = b.department_id
       AND a.department_id NOT IN ( SELECT department_id
                                      FROM departments
                                     WHERE manager_id IS NULL) ;

결과

    EMPLOYEE_ID EMP_NAME          DEPARTMENT_ID DEPARTMENT_NAME
    ----------- ----------------- ------------- --------------------------------
    198         Donald OConnell   50            배송부
    199         Douglas Grant     50            배송부
    200         Jennifer Whalen   10            총무기획부
    201         Michael Hartstein 20            마케팅
    ...
    106개의 행이 선택됨.

앞의 쿼리는 manager_id 값이 있는 부서에 속한 사원 테이블을 조회한 것이다. 여기서 주의할 점은 “departments b”를 조인한 부분은 해당 사원에 대한 부서명을 가져오기 위한 조인이고, 안티 조인은 NOT IN 절 이하의 서브 쿼리에 명시했다.

위 쿼리를 다음과 같이 NOT EXISTS로도 변환할 수 있다.

입력

    SELECT count(*)
      FROM employees a
     WHERE NOT EXISTS ( SELECT 1
                          FROM departments c
                         WHERE a.department_id = c.department_id
                           AND manager_id IS NULL) ;

결과

    COUNT(*)
    ----------
    107

그런데 좀 이상하다. 왜 106건이 아닌 107건일까? 이유는 사원 테이블에서 부서번호가 NULL인 사원이 1명 존재(178번 사원)하기 때문이다. 사실 이 한 건을 제외하고 사원 테이블에는 부서 테이블에서 manager_id 값이 있는 부서만 들어가 있다. 따라서 manager_id 값이 NULL인 부서에 속하는 사원은 단 한 명도 존재하지 않으며 IN이나 EXISTS를 사용했다면 위의 두 쿼리 결과 건수는 모두 0건이다. 이 논리대로 한다면 IN 대신 NOT IN을 사용한다면 모든 사원이 조회되어야 한다. 하지만 조인 조건에 참여하는 사원 테이블의 department_id 값이 NULL이면 NOT IN과의 연산 결과가 FALSE가 되어 106건이 된 것이다. 반면 NOT EXISTS는 조인 조건이 서브 쿼리 내에 존재하고 178 번 사원도 조인 결과가 FALSE가 되며 NOT으로 인해 쿼리 전체는 TRUE가 되므로 107건이 조회된 것이다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.