IN·EXISTS·NOT IN과 NULL
존재 여부를 검사하는 IN·EXISTS와 부정 조건에서 NULL 때문에 생기는 결과 차이를 이해한다.
핵심 요약
존재 여부를 검사하는 IN·EXISTS와 부정 조건에서 NULL 때문에 생기는 결과 차이를 이해한다.
핵심 질문
- IN·EXISTS·NOT IN과 NULL에서 반드시 구분해야 할 개념과 결과 규칙은 무엇인가?
- 0건·1건·여러 건과 NULL·동점·중복 데이터에서 결과가 어떻게 달라지는가?
- 비슷해 보이는 문법과 결과가 같아지는 조건, 달라지는 조건은 무엇인가?
- 작은 샘플 데이터를 이용해 결과를 실수 없이 예측하는 순서는 무엇인가?
학습 목표
- IN과 EXISTS의 논리적 의미를 설명한다.
- NOT IN의 NULL 함정을 NOT EXISTS와 비교한다.
개념 지도
외부 행 단위 → 서브쿼리 반환 행 수 → 연산자 규칙 → NULL·상관 여부 확인
핵심 내용
IN은 왼쪽 값이 결과 집합 중 하나와 같은지 비교한다. EXISTS는 서브쿼리가 한 행이라도 반환하는지만 본다. 상관 EXISTS에서는 SELECT 목록 값보다 행의 존재가 중요하다.
SELECT d.deptno
FROM dept d
WHERE EXISTS (
SELECT 1 FROM emp e WHERE e.deptno = d.deptno
);
부정 조건에서는 NULL이 핵심이다. x NOT IN (1, 2, NULL)은 x<>1 AND x<>2 AND x<>NULL과 연결되며 마지막 비교가 UNKNOWN이므로 TRUE가 되기 어렵다.
SELECT d.deptno
FROM dept d
WHERE NOT EXISTS (
SELECT 1 FROM emp e WHERE e.deptno = d.deptno
);
미매칭을 찾을 때 NOT EXISTS는 조인 조건의 의미가 명확하다. NOT IN을 쓰려면 서브쿼리 결과에서 NULL이 절대 나오지 않음을 제약조건이나 조건으로 보장해야 한다.
흔한 오해와 주의점
- EXISTS 내부의
SELECT 1은 숫자 1과 비교한다는 뜻이 아니다. - IN과 EXISTS의 성능을 문법만 보고 항상 어느 쪽이 빠르다고 단정할 수 없다.
- NOT IN은 외부 값 자체가 NULL인 경우도 TRUE가 되지 않는다.
문항 풀이 보강: 부재 조건을 세 방식으로 표현하기
부양가족이 없는 사원을 찾는 논리는 “현재 사원과 연결되는 가족 행이 존재하지 않는다”다.
-- 가장 직접적인 표현
SELECT e.name
FROM employee e
WHERE NOT EXISTS (
SELECT 1
FROM family f
WHERE f.employee_id = e.employee_id
);
-- 외부 조인으로 표현
SELECT e.name
FROM employee e
LEFT JOIN family f
ON f.employee_id = e.employee_id
WHERE f.employee_id IS NULL;
NOT IN도 사용할 수 있지만 서브쿼리 결과에 NULL이 있으면 전체가 UNKNOWN이 될 수 있다. FK가 NOT NULL인지 확인하거나 서브쿼리에서 NULL을 제거해야 한다.
SQL Server EXCEPT나 Oracle MINUS로 구한 차집합은 키 전체의 조합을 비교한다. 복합키 (A,B)의 차집합을 A NOT IN (SELECT A FROM t2) AND B NOT IN (SELECT B FROM t2)으로 각각 나누면 “같은 한 행” 비교가 아니므로 결과가 달라질 수 있다.
NOT IN의 NULL 함정을 행별로 계산하기
DEPT.deptno = {10, 20, NULL}
EMP.deptno = {10, 30}
SELECT deptno
FROM emp
WHERE deptno NOT IN (SELECT deptno FROM dept);
30 <> 10 AND 30 <> 20 AND 30 <> NULL에서 마지막 비교가 UNKNOWN이므로 전체가 TRUE가 되지 않는다. 결과가 한 건도 없을 수 있다.
안전한 미존재 검사는 NULL 의미가 명확한 NOT EXISTS를 사용한다.
SELECT e.deptno
FROM emp e
WHERE NOT EXISTS (
SELECT 1
FROM dept d
WHERE d.deptno = e.deptno
);
서브쿼리 컬럼이 NOT NULL로 보장되거나 명시적으로 WHERE deptno IS NOT NULL을 추가하면 NOT IN과 같은 결과가 될 수 있지만, 제약과 업무 의미를 확인한다.
IN과 EXISTS의 의미
IN은 왼쪽 값이 오른쪽 값 집합에 속하는지 본다.EXISTS는 상관 조건을 만족하는 행이 하나라도 있는지 본다.- IN은 값의 소속 여부를, EXISTS는 조건을 만족하는 행의 존재 여부를 표현하므로 NULL과 상관 조건을 함께 확인한다.
ANY·ALL 연결
x > ANY(subquery)는 하나보다만 커도 되고, x > ALL(subquery)는 모든 값보다 커야 한다. 빈 집합·NULL 포함 시 3값 논리를 작은 데이터로 계산한다.
결과를 검증하는 순서
- 각 Query Block이 만드는 한 행의 의미를 먼저 적습니다.
- 조건을 적용하기 전 원본 행과 적용 후 남는 행을 작은 표로 그립니다.
- NULL 비교가
TRUE,FALSE,UNKNOWN중 무엇인지 구분합니다. - 중복 제거, 그룹화, 정렬과 행 제한이 적용되는 순서를 확인합니다.
- 데이터가 0건·1건·여러 건일 때도 같은 규칙이 성립하는지 검증합니다.
실무와 시험에서 함께 확인할 항목
ORDER BY가 없다면 결과 순서를 가정하지 않습니다.- 문자열·숫자·날짜 비교에서는 데이터 타입과 명시적 형변환을 확인합니다.
- 같은 결과처럼 보이는 SQL도 NULL과 중복이 있을 때 달라질 수 있습니다.
- 문법을 외우기 전에 샘플 데이터 3~5행으로 결과를 직접 계산합니다.
마지막 점검
- 작성 순서가 아니라 SQL의 논리적 처리 순서로 결과를 계산합니다.
- NULL을 0이나 빈 값과 같은 것으로 취급하지 않습니다.
ORDER BY가 없는 결과 순서와 DISTINCT 없는 중복 제거를 가정하지 않습니다.- 비슷한 문법은 0건·다건·NULL 데이터를 넣어 결과가 정말 같은지 확인합니다.
복습 문제
- 부서번호 목록에 NULL이 포함된 NOT IN 결과가 비는 이유는?
- EXISTS가 확인하는 것은 컬럼 값인가, 행의 존재인가?
- 샘플 데이터 3행으로 결과를 직접 계산할 수 있는가?
- NULL이 포함될 때 결과가 달라지는 지점은 어디인가?