← 개념서모바일 버전
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 = "최댓값보다 커야 참".
  • = ANYIN.
  • <> ALLNOT 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 절로 가독성 있는 복합 쿼리를 작성할 수 있다.