3. 코드 예제
소스: sql-src/mid_02_exists_in/01_exists_in.sql. 샘플 스키마에 orders(주문)를 더했습니다. orders.emp_id 는 담당 직원을 가리키고, 아직 담당이 배정되지 않은 주문은 NULL 입니다.
예제 1: EXISTS 로 주문이 있는 직원 찾기
SELECT
e.name
, e.dept
, e.sal
FROM emp e
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.emp_id = e.id)
ORDER BY e.id;NAME | DEPT | SAL
-------+------+----
이팀장 | 개발 | 650
박선임 | 개발 | 500
정팀장 | 영업 | 600
(3행)주문을 낸 세 직원만 남습니다. 이팀장은 주문이 2건, 정팀장도 2건이지만 EXISTS 는 존재 여부만 보므로 각 1행씩만 나옵니다.
예제 2: 같은 결과를 IN 으로
SELECT
e.name
, e.dept
, e.sal
FROM emp e
WHERE e.id IN (SELECT o.emp_id FROM orders o)
ORDER BY e.id;NAME | DEPT | SAL
-------+------+----
이팀장 | 개발 | 650
박선임 | 개발 | 500
정팀장 | 영업 | 600
(3행)EXISTS 와 똑같은 3행입니다. orders.emp_id 목록에 NULL(106번 주문)이 섞여 있지만, IN 은 "목록 중 하나와 같은가"만 확인하므로 매칭되는 값이 있으면 문제없이 참이 됩니다. NULL 문제는 IN 이 아니라 NOT IN 에서 생깁니다(2.4절, 4절).
예제 3: JOIN 은 주문 수만큼 행이 늘어난다
SELECT
e.name
, e.dept
, e.sal
, o.id AS order_id
FROM emp e
JOIN orders o ON o.emp_id = e.id
ORDER BY e.id, o.id;NAME | DEPT | SAL | ORDER_ID
-------+------+-----+---------
이팀장 | 개발 | 650 | 101
이팀장 | 개발 | 650 | 102
박선임 | 개발 | 500 | 103
정팀장 | 영업 | 600 | 104
정팀장 | 영업 | 600 | 105
(5행)같은 세 직원인데 5행입니다. JOIN 은 매칭되는 주문마다 직원 행을 다시 만들기 때문입니다. "직원별 최근 주문 하나만" 같은 요구가 아니라 "직원 목록만" 필요하다면 JOIN 보다 EXISTS·IN 이 맞는 선택입니다.
예제 4: 상관 EXISTS 로 취소된 주문이 있는 직원 찾기
SELECT
e.name
, e.dept
FROM emp e
WHERE EXISTS
(SELECT 1
FROM orders o
WHERE o.emp_id = e.id
AND o.status = '취소')
ORDER BY e.id;NAME | DEPT
-------+-----
정팀장 | 영업
(1행)정팀장의 주문 2건 중 하나(104번)가 취소 상태입니다. 서브쿼리 조건에 o.status = '취소' 를 더해, "주문이 있다"가 아니라 "취소된 주문이 있다"로 조건을 좁혔습니다.