EXISTS와 NOT EXISTS
서브쿼리 결과의 존재 여부만 확인하는 EXISTS와 NOT EXISTS의 사용법과, NOT IN을 NOT EXISTS로 바꾸는 예, 업무형 실습 문제
#핵심 요약
WHERE EXISTS (SELECT 1 FROM emp e WHERE e.deptno = d.deptno)는 사원 테이블에 그 부서번호가 하나라도 있는 부서만 고름
메인쿼리의 값(d.deptno)을 서브쿼리에 넘겨 존재하는지 찾고, 찾으면 거기서 멈춤
NOT EXISTS는 존재하지 않는 행을 고름
실습에서는 NOT IN으로 쓴 "직속 부하가 없는 사원"을 NOT EXISTS로 바꾸게 했음
#개념과 동작 원리
서브쿼리가 한 단계 이상 위 문장의 테이블 컬럼을 참조하면 상관 서브쿼리(correlated subquery)임
상관 서브쿼리는 개념상 부모 문장이 처리하는 행마다 한 번씩 평가됨EXISTS 예제의 e.deptno = d.deptno가 바깥 dept의 컬럼을 참조하는 상관 조건임
옵티마이저는 같은 의미의 조인 등으로 상관 서브쿼리를 변환할 수 있음
#주요 용어와 구성 요소
| 구문 | 의미 |
|---|---|
| EXISTS | 서브쿼리가 하나 이상의 행을 반환하면 참임 |
| NOT EXISTS | 서브쿼리가 행을 반환하지 않으면 참임 |
| 상관 조건 | 안쪽 행을 바깥 행과 연결하며 빠뜨리면 전체 집합의 존재만 검사하게 됨 |
#필수 명령어 또는 SQL
| 항목 | 실행 조건 |
|---|---|
| 작업 대상 | dept와 emp의 부서 대응, emp의 직속 부하 존재 여부 |
| 필요 권한 | 참조하는 모든 테이블에 대한 조회 권한이 있는 계정 |
| 바꿀 입력값 | 바깥, 안쪽 별칭, 연결 키와 업무 필터 |
| 결과 해석 | 업무형 연습 문제 답과 실제 행 목록은 확인 불가 |
SELECT deptno
FROM dept d
WHERE EXISTS (SELECT 1
FROM emp e
WHERE e.deptno = d.deptno);결과 해석:
실습 테이블: 통신사 요금표 telecom_table(통신사, 요금, 건수)에 KT, LG, SKT와 알뜰 요금제 세 개를 넣었음
문제는 "emp19에 존재하는(존재하지 않는) 통신사만 출력"임(답 확인 불가)emp19의 통신사 값은 대소문자가 섞여 있어 비교 전에 맞춰야 함(조건별 값 변환)
#NOT IN을 NOT EXISTS로
연습 문제의 NOT IN 문장:
SELECT ename
FROM emp
WHERE empno NOT IN (SELECT mgr
FROM emp
WHERE mgr IS NOT NULL);결과 해석:
같은 뜻을 NOT EXISTS로 쓰면 다음과 같음(답 확인 불가)
SELECT e.ename
FROM emp e
WHERE NOT EXISTS (SELECT 1
FROM emp m
WHERE m.mgr = e.empno);결과 해석:
NOT EXISTS는 서브쿼리 행이 있는지만 보므로, NOT IN처럼 서브쿼리 결과의 NULL 때문에 전체가 비는 문제가 없음(NOT IN과 NULL)
#업무형 실습
실습에서는 쇼핑몰 고객, 주문, 쿠폰, 상품, 주문상세 테이블을 만들고 두 문제를 냈음
- EXISTS: 2024년 5월에 상태가
PAID인 주문이 한 건이라도 있는 고객의 ID, 이름, 등급 - NOT EXISTS: 쿠폰을 한 번도 받지 않은 고객의 ID, 이름, 가입일
실습의 테이블 삭제 스크립트에는 DROP TABLE order_items가 두 번 있음
#구문과 옵션 설명
서브쿼리 안의 열 참조는 별칭으로 한정해 안쪽, 바깥쪽 열의 혼동을 피함
바깥 테이블에 같은 키가 여러 행이면 EXISTS는 그 행들을 자동으로 중복 제거하지 않음
#사용 조건과 주의사항
NOT IN을 NOT EXISTS로 바꿀 때는 비교하는 바깥 키의 NULL 가능성도 확인해야 함
본문의 두 형태를 같은 뜻으로 보는 설명은 바깥 empno가 NULL이 아닌 키인 조건임