
서브쿼리(Sub Query)란?
하나의 쿼리 결과를 다른 SQL문에 전달하기 위해 두 개 이상의 SQL문을 하나의 SQL문으로 합쳐 작성하는 방법입니다.
예를 들어, "SMITH 사원과 같은 부서에서 근무하는 사원"을 찾는 상황을 가정해 보겠습니다.
서브쿼리를 사용하지 않는 경우
SMITH 사원의 부서 번호(deptno)를 조회합니다. (SELECT deptno FROM emp WHERE ename = 'SMITH';)SELECT * FROM emp WHERE deptno = 20;)서브쿼리를 사용하는 경우
두 단계의 쿼리를 하나로 합쳐서 간결하게 실행할 수 있습니다.
SELECT empno, ename, deptno, job
FROM emp
WHERE deptno = (SELECT deptno FROM emp WHERE ename = 'SMITH');
| ⚠️ 서브쿼리 작성 시 주의사항
() 로 감싸야 합니다.;을 붙이지 않습니다.ORDER BY 절은 서브쿼리 내에서 사용하는 데 제한이 있거나 의미가 없는 경우가 많습니다.| 서브쿼리의 유형
1. 반환되는 행의 수에 따른 분류
단일 행 서브쿼리 (Single-row Subquery)
=, >, >=, <, <=, <>)와 함께 사용됩니다.다중 행 서브쿼리 (Multi-row Subquery)
IN, ANY, ALL, EXISTS)와 함께 사용됩니다.2. 기능 및 위치에 따른 분류
상호 연관 서브쿼리 (Correlated Subquery)
다중 열 서브쿼리 (Pairwise/Multi-column Subquery)
스칼라 서브쿼리 (Scalar Subquery)
인라인 뷰 (Inline View)
FROM 절에 작성되며, 실행 결과를 마치 하나의 테이블처럼 사용하는 일회성 뷰(View)입니다.| 서브쿼리 사용 위치
서브쿼리는 SQL의 다양한 곳에서 활용될 수 있습니다.
SELECT 절FROM 절 (인라인 뷰)WHERE 절HAVING 절ORDER BY 절UPDATE 문의 SET 절INSERT 문의 VALUES 절 또는 VALUES 절 대체CREATE TABLE 문| 다중 행 비교 연산자
IN
메인 쿼리의 비교 조건이 서브쿼리 결과 중 하나라도 만족하면 참입니다.
ANY
메인 쿼리의 비교 조건이 서브쿼리 결과 중 하나라도 만족하면 참입니다. ANY는 항상 비교 연산자와 함께 사용해야 합니다.
= ANY : IN 연산자와 동일합니다.> ANY : 서브쿼리 결과의 최솟값보다 크면 참입니다.< ANY : 서브쿼리 결과의 최댓값보다 작으면 참입니다.-- inventory 테이블에 있는 어떤 제품의 수량(quantity)보다 적은 수량을 가진 제품을 products 테이블에서 조회
SELECT product_name, quantity
FROM products
WHERE quantity < ANY (SELECT quantity FROM inventory);
ALL
메인 쿼리의 비교 조건이 서브쿼리의 모든 결과를 만족하면 참입니다.
> ALL : 서브쿼리 결과의 최댓값보다 크면 참입니다.< ALL : 서브쿼리 결과의 최솟값보다 작으면 참입니다.EXISTS
서브쿼리의 실행 결과가 존재하면 (하나 이상의 행이 반환되면) 참입니다. SEMI JOIN이라고도 불립니다.
| 주요 서브쿼리 유형별 예시
상호 연관 서브쿼리 (Correlated Subquery)
메인 쿼리와 독립적으로 실행될 수 없으며, 메인 쿼리가 먼저 실행되어 조회된 값을 서브쿼리에서 참조하는 구조입니다.
-- 관리자(자신이 누군가의 mgr인 직원)인 사원의 정보를 조회
SELECT empno, ename, '관리자' AS 구분
FROM emp e
WHERE EXISTS (
SELECT 1
FROM emp e2
WHERE e.empno = e2.mgr -- 메인 쿼리의 e.empno 값을 참조
);
다중 열 서브쿼리 (Multi-column Subquery)
여러 열을 동시에 비교하여 조건을 만족하는 결과를 찾습니다.
❓ 각 부서의 최대 급여를 받는 사원을 조회하는 코드
올바른 쿼리 [ 1 ]
(deptno, sal) 쌍이 서브쿼리가 반환한 부서별 최대 급여 쌍 목록에 포함되는지 정확히 비교합니다.
SELECT ename, sal, deptno
FROM emp
WHERE (deptno, sal) IN (
SELECT deptno, MAX(sal)
FROM emp
GROUP BY deptno
);
잘못된 쿼리 [ 2 ]
deptno와 sal을 개별적으로 비교하기 때문에 의도와 다른 결과가 나옵니다.
SELECT ename, sal, deptno
FROM emp
WHERE deptno IN (SELECT deptno FROM emp GROUP BY deptno) -- 이 조건은 사실상 의미가 없음
AND sal IN (SELECT MAX(sal) FROM emp GROUP BY deptno);
이 쿼리는 '각 부서별 최고 연봉자'를 찾는 것이 아니라, '어떤 부서의 최고 급여액과 우연히 급여가 일치하는 모든 직원' 을 찾아냅니다. 예를 들어 10번 부서의 최대 급여가 5000이고 20번 부서 직원의 급여가 5000이라면, 이 직원은 20번 부서의 최대 급여 수령자가 아니더라도 조회됩니다.
스칼라 서브쿼리 (Scalar Subquery)
SELECT 절에서 마치 하나의 값처럼 사용되며, 단일 행, 단일 열을 반환합니다.
-- 사원별 정보와 함께 '자신의 부서 평균 급여'와 '전체 사원 평균 급여'를 조회
SELECT
ename, sal, deptno,
(SELECT ROUND(AVG(sal), 2) FROM emp d WHERE e.deptno = d.deptno) AS 부서평균급여,
(SELECT ROUND(AVG(sal), 2) FROM emp) AS 전체평균급여
FROM
emp e
ORDER BY
deptno;
인라인 뷰 (Inline View)
FROM 절에서 서브쿼리의 결과를 하나의 가상 테이블처럼 사용합니다. 복잡한 쿼리의 가독성을 높이거나, 가공된 데이터를 재사용할 때 유용합니다.
-- 각 부서별 평균 급여보다 많은 급여를 받는 사원들의 정보 조회
SELECT e.ename, e.sal, e.deptno
FROM emp e,
(SELECT deptno, AVG(sal) AS avg_sal FROM emp GROUP BY deptno) d -- 인라인 뷰
WHERE e.deptno = d.deptno
AND e.sal > d.avg_sal;
| 집합 연산
두 개 이상의 SELECT 문의 결과 집합을 하나로 결합합니다.
UNION ALL: 두 결과 집합의 합집합을 반환합니다. (중복 포함)UNION: 두 결과 집합의 합집합을 반환합니다. (중복 제거)INTERSECT: 두 결과 집합의 교집합을 반환합니다. (MySQL 8.0.31 이상)EXCEPT: 먼저 기술된 쿼리에서 뒤쪽 쿼리의 결과 집합을 뺀 차집합을 반환합니다. (MySQL 8.0.31 이상)