Sub Query

worldclasscitizen·2025년 10월 10일

SSAFY

목록 보기
2/9

서브쿼리(Sub Query)란?

하나의 쿼리 결과를 다른 SQL문에 전달하기 위해 두 개 이상의 SQL문을 하나의 SQL문으로 합쳐 작성하는 방법입니다.

예를 들어, "SMITH 사원과 같은 부서에서 근무하는 사원"을 찾는 상황을 가정해 보겠습니다.

  • 서브쿼리를 사용하지 않는 경우

    1. SMITH 사원의 부서 번호(deptno)를 조회합니다. (SELECT deptno FROM emp WHERE ename = 'SMITH';)
    2. 위에서 얻은 부서 번호를 이용해 해당 부서의 사원 전체를 조회합니다. (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 ]
    deptnosal을 개별적으로 비교하기 때문에 의도와 다른 결과가 나옵니다.

    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 절에서 서브쿼리의 결과를 하나의 가상 테이블처럼 사용합니다. 복잡한 쿼리의 가독성을 높이거나, 가공된 데이터를 재사용할 때 유용합니다.

  • 반드시 별칭(Alias) 을 지정해야 합니다.
  • 데이터베이스에 저장되지 않는 일회성 뷰입니다.
-- 각 부서별 평균 급여보다 많은 급여를 받는 사원들의 정보 조회
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 이상)

0개의 댓글