서브쿼리 (Subquery)
핵심 인사이트 (3줄 요약)
- 본질: 서브쿼리 (Subquery)는 SELECT, INSERT, UPDATE, DELETE 문 안에 중첩된另一 SELECT 문으로, 메인 쿼리의 조건에 필요한 값을 동적으로 산출한다.
- 가치: 서브쿼리를 통해 복잡한 쿼리를 단계적으로 구성하고, WHERE 조건에 미리 계산된 값을 利用할 수 있어 쿼리의 Modularization과 可読性를 높인다.
- 융합: 상관 서브쿼리 (Correlated Subquery)는 외부 쿼리의 행마다 내부 쿼리가 실행되어,窓関数와类似的 분석 기능을 제공한다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
개념 정의
서브쿼리 (Subquery)는 다른 SQL 문 안에 포함된 SELECT 문으로, 메인 쿼리의 실행 전이나 후에 독립적으로 실행되어 그 결과를 메인 쿼리에 제공한다.
서브쿼리가 필요한 이유
┌─────────────────────────────────────────────────────────────────────┐
│ 서브쿼리 필요성 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [예시: 평균 나이보다 나이 많은 고객 찾기] │
│ │
│ ① 먼저 평균 나이 계산: │
│ SELECT AVG(age) FROM customers; → 결과: 35 │
│ │
│ ② 그 결과로 필터링: │
│ SELECT name FROM customers WHERE age > 35; │
│ │
│ → 두 단계를 하나로 결합: │
│ │
│ SELECT name │
│ FROM customers │
│ WHERE age > (SELECT AVG(age) FROM customers); │
│ │
└─────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 평균 나이를 수동으로 计算한 후 그 값을 쿼리에 하드코딩하면, 데이터가 변경될 때마다 수동으로 값을 갱신해야 한다. 서브쿼리를 사용하면 평균 나이가动态적으로 计算되어 항상 정확한 결과를 반환한다.
비유
서브쿼리는 음식점에서 "이 가게에서 제일 비싼 메뉴보다 더 비싼 메뉴"를 찾는 것과 같다. 먼저 가게에서 제일 비싼 메뉴의 가격을 찾고 (서브쿼리), 그 가격보다 더 비싼 메뉴를 필터링 (메인 쿼리)한다.
- **📢 섹션 요약 비유:**레시피에서 "양파를 먼저 썰어서" (서브쿼리), "양파를 넣고 볶아서" (메인 쿼리) 요리를 완성하는 것과 같습니다.
Ⅱ. 서브쿼리의 유형
1. 스칼라 서브쿼리 (Scalar Subquery)
단일 값 (1행 1열)을 반환하는 서브쿼리로, 표현식이 사용될 수 있는 곳ならどこ都可以配置한다.
-- 각 고객의 나이와 평균 나이 차이
SELECT name, age,
age - (SELECT AVG(age) FROM customers) as age_diff
FROM customers;
2. 인라인 뷰 (Inline View)
FROM 절에 사용되는 서브쿼리로, 임시 뷰를 생성한다.
-- 도시별 고객 수를 계산한 후, 2명 이상인 도시만 필터링
SELECT city, cnt
FROM (
SELECT city, COUNT(*) as cnt
FROM customers
GROUP BY city
) city_counts
WHERE cnt >= 2;
3. 중첩 서브쿼리 (Nested Subquery)
WHERE 절에 사용되는 서브쿼리로, 조건 비교에 사용된다.
-- 주문이 있는 고객 목록
SELECT name
FROM customers
WHERE customer_id IN (
SELECT customer_id
FROM orders
);
Ⅲ. 상관 vs 비상관 서브쿼리
비상관 서브쿼리 (Non-Correlated Subquery)
서브쿼리가 메인 쿼리의 값에 의존하지 않고 독립적으로 실행된다.
-- 평균 나이보다 많은 고객 (비상관)
SELECT name, age
FROM customers
WHERE age > (SELECT AVG(age) FROM customers);
상관 서브쿼리 (Correlated Subquery)
서브쿼리가 메인 쿼리의 열을 참조하여, 메인 쿼리의 각 행마다 서브쿼리가 실행된다.
-- 각 고객의 주문 수
SELECT name,
(SELECT COUNT(*)
FROM orders o
WHERE o.customer_id = c.customer_id) as order_count
FROM customers c;
┌─────────────────────────────────────────────────────────────────────┐
│ 상관 서브쿼리 동작 방식 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ customers: │
│ ┌────────┬──────┐ │
│ │ C001 │ 김철수│ │
│ │ C002 │ 이영희│ │
│ │ C003 │ 박민수│ │
│ └────────┴──────┘ │
│ │
│ orders: │
│ ┌────────┬──────┐ │
│ │ O001 │ C001 │ │
│ │ O002 │ C002 │ │
│ │ O003 │ C001 │ │
│ └────────┴──────┘ │
│ │
│ Query: │
│ SELECT c.name, │
│ (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.customer_id) │
│ FROM customers c; │
│ │
│ Result: │
│ ┌──────┬────────────┐ │
│ │ 김철수│ 2 │ ← C001는 2건 주문 │
│ │ 이영희│ 1 │ ← C002는 1건 주문 │
│ │ 박민수│ 0 │ ← C003는 주문 없음 │
│ └──────┴────────────┘ │
│ │
│ ※ 상관 서브쿼리는 외부 쿼리의 각 행마다 실행됨 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Ⅳ. EXISTS와 NOT EXISTS
EXISTS
서브쿼 결과가 한 건이라도 있으면 TRUE를 반환한다.
-- 주문이 있는 고객만
SELECT name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
NOT EXISTS
서브쿼리 결과가 없으면 TRUE를 반환한다.
-- 주문이 없는 고객만
SELECT name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
┌─────────────────────────────────────────────────────────────────────┐
│ EXISTS 동작 예시 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ [customers 중 주문이 있는 고객] → EXISTS 사용 │
│ → 서브쿼리 결과가 1건 이상이면 TRUE │
│ │
│ [customers 중 주문이 없는 고객] → NOT EXISTS 사용 │
│ → 서브쿼리 결과가 0건이면 TRUE │
│ │
│ ※ IN vs EXISTS: │
│ • IN: 서브쿼리 결과 전체를 비교 │
│ • EXISTS: 행 존재 여부만 확인 (더 효율적일 수 있음) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Ⅴ. 실무 적용 및 기술사적 판단
실무 시나리오
-
시나리오 — 연간 최다 주문 고객: 주문 금액을 年度別로 分析하여 최다 주문 고객을 찾을 때, 서브쿼리로 年度別 합계를 계산한 후 순위를 매긴다.
-
시나리오 — 전월 대비 매출 감소 확인: 서브쿼리로 전월 매출을 계산하고, 현재 월 매출과 비교한다.
도입 체크리스트
- 기술적: 서브쿼리 결과를 캐싱하여 반복 실행을 방지했는가? 상관 서브쿼리의 성능 영향을 평가했는가?
- 운영·보안적: 서브쿼리에 사용자 입력이 직접 포함되지 않도록 주의했는가 (SQL Injection 방지)?
안티패턴
-
과도한 서브쿼리 중첩: 3단계 이상의 중첩은 可読性와 성능을 해친다. JOIN이나 CTE (Common Table Expression)로 대체하는 것이 좋다.
-
상관 서브쿼리 남용: 대량 데이터에서 상관 서브쿼리는 성능 저하의 주요 원인이 된다.
-
**📢 섹션 요약 비유:**친구에게 물어보고, 그 친구의 친구에게 또 물어보고...하면 (상관 서브쿼리) 시간이 오래 걸리듯이, 서브쿼리도 깊어지면性能가 떨어집니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 단일 쿼리 | 서브쿼리 활용 | 개선 효과 |
|---|---|---|---|
| 정성 | 하드코딩된 값 | 동적 계산 | 정확성 향상 |
| 정성 | 복잡한 쿼리 | 단계적 구성 | 가독성·유지보수성 향상 |
미래 전망
-
CTE (Common Table Expression): WITH 절을利用한 서브쿼리의 가독성 향상
-
재귀 CTE: 계층 구조 (조직도, 카테고리 트리) 탐색
-
**📢 섹션 요약 비유:**요리할 때 재료를 미리 손질하면 (서브쿼리) 조리가 쉬워지는 것처럼, 서브쿼리를利用하면 복잡한 데이터 조작도 단계별로 쉽게 구성할 수 있습니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| CTE | Common Table Expression으로, 서브쿼리에 이름을 부여하여 가독성을 높인다. |
| JOIN | 서브쿼리와 비교되며, 때로는 상호 대체 가능하다. |
| 윈도우 함수 | 상관 서브쿼리와类似的 기능を提供하지만,より 효율적인 경우 많다. |
| 스칼라 값 | 단일 행, 단일 열의 결과로, 표현식 어디에나 사용할 수 있다. |
👶 어린이를 위한 3줄 비유 설명
- 서브쿼리는 **"더하기 문제를 풀기 전에 먼저 빼기 문제를 푸는 것"**과 같아요. 작은 문제를 먼저 풀고 (서브쿼리), 그 결과를 큰 문제에 써먹는 거예요.
- 상관 서브쿼리는 **"반에서 각 친구마다 그 친구 친구 수를 물어보는 것"**과 같아요. 친구마다 물어보는 게 다르니까 (상관), 더 오래 걸려요.
- 서브쿼리를 잘 쓰면 복잡한 질문도 쉽게 나눠서 대답할 수 있어요!