MySQL에서 데이터를 조합하거나 조건에 따라 걸러낼 때 자주 사용되는 문법이 바로 서브쿼리(Subquery), JOIN, 그리고 CTE(Common Table Expression)다
각 문법은 결과적으로 같은 데이터를 만들 수 있어도 가독성, 유지보수성, 성능에서 큰 차이를 만든다
실제 실무에서는 이 셋 중 어떤 걸 썼느냐에 따라 쿼리 실행 속도가 수배 차이 나기도 한다

🔍 서브쿼리란?
서브쿼리(Subquery)는 쿼리 안에 중첩된 또 다른 쿼리를 의미한다
주로 SELECT, FROM, WHERE 절 안에 포함되며 하나의 값을 반환하거나, 하위 테이블처럼 작동한다
예시
SELECT name FROM users
WHERE id = (SELECT user_id FROM orders WHERE order_id = 123);
위 예제처럼 단일 값을 반환하거나
SELECT name FROM users
WHERE id IN (SELECT user_id FROM orders WHERE status = 'pending');
여러 행을 반환하는 서브쿼리도 있다
서브쿼리는 간결하지만, 내부적으로 쿼리마다 별도 실행계획이 수립되고, 최적화가 어려운 구조로 남는 경우가 많다
🔁 JOIN이란?
JOIN은 여러 테이블을 하나로 연결하여 데이터를 결합하는 SQL 문법이다
INNER JOIN, LEFT JOIN, RIGHT JOIN 등 여러 형태가 있으며, 관계형 데이터 모델에서 가장 기본적인 방식
예시
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id;
JOIN은 명시적으로 두 테이블의 관계 조건을 표현하기 때문에 MySQL 옵티마이저가 실행계획을 수립하기 용이하다
즉, 성능 최적화 여지가 많다
📦 CTE(Common Table Expression)란?
CTE는 WITH 키워드를 이용하여 쿼리 안에서 일시적인 이름 있는 결과 집합을 생성하는 기능이다
쿼리 가독성을 높이고 복잡한 서브쿼리를 분리해서 작성할 수 있다는 장점이 있다
예시
WITH recent_orders AS (
SELECT * FROM orders WHERE order_date >= CURDATE() - INTERVAL 7 DAY
)
SELECT u.name, r.amount
FROM users u
JOIN recent_orders r ON u.id = r.user_id;
CTE는 마치 뷰(View)를 일시적으로 정의한 것처럼 동작하며, 재귀 쿼리에도 사용 가능하다
다만 MySQL에서는 CTE가 서브쿼리보다 반드시 빠른 건 아니며, 성능은 작성 방식에 따라 달라진다
🧠 세 문법의 차이점 요약
| 항목 | 서브쿼리 | JOIN | CTE |
| 기본 구조 | 쿼리 안에 중첩된 또 다른 SELECT | 테이블 간 조건을 이용한 조인 | WITH 키워드로 선언한 결과 집합 |
| 가독성 | 낮음 (중첩될수록 복잡) | 중간 수준 | 높음 (블록별 쿼리 분리 가능) |
| 성능 최적화 | 어려움 (옵티마이저 최적화 한계 존재) | 유리함 (실행계획 제어 용이) | 경우에 따라 느릴 수 있음 |
| 사용 용도 | 단순 조건, 일부 필터링 로직 | 관계형 데이터 병합 | 복잡한 쿼리 분리, 재귀 쿼리 |
| 재사용 가능성 | 없음 | 없음 | 높음 (WITH로 여러 번 사용 가능) |
🧪 실무 팁: 언제 무엇을 쓰면 좋을까?
- 단일 값 조회: 서브쿼리 사용해도 무방
- 관계형 테이블 병합: 무조건 JOIN
- 조건이 복잡하거나 반복 계산되는 쿼리: CTE로 리팩토링 권장
- 성능 민감한 쿼리: EXPLAIN으로 실행계획 비교 필수
✅ 정리
- 서브쿼리, JOIN, CTE는 모두 테이블 간 관계를 표현하는 방법이지만 구조와 실행 방식이 다르다
- JOIN은 성능상 가장 최적화 여지가 크며, MySQL에서는 우선적으로 고려할 방법
- CTE는 가독성과 유지보수에서 큰 장점이 있지만, 항상 성능이 좋은 것은 아니므로 주의
- 서브쿼리는 간단한 조건에는 적합하지만, 중첩이 깊어지면 성능과 유지보수 모두 악화된다
🔗 공식 문서 참고
MySQL 8.0 Reference Manual - WITH (Common Table Expressions)
MySQL 8.0 Reference Manual - Join Syntax
MySQL 8.0 Reference Manual - Subqueries
'DB' 카테고리의 다른 글
| [MySQL] 서브쿼리 vs JOIN 실전 성능 비교 예제 (0) | 2025.07.08 |
|---|---|
| [MySQL] CTE(Common Table Expression) 개념과 성능 특성 (0) | 2025.07.08 |
| [MySQL] JOIN의 성능 원리와 최적화 전략 (0) | 2025.07.08 |
| [MySQL] 서브쿼리의 성능 특징과 사용 시 주의점 (0) | 2025.07.08 |
| [MySQL] 슬로우 쿼리 실전 튜닝 사례로 배우는 병목 원인 분석 (1) | 2025.07.07 |
| [MySQL] mysqldumpslow, pt-query-digest로 슬로우 쿼리 분석하기 🔧 (4) | 2025.07.04 |
| [MySQL] 슬로우 쿼리 로그 파일 위치와 확인 방법 🗂️ (0) | 2025.07.04 |
| [MySQL] slow_query_log 주요 설정 옵션 설명 (long_query_time 등) ⚙️ (0) | 2025.07.04 |