2과목 · SQL 기본 및 활용·12장
서브쿼리
스칼라/인라인뷰/상관/중첩 서브쿼리와 EXISTS·IN·ANY·ALL, WITH(CTE)까지 정리합니다.
1. 서브쿼리란
쿼리 안에 또 다른 쿼리가 들어간 구조. 위치와 관계에 따라 다음과 같이 분류됩니다.
| 위치 | 이름 |
|---|---|
SELECT 리스트 안 |
스칼라 서브쿼리(Scalar Subquery) |
FROM 절 안 |
인라인 뷰(Inline View) |
WHERE / HAVING 조건 |
중첩 서브쿼리(Nested Subquery) |
| 메인과의 관계 | 이름 |
|---|---|
| 서브쿼리에 메인 컬럼 참조 | 상관 서브쿼리(Correlated) |
| 독립적으로 실행 가능 | 비상관 서브쿼리(Uncorrelated) |
2. 스칼라 서브쿼리
한 행, 한 컬럼을 반환해 값처럼 사용.
SELECT
e.name,
(SELECT name FROM dept d WHERE d.dept_id = e.dept_id) AS dept_name
FROM emp e;
- 결과가 여러 행이면 오류.
- 한 번도 매칭이 없으면 NULL 반환.
3. 인라인 뷰 (FROM 절 서브쿼리)
서브쿼리 결과를 임시 테이블처럼 취급.
SELECT dept_id, avg_sal
FROM (
SELECT dept_id, AVG(salary) AS avg_sal FROM emp GROUP BY dept_id
) t
WHERE avg_sal >= 4000000;
4. WHERE 절 서브쿼리 (중첩)
단일행 서브쿼리 — =, <, > 비교
SELECT * FROM emp WHERE salary > (SELECT AVG(salary) FROM emp);
다중행 서브쿼리 — IN, ANY, ALL, EXISTS
-- IN: OR 체인
SELECT * FROM emp WHERE dept_id IN (SELECT dept_id FROM dept WHERE active='Y');
-- ANY: "어떤 하나라도 만족"
SELECT * FROM emp WHERE salary > ANY (SELECT salary FROM emp WHERE job='CLERK');
-- ALL: "모두 만족"
SELECT * FROM emp WHERE salary > ALL (SELECT salary FROM emp WHERE job='CLERK');
-- EXISTS: 존재 여부만
SELECT * FROM emp e
WHERE EXISTS (SELECT 1 FROM dept d WHERE d.dept_id = e.dept_id AND d.active='Y');
ANY / ALL 의미
> ANY= "최솟값보다 크면 참".> ALL= "최댓값보다 커야 참".= ANY≡IN.<> ALL≡NOT IN(단, NULL 주의).
5. 상관 서브쿼리 (Correlated)
서브쿼리가 메인 쿼리의 컬럼을 참조. 외부 쿼리 행마다 서브쿼리가 실행되는 구조(논리상).
-- 각 부서에서 급여가 가장 높은 사원
SELECT *
FROM emp e1
WHERE salary = (
SELECT MAX(salary) FROM emp e2 WHERE e2.dept_id = e1.dept_id
);
6. NOT IN과 NULL의 함정
NOT IN 서브쿼리에 NULL이 하나라도 있으면 전체 조건이 UNKNOWN으로 평가되어 결과가 0건이 됩니다.
-- 위험
SELECT * FROM emp WHERE dept_id NOT IN (SELECT dept_id FROM blacklist);
-- 안전: NULL 제거
SELECT * FROM emp
WHERE dept_id NOT IN (SELECT dept_id FROM blacklist WHERE dept_id IS NOT NULL);
-- 더 안전: NOT EXISTS 로
SELECT * FROM emp e
WHERE NOT EXISTS (SELECT 1 FROM blacklist b WHERE b.dept_id = e.dept_id);
7. WITH (CTE, Common Table Expression)
가독성이 좋은 이름 붙은 서브쿼리.
WITH dept_avg AS (
SELECT dept_id, AVG(salary) AS avg_sal FROM emp GROUP BY dept_id
),
top_depts AS (
SELECT dept_id FROM dept_avg WHERE avg_sal >= 4000000
)
SELECT e.*
FROM emp e
JOIN top_depts t ON e.dept_id = t.dept_id;
- 재귀 CTE는
WITH RECURSIVE로 작성 (계층형 질의 참고).
8. 서브쿼리의 분류 — 서브쿼리 유형
- Access Subquery: 메인의 조건을 제공자로서 미리 걸러줌 (주입 X).
- Filter (형): 메인에서 값을 주입받아 판단.
- Early Filter: 조인 전 단계에서 먼저 필터링.
- Associative / Semi-join: 존재 여부 기반 필터.
SQLD 단골: "서브쿼리가 메인쿼리의 제공자 역할을 하고 메인쿼리의 값이 서브쿼리에 주입되지 않는 유형" → Access Subquery.
9. 자주 출제되는 포인트
- 스칼라 서브쿼리는 1행 1컬럼.
NOT IN+ NULL의 함정.EXISTS는 존재만 확인 → SELECT 내용 무관.- 상관 서브쿼리는 메인 컬럼을 참조하는 서브쿼리.
WITH는 가독성을 위한 CTE.
10. 요약 체크리스트
- 서브쿼리의 위치/관계별 4가지 분류를 안다.
- ANY/ALL의 의미를 예로 설명할 수 있다.
- NOT IN과 NULL의 함정을 피하는 세 가지 방법을 안다.
- WITH 절로 가독성 있는 복합 쿼리를 작성할 수 있다.