SQL 서브쿼리 활용 가이드: 기본 문법부터 페이징 처리까지

서브쿼리의 활용 시나리오

단일 쿼리로 해결하기 어려운 다단계 조건 필터링이나 집계 기반 검색이 필요할 때 서브쿼리(중첩 쿼리)를 사용합니다. 외부 쿼리의 조건절에 내부 쿼리의 결과값을 동적으로 주입하여 복잡한 데이터 관계를 한 번에 해결할 수 있습니다.

예제 데이터 구조

아세 세 개의 테이블을 기준으로 실습을 진행합니다.

member_idmember_namedept_idphonegenderbirth_date
M1001김도진D05010-1234-56781999-04-12
M1002이서연D04010-2345-67891998-11-03
M1003박준혁D02010-3456-78901997-08-21

학과 테이블(departments)과 성적 테이블(exam_results)이 위 회원 테이블(members)과 외래키로 연결되어 있다고 가정합니다.

기본 문법 구조

SELECT 컬럼명 FROM 메인테이블 WHERE 조건컬럼 연산자 (
    SELECT 컬럼명 FROM 서브테이블 WHERE 필터조건
);

내부 쿼리가 먼저 실행되어 결과 집합을 반환하면, 외부 쿼리가 해당 값을 기준으로 최종 데이터를 추출합니다.

실전 활용 패턴

1. 단일 값 매칭 (= 연산자)

서브쿼리가 정확히 하나의 행과 열을 반환할 때 등호 연산자를 사용합니다.

-- '김도진' 회원이 소속된 학과 정보 조회
SELECT * FROM departments WHERE dept_id = (
    SELECT dept_id FROM members WHERE member_name = '김도진'
);

2. 다중 값 처리 (IN / NOT IN)

내부 쿼리가 여러 행을 반환할 경우 IN을 사용하며, 제외 조건에는 NOT IN을 적용합니다.

-- '소프트웨어공학과'에 재학 중인 모든 회원 조회
SELECT * FROM members WHERE dept_id IN (
    SELECT dept_id FROM departments WHERE dept_name = '소프트웨어공학과'
);

-- '경영학부' 소속이 아닌 회원 목록
SELECT * FROM members WHERE dept_id NOT IN (
    SELECT dept_id FROM departments WHERE faculty = '경영학부'
);

3. 집계 함수와의 결합

평균, 최대/최소값 등을 기준으로 데이터를 필터링할 때 유용합니다.

-- 실무 점수가 전체 평균을 초과한 회원 정보
SELECT * FROM members WHERE member_id IN (
    SELECT member_id FROM exam_results WHERE practical_score > (
        SELECT AVG(practical_score) FROM exam_results
    )
);

-- 최고 득점자 정보 추출
SELECT * FROM members WHERE member_id IN (
    SELECT member_id FROM exam_results WHERE practical_score = (
        SELECT MAX(practical_score) FROM exam_results
    )
);

4. 그룹 기반 조건 필터링

그룹화된 통계 값을 서브쿼리에서 계산한 후 외부 조건과 비교합니다.

-- 평균 소속 인원보다 많은 인원을 보유한 학과 조회
SELECT * FROM departments WHERE dept_id IN (
    SELECT dept_id FROM members GROUP BY dept_id HAVING COUNT(member_id) > (
        SELECT AVG(member_count) FROM (
            SELECT COUNT(member_id) AS member_count FROM members GROUP BY dept_id
        ) AS dept_stats
    )
);

5. ANY / ALL 비교 연산

집합 내 일부 또는 전체 값과 대소 비교를 수행할 때 사용합니다.

-- 특정 회원들('M1001', 'M1002') 중 한 명보다 점수가 높은 경우
SELECT * FROM members WHERE member_id IN (
    SELECT member_id FROM exam_results WHERE practical_score > ANY (
        SELECT practical_score FROM exam_results WHERE member_id IN ('M1001', 'M1002')
    )
);

-- 특정 회원들보다 점수가 모두 높은 경우
SELECT * FROM members WHERE member_id IN (
    SELECT member_id FROM exam_results WHERE practical_score > ALL (
        SELECT practical_score FROM exam_results WHERE member_id IN ('M1001', 'M1002')
    )
);

6. 윈도우 함수와 행 번호 기반 페이징

ROW_NUMBER()를 활용하면 정렬 기준에 따른 순위를 매기거나 특정 구간 데이터를 추출할 수 있습니다. SQL Server 2012 이상에서는 OFFSET-FETCH 구문이 더 효율적입니다.

-- 회원 ID 기준 오름차순 순위 부여
SELECT ROW_NUMBER() OVER(ORDER BY member_id ASC) AS seq, * FROM members;

-- 총점(이론+실무) 기준 내림차순 정렬 후 4~8등 추출
SELECT * FROM (
    SELECT ROW_NUMBER() OVER(ORDER BY (theory_score + practical_score) DESC) AS rank_num, *
    FROM exam_results
) AS ranked_data WHERE rank_num BETWEEN 4 AND 8;

-- OFFSET-FETCH를 이용한 페이징 (3개 건너뛰고 5개 가져오기)
SELECT * FROM exam_results
ORDER BY (theory_score + practical_score) DESC
OFFSET 3 ROWS FETCH NEXT 5 ROWS ONLY;

태그: SQL T-SQL Subquery WindowFunctions OFFSET-FETCH

9월 22일 18:56에 게시됨