1. 서브쿼리란 무엇인가?
서브쿼리는 쿼리 안에서 실행되는 또 다른 쿼리다.
SQL에서는 서브쿼리를 이용해 어떤 값을 먼저 계산한 뒤, 그 결과를 바깥 쿼리에서 조건이나 기준으로 사용한다.
특징
• 항상 SELECT로 시작
• 괄호 ()로 감싸서 사용
• 바깥 쿼리보다 먼저 실행되는 경우가 많음
• 결과는 바깥 쿼리의 → 조건 / 비교 대상 / 값으로 사용됨
사용하는 이유?
“조건을 바로 쓸 수 없고, 먼저 계산하거나 골라내야 할 때”
• 비교 기준이 다른 테이블에 있을 경우
• 먼저 계산해야 할 값이 있는 경우
• 특정 조건에 맞는 값이 필요로 할때
• 요약된 결과를 기준으로 판단해야 할 경우
서브쿼리는 항상 이 값을 먼저 알아야 다음 단계를 진행할 수 있다는 상황에서 등장한다.
수도코드로 이해하기
| 먼저 기준이 되는 값을 계산한다 그 값을 이용해 바깥 쿼리를 실행한다 |
서브쿼리는 사용되는 위치에 따라 역할이 바뀌기도 한다.
• SELECT 절 : 기준값을 출력에 같이 보여줄 때
• WHERE 절 : 조건 비교를 위한 값이나 목록이 필요할 때
• FROM 절 : 중간 결과를 테이블처럼 사용할 때
• EXISTS: 존재 여부를 판정할때
2. SELECT 절에서 쓰는 서브쿼리
각 행 옆에 기준이 되는 값(평균, 합계 등) 을 같이 보여주고 싶을 때 사용한다.
SELECT
컬럼1,
(SELECT 집계함수(컬럼)
FROM 테이블
WHERE 조건) AS 기준값
FROM 테이블;
결과는 항상 한 값만 나와야 한다. (하나의 행만, 때문에 집계 함수를 사용)
각 행 옆에 비교 기준, 평균, 합계 등을 붙일 때 사용된다.
예시문제: 직원의 이름과 급여, 비교를 위해 평균 급여를 출력하라.
수도코드로 보기
| 전체 직원의 평균 급여를 구한다. 각 직원의 이름, 급여를 조사한다. 조회할 때 평균 급여를 같이 보여준다. |
SELECT
name,
salary,
(SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees;
실행 순서 (Line by Line)
| 1. 서브쿼리 실행 → 평균 급여 1개 값 생성 2. employees 테이블 한 행씩 출력 3. 각 행마다 평균급여 값을 그대로 붙임 |
3. WHERE 절에서 쓰는 서브쿼리
조건에 쓸 기준값이 필요할 때, 조건을 비교해야 할 때 사용한다.
# 값이 1개일 때 (행이 1개)
WHERE 컬럼 = (SELECT ...)
# 값이 여러개일 때 (행이 여러개)
WHERE 컬럼 IN (SELECT ...)
값이 1개일때, 값이 여러개일때로 나눠진다.
이때의 값은 행(row)를 의미하며, 만약 컬럼이 여러개인 경우 WHERE절이 아닌 FROM절에 서브쿼리를 사용해야 한다.
단일값 예시문제: 급여가 평균보다 큰 사람을 출력하라
수도코드로 보기
| 전체 직원의 평균 급여를 계산한다. 그 결과를 하나의 테이블로 구성한다. 평균급여보다 급여가 많은 사람을 구한다. |
SELECT name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees
);
실행 순서 (Line by Line)
| 1. 서브쿼리 실행 → 평균 급여 1개 값 생성 2. employees 테이블 첫 번째 행 조회 3. salary > 평균 급여 비교 4. 조건이 참이면 출력, 거짓이면 제외 5. 모든 행에 대해 반복 |
여러 값 예시문제: 서울 소재 부서 소속 직원 조회 문제
수도코드로 보기
| 서울에 있는 부서의 id 목록을 구한다 그 부서에 속한 직원만 조회한다 |
SELECT *
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location = '서울'
);
실행 순서 (Line by Line)
| 1. 서브쿼리 실행 → 서울에 위치한 부서의 department_id 목록 생성 2. employees 테이블에서 행 하나씩 조회 3. department_id가 목록 안에 있는지 확인 4. 있으면 출력, 없으면 제외 |
4. FROM 절 에서 쓰는 서브쿼리
원본 테이블을 그대로 쓰지 않고, 서브쿼리를 미리 가공한 결과를 새로운 테이블처럼 사용할때 쓰는 방식이다.
즉, “값”이 아니라 “테이블”을 만드는 서브쿼리이다
SELECT 컬럼 1, 컬럼2
FROM (
SELECT 컬럼 1, 컬럼 2
FROM 테이블
GROUP BY 묶을 컬럼) AS 서브쿼리명;
예시문제: 부서별 평균 급여 기준 필터링 문제
수도코드로 보기
| 부서별 평균 급여를 계산한다 이 결과를 하나의 임시 테이블로 만든다 평균 급여가 3000 이상인 부서만 조회한다 |
SELECT department, avg_salary
FROM (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) t
WHERE avg_salary >= 3000;
실행 순서 (Line by Line)
| 1. FROM 절 안의 서브쿼리 실행→ 부서별 평균 급여 테이블 생성 2. 생성된 임시 테이블을 t로 이름 지정 3. t 테이블에 대해 WHERE 조건 적용 4. 조건을 만족하는 행 출력 |
4-1. JOIN 옆에 사용하는 서브쿼리
FROM 서브쿼리가
FROM절에서 서브쿼리를 사용해 미리 가공한 결과를 하나의 테이블처럼 사용하는 방식이라면
JOIN 서브쿼리는 FROM 절에 있는 기본 테이블에 서브쿼리를 JOIN 하는 방식을 알아볼것이다.
이 방식 역시 "값"이 아닌 "테이블"을 만드는 서브쿼리이지만, 기존 테이블과의 관계를 유지한 채로 필요한 정보를
추가적으로 사용할때 사용한다.
SELECT 컬럼들
FROM 테이블 A
JOIN (
SELECT 컬럼들
FROM 테이블 B
GROUP BY 컬럼
) 서브쿼리명
ON A.컬럼 = 서브쿼리명.컬럼;
# join은 용도에 따라 INNER, LEFT로 나눠짐
서브쿼리가 JOIN 대상 테이블 역할을 하고 ON 절을 통해 기존 테이블과 연결된다.
예시문제: 부서별 평균 급여와 직원 정보 함께 조회
수도코드로 보기
| 부서별 평균 급여를 계산한다. 이 결과를 임시 테이블로 구성한다. 직원 테이블과 부서 기준으로 JOIN한다. 직원정도 + 부서 평균 급여를 함께 조회한다. |
SELECT e.name,
e.department,
e.salary,
d.avg_salary
FROM employees e
JOIN (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) d
ON e.department = d.department;
실행 순서 (Line by Line)
| 1. FROM 절 안의 JOIN 서브쿼리 실행→ 부서별 평균 급여 테이블 생성 2. 생성된 임시 테이블을 d로 이름 지정 3. employees 테이블(e)과 d 테이블을 department 기준으로 JOIN 4. JOIN된 결과에서 SELECT 컬럼 출력 |
FROM절 서브쿼리와 JOIN서브쿼리의 차이점
공통점
• 둘 다 "값"이 아닌 "테이블을 만드는 서브쿼리문
• GROUP BY, 집계 결과를 다뤄야 할때 자주 사용됨
• 임시 테이블로써 동작함
차이점
| 구분 | FROM절 서브쿼리 | JOIN 서브쿼리 |
| 목적 | 결과 자체를 필터링 | 기존 테이블에 정보를 추가 |
| 중심 테이블 | 서브쿼리 결과 | 원본 테이블 |
| 사용 위치 | FROMW 단독 | JOIN 옆 |
| 활용 | 집계 결과를 필터링 | 집계 결과 결합 |
사용 타이밍을 자세하게
FROM절은 언제?
• 집계 결과 자체를 다시 조건으로 걸고 싶을 때
• “이 결과를 하나의 테이블로 보고 싶다”는 느낌일 때
예시
• 부서 평균 급여가 3000 이상인 부서만 조회
• 카테고리별 평균 가격 상위 그룹 추출
JOIN은 언제?
• 기존 테이블의 행을 유지하면서 집계된 정보를 옆에 붙이고 싶을 때
예시
• 직원 + 부서 평균 급여
• 주문 + 고객별 총 주문 금액
포인트!
FROM 절 서브쿼리는 결과 자체가 주인공 → “이 결과를 필터링하고 싶은가?”
JOIN 서브쿼리는 기존 테이블이 주인공 → “아니면 옆에 붙이고 싶은가?”
로 판단하기
5. EXISTS / NOT EXISTS — 존재 여부로 판단하기
EXISTS는 값을 비교하지 않고, 행이 존재하는지만 판단한다.
• 값이 필요한가? → NO
• 있냐 없냐만 중요한가? → YES
-- EXISTS
SELECT *
FROM 테이블 A
WHERE EXISTS (
SELECT 1
FROM 테이블 B
WHERE 조건
); -- “이 안쪽 쿼리가 한 줄이라도 나오면 TRUE”
-- NOT EXISTS
SELECT *
FROM 테이블 A
WHERE NOT EXISTS (
SELECT 1
FROM 테이블 B
WHERE 조건
);
예시문제: 주문 이력이 있는 고객 조회 문제
수도코드로 보기
| 이 고객에 대한 주문이 존재하는지 확인한다 주문이 하나라도 있으면 고객을 출력한다 |
SELECT *
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);
실행 순서 (Line by Line)
| customers 테이블의 각 행을 하나씩 확인 해당 customer_id로 orders에 행이 있는지 검사 하나라도 있으면 TRUE → 고객 출력 |
6. 상관 서브쿼리 — 바깥 쿼리를 참조하는 서브쿼리
지금까지 봤던 서브쿼리는 대부분 이러했다.
• 서브쿼리 혼자 실행 가능
• 한 번 실행 → 결과를 바깥 쿼리가 사용
허나, 상관 서브쿼리는 바깥 쿼리의 한 행마다 서브쿼리가 다시 실행된다.
언제 사용하나?
“각 행마다 기준이 달라질 때”
- 각 부서에서 평균보다 급여가 높은 직원
- 각 고객의 주문 평균 금액보다 큰 주문
- 각 상품의 카테고리 평균보다 비싼 상품
예시문제: 각 부서에서 자기 부서 평균 급여보다 많이 받는 직원 조회
수도코드로 보기
| 직원 한 명을 선택한다 이 직원이 속한 부서의 평균 급여를 계산한다 직원 급여가 그 평균보다 큰지 비교한다 크면 출력한다 다음 직원으로 이동한다 |
SELECT name, salary, department
FROM employees e
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE department = e.department
);
실행 순서 (Line by Line)
| 1. employees 테이블에서 첫 번째 직원 e 선택 2. 서브쿼리 실행 → 이 직원의 부서 평균 급여 계산 3. e.salary > 부서 평균 급여 비교 4. 조건이 참이면 출력 5. 다음 직원으로 이동하여 반복 |
6. CTE (WITH) — 이름 붙인 서브쿼리
CTE는 FROM 서브쿼리에 이름을 붙인 형태다.
서브쿼리를 위에서 이름 붙여서 미리 정의한다. 때문에 읽기 쉽고, 재사용도 가능하다.
• FROM 절 서브쿼리가 길어질 때
• 같은 서브쿼리를 여러 번 써야 할 때
• 실행 순서를 명확히 보여주고 싶을 때
일때 사용한다.
예시문제: 부서별 평균 급여를 구한 뒤 평균 급여가 3000 이상인 부서 조회
수도코드로 보기
| 부서별 평균 급여 테이블을 만든다 이 테이블에 이름을 붙인다 그 결과에서 평균 급여가 3000 이상인 부서를 조회한다 |
WITH dept_avg AS (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
)
SELECT department, avg_salary
FROM dept_avg
WHERE avg_salary >= 3000;
실행 순서 (Line by Line)
| 1. WITH 절 실행 → 부서별 평균 급여 테이블 생성 → dept_avg라는 이름으로 저장 2. 메인 SELECT 실행 FROM dept_avg 3. WHERE 조건 적용 4. 결과 출력 |
오늘 학습한 내용 정리
이번 챕터에서는 복잡한 SQL을 다루는 핵심 도구인 서브쿼리와 CTE를 복습했다.
- 서브쿼리는 임시 계산 결과를 만드는 도구다
- SELECT 절은 각 행 옆에 기준이 되는 값을 보여줄수 있다.
- WHERE절은 조건에 쓸 기준값이 필요할 때, 조건을 비교해야 할 때 사용한다.
- FROM절로 미리 가공한 결과를 임시테이블로 만들수 있다.
- JOIN 서브쿼리로 테이블을 붙일수 있다.
- EXISTS / NOT EXISTS 로 존재 여부를 파악할수 있다.
- 상관 서브쿼리는 행 단위 로직에 사용된다
- CTE는 가독성과 유지보수를 위한 구조다
'F.SQL > SQL 기초 복습' 카테고리의 다른 글
| [SQL] 윈도우 함수 — 분석가의 필수요소 (0) | 2026.01.12 |
|---|---|
| [SQL] JOIN 실전 — 테이블 연결하기 (0) | 2026.01.04 |
| [SQL] 테이블 구조 이해 & JOIN 준비 운동 (0) | 2026.01.04 |
| [SQL] 데이터를 숫자로 요약하기 — 집계와 그룹화 (0) | 2026.01.03 |
| [SQL] 조건을 조금 더 똑똑하게 — CASE로 데이터 분류하기 (0) | 2026.01.03 |
