서브쿼리: 스칼라·인라인 뷰·상관 서브쿼리, DB별 차이
2. 핵심 원리
2.1 스칼라 서브쿼리
SELECT 절 안에서 값 하나를 돌려주는 서브쿼리입니다. 열처럼 취급되므로 반드시 한 행, 한 열만 반환해야 합니다. 전체 평균처럼 모든 행에 같은 값을 붙이거나, 행마다 다른 값을 붙이는 상관 서브쿼리(2.4절) 형태로도 씁니다.
핵심스칼라 서브쿼리는 "행 하나, 열 하나"라는 약속이 깨지는 순간 오류입니다. WHERE 절의 단일행 서브쿼리도 같은 약속을 씁니다.
2.2 인라인 뷰
FROM 절에 SELECT 문을 그대로 넣어 임시 테이블처럼 쓰는 방식입니다. GROUP BY 로 집계한 결과를 다시 걸러야 할 때 유용합니다. HAVING 으로도 되는 조건이 많지만, 집계 결과에 다른 테이블을 조인하거나 여러 단계로 가공할 때는 인라인 뷰가 더 읽기 쉽습니다.
인라인 뷰는 별칭이 있어야 컬럼을 t.dept 처럼 가리킬 수 있습니다. 별칭 표기 방식은 DB마다 다르고, 4절에서 비교합니다.
2.3 WHERE 절의 단일행·다중행 서브쿼리
WHERE 절에 오는 서브쿼리는 비교 연산자에 따라 반환 행 수 제약이 다릅니다.
| 연산자 | 허용 행 수 | 의미 |
|---|---|---|
=, >, <, >=, <= |
단일행(1행) | 값 하나와 직접 비교 |
IN |
다중행 | 목록 중 하나와 일치 |
> ALL (...) |
다중행 | 목록의 모든 값보다 큼 |
> ANY (...) |
다중행 | 목록 중 하나라도보다 큼 |
= ALL 이나 = ANY 도 문법상 가능하지만 실무에서는 IN 으로 대체하는 경우가 대부분이라 이 레슨에서는 다루지 않습니다.
주의단일행 자리(
=등)에 다중행이 반환되면 "Scalar subquery contains more than one row" 류의 오류가 납니다. 조건절에서 그룹 조건(부서 등)을 빠뜨리지 않았는지 먼저 확인합니다.
2.4 상관 서브쿼리
바깥 쿼리의 컬럼을 안쪽 서브쿼리에서 참조하는 형태입니다. 바깥 쿼리가 한 행을 처리할 때마다 서브쿼리가 그 행의 값(예: dept)을 받아 다시 실행됩니다. "부서별 최고 급여자"처럼 그룹마다 기준이 달라지는 조회에 씁니다.
상관 서브쿼리는 개념적으로 바깥 쿼리 행 수만큼 반복 실행됩니다. 실제 DB는 실행 계획 단계에서 조인이나 세미조인으로 바꿔 최적화하기도 하지만, 옵티마이저가 그렇게 바꿔주지 못하는 경우도 많아 직접 JOIN 이나 윈도 함수로 재작성하는 습관이 필요합니다.
2.5 EXISTS 맛보기
EXISTS (서브쿼리) 는 서브쿼리가 결과를 한 행이라도 반환하면 참이 됩니다. 값 자체가 아니라 존재 여부만 보므로 상관 서브쿼리와 자주 같이 씁니다. NOT EXISTS 로 "다른 테이블에 없는 행"을 찾는 패턴, EXISTS 와 IN 의 성능 차이는 다음 레슨(서브쿼리 02: EXISTS·집합 연산)에서 본격적으로 다룹니다.
2.6 성능 메모: 스칼라 서브쿼리 캐싱과 재작성
스칼라 서브쿼리는 원칙적으로 바깥 쿼리 행마다 실행됩니다. Oracle 은 같은 입력값에 같은 서브쿼리를 다시 실행하지 않도록 결과를 캐싱하는 최적화(스칼라 서브쿼리 캐싱)를 자체적으로 수행합니다. 다만 입력값이 자주 바뀌는 데이터에서는 캐시 효과가 작습니다.
상관 서브쿼리도 같은 이유로 행이 많아질수록 느려지기 쉽습니다. "부서별 최고 급여자" 같은 패턴은 아래 두 방식으로 재작성할 수 있고, 3절 예제에서 실행 결과가 같음을 확인합니다.
- JOIN 재작성: 그룹별 집계를 인라인 뷰로 먼저 구하고 원본 테이블과 조인.
- 윈도 함수 재작성:
MAX(sal) OVER (PARTITION BY dept)로 한 번에 계산.
팁상관 서브쿼리가 느리면 먼저 JOIN 재작성을, 조회 컬럼이 많아 조인이 번거로우면 윈도 함수 재작성을 시도합니다.