9. 서브쿼리

1. 서브쿼리란?

서브쿼리는 쿼리 안에 포함된 또 다른 쿼리를 의미한다.

서브쿼리는 메인쿼리의 일부로 사용되며, 메인쿼리는 서브쿼리의 결과를 이용해 최종 실행된다.

서브쿼리는 괄호 () 안에 작성되며, 다양한 SQL 구문 내에서 사용할 수 있다.

다만, 서브쿼리는 편리하지만 성능 측면에서는 JOIN을 사용하는 것이 더 효율적인 경우가 많다.

1.1. 기본 구문

SELECT 칼럼명1, 칼럼명2, ...
FROM 테이블명
WHERE 칼럼명 연산자 (
  서브쿼리
);

 

특성 서브쿼리 JOIN
가독성 일반적으로 높음 (간단한 경우) 복잡한 조인 조건에서는 낮을 수 있음
성능 다중 실행 시 비효율적일 수 있음 일반적으로 더 효율적
사용 적합성 단순 필터링, 임시 결과 필요 시 여러 테이블 데이터 결합 시
결과 형태 필터링이나 집계 결과 테이블 간 결합된 완전한 결과

 

 

2. 서브쿼리의 위치별 특징

서브쿼리는 SQL 문의 여러 위치에서 사용될 수 있으며, 위치에 따라 다른 규칙과 제약사항이 적용된다.

2.1. SELECT 절에서의 서브쿼리

SELECT 절은 각 행의 개별 열 값을 표시하기 때문에 여러 값을 반환하면 어떤 값을 표시해야 할지 모호해진다.
행당 하나의 값만 표시할 수 있으므로 스칼라(단일) 값이 필요하다.

그러므로 SELECT 절에 사용되는 서브쿼리는 실행 결과로 단일값(스칼라 값)을 반환해야 한다.

이러한 서브쿼리를 스칼라 서브쿼리라고도 한다.

SELECT 
    직원이름,
    (SELECT AVG(급여) FROM 직원) AS 평균급여,
    급여
FROM 직원;

2.2. FROM 절에서의 서브쿼리

FROM 절에서 사용되는 서브쿼리는 반환하는 행과 칼럼 수에 제한이 없다.

FROM 절의 서브쿼리는 가상 테이블(인라인 뷰)을 생성한다.
SQL에서 모든 테이블은 참조할 수 있는 이름이 필요하므로 별칭이 필수이다.
이 별칭을 통해 메인 쿼리에서 서브쿼리의 결과를 테이블처럼 참조할 수 있다.

그러므로 결과 테이블에 반드시 별칭을 지정해야 한다.

이러한 서브쿼리를 인라인 뷰(Inline View)라고도 한다.

SELECT d.부서명, e.직원수
FROM 부서 d
JOIN (
    SELECT 부서ID, COUNT(*) AS 직원수
    FROM 직원
    GROUP BY 부서ID
) e ON d.부서ID = e.부서ID;

2.3. JOIN 절에서의 서브쿼리

JOIN 절에서의 서브쿼리도 FROM 절과 마찬가지로 반환하는 행과 칼럼 수에 제한이 없으며, 결과 테이블에 반드시 별칭을 지정해야 한다.

SELECT e.이름, d.부서명
FROM 직원 e
JOIN (
    SELECT 부서ID, 부서명
    FROM 부서
    WHERE 위치 = '서울'
) d ON e.부서ID = d.부서ID;

2.4. WHERE 절에서의 서브쿼리

WHERE 절에서의 서브쿼리는 단일값 또는 다중 행의 단일칼럼을 반환할 수 있다.

1) 단일값(스칼라)을 반환할 경우: 메인 쿼리에서는 비교 연산자(=, <, >, <=, >=, <>)를 사용한다.

  SELECT 이름, 급여
  FROM 직원
  WHERE 급여 > (SELECT AVG(급여) FROM 직원);

2) 다중 행의 단일칼럼을 반환할 경우: 메인 쿼리에서는 IN, ANY, ALL, EXISTS 연산자를 사용한다.

  SELECT 이름, 부서ID
  FROM 직원
  WHERE 부서ID IN (SELECT 부서ID FROM 부서 WHERE 위치 = '서울');

2.5. HAVING 절에서의 서브쿼리

HAVING 절에서의 서브쿼리는 그룹화 필터링을 수행하기 위한 것으로, 단일값 또는 다중 행의 단일칼럼을 반환할 수 있다.

SELECT 부서ID, AVG(급여) AS 평균급여
FROM 직원
GROUP BY 부서ID
HAVING AVG(급여) > (SELECT AVG(급여) FROM 직원);

 

 

위치 별칭 필수 여부 반환 값 제약 주요 용도 특징 효율
SELECT 절 불필요 단일값(스칼라) 필수 계산된 값 표시, 집계값 표시 스칼라 서브쿼리라고 함 각 행마다 서브쿼리가 실행될 수 있어 성능 부담
FROM 절 필수 제한 없음 (다중행/다중열 가능) 임시 테이블 생성 인라인 뷰(Inline View)라고 함 한 번만 실행되므로 SELECT 절보다 효율적일 수 있음
JOIN 절 필수 제한 없음 (다중행/다중열 가능) 조인 대상 필터링 FROM 절과 유사한 특성 조인 전에 데이터를 미리 필터링하여 효율성 증가 가능
WHERE 절 (단일값) 불필요 단일값 행 필터링 비교 연산자(=, <, > 등) 사용 상관 서브쿼리일 경우 성능 저하 가능성
WHERE 절 (다중행) 불필요 다중행의 단일열 다중 조건 필터링 IN, ANY, ALL, EXISTS 연산자 사용 IN보다 EXISTS가 더 효율적인 경우가 많음
HAVING 절 불필요 주로 단일값 그룹화 결과 필터링 집계함수 결과와 비교에 주로 사용 그룹화 후 실행되므로 WHERE보다 비효율적일 수 있음

 

 

 

3. 다중 행 서브쿼리를 위한 연산자

3.1. IN 연산자

IN 연산자는 지정된 집합에 포함되는 대상을 찾는다.

쉼표로 구분된 값 목록이나 다중 행의 단일 칼럼을 반환하는 서브쿼리를 입력받는다.

-- 값 목록 사용
SELECT 이름, 부서ID
FROM 직원
WHERE 부서ID IN (1, 3, 5);

-- 서브쿼리 사용
SELECT 이름, 부서ID
FROM 직원
WHERE 부서ID IN (SELECT 부서ID FROM 부서 WHERE 위치 = '서울');

3.2. ANY 연산자

ANY 연산자는 지정된 집합의 모든 요소와 비교 연산을 수행해 하나라도 만족하는 대상을 찾는다.

다중 행의 단일 칼럼을 반환하는 서브쿼리를 입력받는다.

SELECT 이름, 급여
FROM 직원
WHERE 급여 > ANY (SELECT 급여 FROM 직원 WHERE 부서ID = 2);

위 쿼리는 부서ID가 2인 직원 중 가장 낮은 급여보다 높은 급여를 받는 모든 직원을 반환한다.

3.3. ALL 연산자

ALL 연산자는 지정된 집합의 모든 요소와 비교 연산을 수행해 모두를 만족하는 대상을 찾는다.

다중 행의 단일 칼럼을 반환하는 서브쿼리를 입력받는다.

SELECT 이름, 급여
FROM 직원
WHERE 급여 > ALL (SELECT 급여 FROM 직원 WHERE 부서ID = 2);

위 쿼리는 부서ID가 2인 모든 직원의 급여보다 높은 급여를 받는 직원을 반환한다.

3.4. EXISTS 연산자

EXISTS 연산자는 서브쿼리를 입력받아 서브쿼리가 하나 이상의 행을 반환하는 경우 TRUE, 아니면 FALSE를 반환한다.

서브쿼리가 반환하는 행의 수나 칼럼의 내용에 제한이 없다.

SELECT 이름
FROM 직원 e
WHERE EXISTS (
    SELECT 1
    FROM 주문
    WHERE 직원ID = e.직원ID
);

위 쿼리는 한 번이라도 주문을 처리한 적이 있는 직원의 이름을 반환한다.

 

연산자 동작방식 사용예시 특징
IN 집합에 포함된 값 검색 WHERE col IN (subquery) NULL 값 처리에 주의 필요
ANY 하나라도 조건 만족 시 TRUE WHERE col > ANY (subquery) > ANY는 최소값보다 큰 것과 동일
ALL 모든 값이 조건 만족 시 TRUE WHERE col > ALL (subquery) > ALL은 최대값보다 큰 것과 동일
EXISTS 서브쿼리 결과가 존재하면 TRUE WHERE EXISTS (subquery) 서브쿼리 내용보다 존재 여부만 확인

 

 

4. 상관 서브쿼리

상관 쿼리(Correlated Subquery)는 메인 쿼리의 각 행에 대해 실행되며, 서브쿼리가 메인 쿼리의 칼럼 값을 참조하는 쿼리이다.

상호 의존 관계를 가진 메인 쿼리와 서브쿼리 간 특정 조건을 만족하는 튜플을 찾는 데 사용된다.

상관 서브쿼리는 메인 쿼리의 각 행에 대해 서브쿼리가 실행되므로 성능에 주의해야 한다.

SELECT 부서ID, 이름, 급여
FROM 직원 e1
WHERE 급여 > (
    SELECT AVG(급여)
    FROM 직원 e2
    WHERE e1.부서ID = e2.부서ID
);

위 쿼리는 각 직원이 속한 부서의 평균 급여보다 높은 급여를 받는 직원을 찾는다.

서브쿼리에서 `e1.부서ID`를 참조하고 있으므로 상관 서브쿼리이다.

'학습 > DB' 카테고리의 다른 글

10. 데이터 모델링  (0) 2025.03.17
8. 그룹화, 정렬, 제한  (0) 2025.03.06
7. 조인(JOIN)  (0) 2025.03.01
6. 관계와 제약조건  (0) 2025.02.26
5. 자료형과 필터링  (1) 2025.02.23