서브쿼리, 집합 연산자, 뷰
서브쿼리의 위치와 반환 건수, IN·ANY·ALL·EXISTS, UNION 계열 집합 연산자와 뷰의 장점을 시험 기준으로 정리합니다.
INEXISTSUNIONMINUSVIEW 서브쿼리란
서브쿼리는 SQL 안에 들어 있는 또 다른 SQL입니다. 바깥 쿼리가 메인 쿼리이고, 안쪽 쿼리가 서브쿼리입니다.
SELECT ename, sal
FROM emp
WHERE sal > (
SELECT AVG(sal)
FROM emp
);
위 쿼리는 전체 평균 급여보다 급여가 높은 사원을 찾습니다. 서브쿼리가 먼저 평균을 구하고, 메인 쿼리가 그 값을 조건에 사용합니다.
비연관과 연관 서브쿼리
비연관 서브쿼리 (Non-correlated)
- 메인 쿼리의 열을 참조하지 않음
- 안쪽 쿼리만 따로 실행 가능
- 한 번 실행 후 결과 재사용
- 대개 성능에 유리
연관 서브쿼리 (Correlated)
- 메인 쿼리의 열을 참조함
- 안쪽만 따로 실행 불가
- 바깥 행마다 안쪽이 재평가될 수 있음 (스칼라 캐싱으로 최적화)
EXISTS와 자주 결합
SELECT e.ename, e.sal, e.deptno
FROM emp e
WHERE e.sal > (
SELECT AVG(x.sal)
FROM emp x
WHERE x.deptno = e.deptno
);
이 쿼리는 “자기 부서 평균보다 급여가 높은 사원”을 찾습니다. 서브쿼리 안의 e.deptno가 메인 쿼리를 참조하므로 연관 서브쿼리입니다.
반환 건수에 맞는 연산자
서브쿼리 문제는 결과가 몇 행인지 판단하면 절반은 풀립니다.
| 서브쿼리 결과 | 사용할 수 있는 연산자 | 예 |
|---|---|---|
| 단일 행 | =, >, <, >=, <=, <> | 평균보다 큰 급여 |
| 다중 행 | IN, ANY, ALL, EXISTS | 여러 부서 중 하나 |
| 다중 컬럼 | (col1, col2) IN (...) | 팀별 최솟값 같은 복합 조건 |
단일 행 연산자에 여러 행이 반환되면 오류가 발생합니다. 반대로 여러 행이 나올 수 있으면 IN, ANY, ALL, EXISTS를 고려합니다.
IN, ANY, ALL, EXISTS
| 연산자 | 의미 | 초보자용 해석 |
|---|---|---|
IN | 목록 중 하나와 같음 | 후보 중 하나면 통과 |
= ANY | 결과 중 하나라도 만족 (= IN) | 최소 하나만 이기면 통과 |
< ANY | 어떤 값보다 작은 것 | 최댓값보다 작으면 통과 |
> ALL | 모든 값보다 큼 (= > MAX) | 전원을 이겨야 통과 |
< ALL | 모든 값보다 작음 (= < MIN) | 전원에게 져야 통과 |
EXISTS | 조건을 만족하는 행 존재 | 값보다 존재 여부가 중요 |
SELECT d.deptno, d.dname
FROM dept d
WHERE EXISTS (
SELECT 1
FROM emp e
WHERE e.deptno = d.deptno
);
EXISTS는 서브쿼리가 어떤 값을 반환하는지보다 “행이 존재하는가”가 핵심입니다. 그래서 SELECT 1처럼 의미 없는 상수를 써도 됩니다.
서브쿼리 위치별 이름
| 위치 | 이름 | 조건 |
|---|---|---|
SELECT 절 | 스칼라 서브쿼리 | 한 행·한 컬럼만 반환. 2건 이상이면 오류 |
FROM 절 | 인라인 뷰 | 서브쿼리 결과를 테이블처럼 사용 |
WHERE 절 | 중첩 서브쿼리 | 비교·존재 조건 |
HAVING 절 | 그룹 조건 서브쿼리 | 집계 결과 비교 |
집합 연산자
집합 연산자는 두 SELECT 결과를 위아래로 결합합니다. 조인이 좌우로 컬럼을 붙이는 것과 다릅니다.
| 연산자 | 결과 | 중복 처리 | 정렬 |
|---|---|---|---|
UNION | 합집합 | 중복 제거 | 내부 정렬 발생 (비용↑) |
UNION ALL | 합집합 | 중복 유지 | 정렬 없음 (빠름) |
INTERSECT | 교집합 | 중복 제거 | 중복 제거 목적 정렬 |
EXCEPT / MINUS | 차집합 | 앞 결과에서 뒤 결과 제거 | 내부 정렬 |
SELECT deptno FROM emp
UNION
SELECT deptno FROM dept;
집합 연산자를 쓰려면 각 SELECT의 컬럼 개수와 대응되는 데이터 타입이 맞아야 합니다. 컬럼 이름은 일치할 필요 없고, 결과 헤더는 첫 번째 SELECT를 따릅니다.
ORDER BY는 가장 마지막 SELECT 뒤에 한 번만 쓸 수 있습니다.
뷰
뷰는 자주 쓰는 SELECT에 이름을 붙여 놓은 가상 테이블입니다. 실제 데이터를 별도로 복사하는 것이 아니라, 조회할 때 정의된 SQL을 재실행합니다.
CREATE VIEW v_emp_dept AS
SELECT e.empno, e.ename, d.dname
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
뷰의 장점은 세 가지로 정리할 수 있습니다.
- 편의성: 복잡한 SQL을 단순한 이름으로 사용
- 보안성: 필요한 컬럼만 공개해 민감 정보 차단
- 독립성: 원본 테이블 구조가 바뀌어도 사용자 쿼리 영향 최소화
서브쿼리는 결과 건수에 맞는 연산자를 고르고, 집합 연산자는 SELECT 결과를 세로로 합칩니다.