서브쿼리: Single·Multi·Correlated
단일행·다중행·상관 서브쿼리를 반환 행 수와 외부 행 의존성으로 구분한다.
핵심 요약
단일행·다중행·상관 서브쿼리를 반환 행 수와 외부 행 의존성으로 구분한다.
핵심 질문
- 서브쿼리: Single·Multi·Correlated에서 반드시 구분해야 할 개념과 결과 규칙은 무엇인가?
- 0건·1건·여러 건과 NULL·동점·중복 데이터에서 결과가 어떻게 달라지는가?
- 비슷해 보이는 문법과 결과가 같아지는 조건, 달라지는 조건은 무엇인가?
- 작은 샘플 데이터를 이용해 결과를 실수 없이 예측하는 순서는 무엇인가?
학습 목표
- 서브쿼리 반환 건수에 맞는 연산자를 선택한다.
- 상관 서브쿼리가 외부 쿼리의 행을 참조하는 방식을 설명한다.
개념 지도
외부 행 단위 → 서브쿼리 반환 행 수 → 연산자 규칙 → NULL·상관 여부 확인
핵심 내용
서브쿼리는 다른 SQL 안에 포함된 질의다. 위치에 따라 스칼라 서브쿼리, 인라인 뷰, WHERE 서브쿼리 등으로 사용된다.
단일행 서브쿼리는 0 또는 1행을 기대하며 =, <, > 같은 단일행 연산자를 쓴다. 여러 행을 반환할 수 있으면 IN, ANY, ALL, EXISTS 같은 연산자가 필요하다.
SELECT e.empno, e.sal
FROM emp e
WHERE e.sal > (
SELECT AVG(sal) FROM emp
);
상관 서브쿼리는 내부 쿼리가 외부 행의 값을 참조한다.
SELECT e.empno
FROM emp e
WHERE e.sal > (
SELECT AVG(x.sal) FROM emp x WHERE x.deptno = e.deptno
);
상관 서브쿼리는 외부 Query의 현재 행 값을 참조하며, 각 외부 행에 대해 조건의 참·거짓을 판단하는 형태로 해석한다.
흔한 오해와 주의점
=뒤 서브쿼리가 두 행 이상 반환하면 단일행 서브쿼리 오류가 난다.- 스칼라 서브쿼리가 0행이면 NULL, 두 행 이상이면 오류다.
- 상관 서브쿼리의 논리적 의미와 실제 반복 실행 횟수를 동일시하지 않는다.
문항 풀이 보강: 다중행 비교와 그룹별 최신 행
| 표현 | 의미 |
|---|---|
x > ANY (subquery) | 결과 중 하나보다 크면 됨, 사실상 최솟값보다 큼 |
x > ALL (subquery) | 모든 결과보다 커야 함, 사실상 최댓값보다 큼 |
x IN (subquery) | 결과 중 같은 값이 하나라도 있음 |
EXISTS (subquery) | 조건을 만족하는 행이 하나라도 존재 |
서브쿼리가 빈 집합일 때 ANY와 ALL의 논리 결과도 구분한다. NULL이 섞이면 3값 논리를 함께 적용한다.
그룹별 최초·최신 한 행
광고매체별 최초 광고처럼 “그룹별 한 행”을 구할 때 단순 MIN(날짜)만 선택하면 같은 행의 광고명을 함께 가져오지 못할 수 있다.
SELECT *
FROM (
SELECT p.*,
ROW_NUMBER() OVER (
PARTITION BY media_id
ORDER BY start_dt, post_id
) rn
FROM ad_post p
)
WHERE rn = 1;
상관 서브쿼리로 현재 행보다 더 빠른 행이 없는지 검사하거나, 집계 결과를 원본과 다시 조인하는 방법도 있다. 동점 처리 기준까지 명시해야 결과가 결정적이다.
스칼라 서브쿼리
SELECT 목록의 스칼라 서브쿼리는 0행이면 NULL, 정확히 1행이면 그 값, 2행 이상이면 오류다. COUNT(*) 집계는 입력이 없어도 0 한 행을 반환하므로 부양가족 수 같은 계산에 사용할 수 있다.
문제에 적용하는 순서
- 서브쿼리가 몇 행을 반환할 수 있는지 판단한다.
- 외부 행을 참조하는 상관 조건을 표시한다.
- 단일행 연산자와 다중행 연산자가 맞는지 본다.
- 그룹별 한 행이면 동점과 연결 컬럼의 일관성을 확인한다.
반환 행 수와 연산자 맞추기
| 서브쿼리 결과 | 사용할 수 있는 대표 연산자 | 다건일 때 |
|---|---|---|
| Scalar 0·1행 | =, <, > | 2행 이상이면 ORA-01427 |
| Multi Row | IN, ANY, ALL, EXISTS | 집합 규칙으로 평가 |
| Correlated | 외부 행마다 논리적으로 평가 | Unnesting·Cache 가능 |
SELECT ename
FROM emp
WHERE sal > (SELECT AVG(sal) FROM emp);
집계 Scalar Subquery는 입력이 0행이어도 AVG 결과 한 행(NULL)을 반환한다는 점과, 일반 Scalar Subquery의 0행→NULL 규칙을 구분한다.
ANY와 ALL
x > ANY(10,20,30) → 최소값 10보다 크면 참 가능
x > ALL(10,20,30) → 최대값 30보다 커야 참
빈 집합과 NULL이 포함되면 결과가 달라지므로 논리식을 직접 계산한다.
상관 서브쿼리
SELECT d.deptno
FROM dept d
WHERE EXISTS (
SELECT 1 FROM emp e
WHERE e.deptno = d.deptno
AND e.status = 'ACTIVE'
);
IN과 EXISTS는 NULL과 중복의 영향까지 같을 때에만 서로 바꿔 쓸 수 있다. 문법 모양만 보고 두 표현이 항상 같은 결과라고 판단하지 않는다.
변환 주의
Scalar를 Join으로 바꾸면 오른쪽 중복 때문에 외부 행 수가 늘 수 있다. 0건→NULL, 다건→오류라는 원래 의미를 보존하는지 먼저 확인한다.
결과를 검증하는 순서
- 각 Query Block이 만드는 한 행의 의미를 먼저 적습니다.
- 조건을 적용하기 전 원본 행과 적용 후 남는 행을 작은 표로 그립니다.
- NULL 비교가
TRUE,FALSE,UNKNOWN중 무엇인지 구분합니다. - 중복 제거, 그룹화, 정렬과 행 제한이 적용되는 순서를 확인합니다.
- 데이터가 0건·1건·여러 건일 때도 같은 규칙이 성립하는지 검증합니다.
실무와 시험에서 함께 확인할 항목
ORDER BY가 없다면 결과 순서를 가정하지 않습니다.- 문자열·숫자·날짜 비교에서는 데이터 타입과 명시적 형변환을 확인합니다.
- 같은 결과처럼 보이는 SQL도 NULL과 중복이 있을 때 달라질 수 있습니다.
- 문법을 외우기 전에 샘플 데이터 3~5행으로 결과를 직접 계산합니다.
마지막 점검
- 작성 순서가 아니라 SQL의 논리적 처리 순서로 결과를 계산합니다.
- NULL을 0이나 빈 값과 같은 것으로 취급하지 않습니다.
ORDER BY가 없는 결과 순서와 DISTINCT 없는 중복 제거를 가정하지 않습니다.- 비슷한 문법은 0건·다건·NULL 데이터를 넣어 결과가 정말 같은지 확인합니다.
복습 문제
- 부서별 평균보다 급여가 높은 사원을 찾는 서브쿼리는 왜 상관 서브쿼리인가?
- 다중행 결과에
=를 사용하면 어떻게 되는가? - 샘플 데이터 3행으로 결과를 직접 계산할 수 있는가?
- NULL이 포함될 때 결과가 달라지는 지점은 어디인가?