데이터베이스 용어 사전
SQLsubquery · 상관 서브쿼리 · EXISTS · IN

서브쿼리

쿼리 안의 쿼리. 바깥 행을 참조하면 상관 서브쿼리가 되며 행마다 실행될 수 있다.

다른 쿼리 안에 들어간 SELECT. 바깥을 참조하느냐로 성격이 갈린다.

두 종류

-- 비상관(uncorrelated) — 혼자서도 실행된다. 한 번만 계산된다
SELECT * FROM emp WHERE salary > (SELECT avg(salary) FROM emp);

-- 상관(correlated) — 바깥 행(e)을 참조한다. 개념적으로 행마다 실행된다
SELECT * FROM emp e
 WHERE salary > (SELECT avg(salary) FROM emp WHERE dept = e.dept);

상관 서브쿼리는 행마다 도는 것처럼 보이지만, 옵티마이저가 대개 조인이나 해시 집계로 바꿔 한 번에 처리한다. 그래서 "상관 서브쿼리는 무조건 느리다"는 옛말이다 — 다만 실행계획으로 확인해야 한다.

위치별 이름

SELECT (SELECT name FROM member WHERE id = o.member_id)   -- 스칼라 서브쿼리 (1행 1칼럼)
  FROM orders o
 WHERE o.member_id IN (SELECT id FROM member WHERE grade='GOLD');   -- 조건절

SELECT * FROM (SELECT ... ) t;                            -- 인라인 뷰 (FROM 절)

스칼라 서브쿼리는 2행 이상을 반환하면 오류가 난다 — 데이터가 늘면서 어느 날 갑자기 터지는 유형이다.

IN vs EXISTS — NULL에서 갈린다

-- ⚠️ NOT IN 은 목록에 NULL 이 하나라도 있으면 결과가 통째로 비어 버린다
SELECT * FROM member WHERE id NOT IN (SELECT member_id FROM orders);
-- orders.member_id 에 NULL 이 있으면 → 0건

-- ✅ NOT EXISTS 는 NULL 에 안전하다
SELECT * FROM member m
 WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.member_id = m.id);

이유는 3값 논리다. id NOT IN (1, 2, NULL)id<>1 AND id<>2 AND id<>NULL이 되고, 마지막이 NULL이라 전체가 절대 true가 되지 않는다.

"NOT IN 대신 NOT EXISTS" 는 실무에서 가장 값어치 있는 습관 중 하나다.

EXISTS는 첫 일치에서 멈춘다

-- 존재 여부만 필요할 때 count 를 세지 않는다
-- ❌ 전부 센다
SELECT * FROM member m WHERE (SELECT count(*) FROM orders o WHERE o.member_id=m.id) > 0;
-- ✅ 하나 찾으면 즉시 종료
SELECT * FROM member m WHERE EXISTS (SELECT 1 FROM orders o WHERE o.member_id=m.id);

어떤 걸 고를까

  • 결과가 작고 고정적 — IN 이 읽기 쉽다
  • 바깥 행마다 존재만 확인 — EXISTS
  • 집계 결과를 여러 번 참조 — CTE 로 빼는 게 낫다
  • 행마다 값을 하나 붙인다 — 스칼라 서브쿼리 또는 LEFT JOIN

면접 함정

  • "IN과 EXISTS는 성능만 다르다"NOT IN은 NULL에서 결과 자체가 다르다.
  • "서브쿼리는 조인으로 바꿔야 빠르다" → 옵티마이저가 대개 알아서 변환한다. 계획을 보고 판단한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — SQL 쿼리문 — SELECT·조인·서브쿼리·집계·윈도우·CTE