📌 서브쿼리가 사용될 수 있는 위치
위치 | 사용 예시 |
---|---|
SELECT 절 |
컬럼처럼 사용 |
FROM 절 |
인라인 뷰처럼 사용 |
WHERE 절 |
조건 필터로 사용 |
HAVING 절 |
그룹 결과 필터링 |
IN , EXISTS , = , < , > 등 조건 내부 |
값 비교 |
📘 서브쿼리란?
- SELECT문 내부에 포함된 또 다른 SELECT문
- 단일 값, 다중 값, 테이블처럼 사용 가능
- 복잡한 조건을 처리하거나, 결과 비교, 동적 조건 처리에 유용
✅ 1. WHERE
절에서 서브쿼리 사용
✅ 1-1. 단일 값 반환 서브쿼리 (=
, <
, >
, !=
, <>
등)
SELECT name, salary
FROM employees
WHERE salary > (
SELECT AVG(salary) FROM employees
);
📌 설명:
salary > 평균 급여
조건을 위해, AVG 결과값을 서브쿼리로 뽑음- 서브쿼리 결과가 1개 행(스칼라)*만 나올 때 사용
⚠️ 주의:
- 결과가 2개 이상이면
"Subquery returns more than 1 row"
오류 발생 LIMIT 1
이나MAX
,MIN
으로 단일 보장
✅ 1-2. 다중 값 반환 서브쿼리 (IN
, NOT IN
)
SELECT name
FROM students
WHERE class_id IN (
SELECT id FROM classes WHERE grade = 3
);
📌 설명:
- 3학년 클래스 ID들을 뽑고,
- 그 ID에 포함되는 학생만 조회
💡 실무 팁:
IN
은 값이 많을 경우 성능 저하- NULL이 결과에 포함되면 전체 결과가 무시될 수 있음 →
NOT IN
주의
✅ 1-3. 존재 여부 확인 서브쿼리 (EXISTS
, NOT EXISTS
)
SELECT u.id, u.name
FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
📌 설명:
- 특정 조건을 "만족하는 행이 존재하는지" 확인
EXISTS
는 존재만 판단하므로 성능이 좋음
💡 실무 팁:
EXISTS
는 중복 제거 안 함 ⇒IN
보다 빠름EXISTS
내부 SELECT는 보통SELECT 1
로 최소화
✅ 2. SELECT
절에서 서브쿼리 사용
SELECT
name,
(SELECT COUNT(*)
FROM orders o
WHERE o.user_id = u.id) AS order_count
FROM users u;
📌 설명:
- 각 사용자별로 주문 수를 함께 출력
- 사용자 수 × 서브쿼리 실행 수 = 성능 이슈 주의
⚠️ 성능 팁:
- 유저 수가 많으면 N회 반복 실행됨
- JOIN + GROUP BY로 대체 가능
✅ 3. FROM
절에서 서브쿼리 사용 (인라인 뷰)
SELECT region, COUNT(*) AS user_count
FROM (
SELECT id, region FROM users WHERE status = 'active'
) AS active_users
GROUP BY region;
📌 설명:
- 내부 SELECT의 결과를 가상의 테이블처럼 사용
- 서브쿼리에 별칭 필수:
AS active_users
💡 장점:
- 복잡한 중간 결과를 명확히 분리
- 가독성 향상, 조건 분리 가능
⚠️ 성능 주의:
- 인라인 뷰는 인덱스를 잘 활용 못할 수 있음
- 너무 복잡한 인라인 뷰보다는 임시 테이블, CTE가 나을 수 있음
✅ 4. HAVING
절에서 서브쿼리 사용
SELECT department, AVG(salary) AS avg_sal
FROM employees
GROUP BY department
HAVING AVG(salary) > (
SELECT AVG(salary) FROM employees
);
📌 설명:
- 전체 평균보다 높은 부서만 필터링
HAVING
은GROUP BY
이후의 결과를 대상으로 조건 필터링
💡 실무 팁:
HAVING
서브쿼리 자주 쓰임 (전체 평균, 전체 최대값 등과 비교)
✅ 5. 상관 서브쿼리 (Correlated Subquery)
SELECT name
FROM employees e
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE department = e.department
);
📌 설명:
- 서브쿼리가 외부 쿼리의
e.department
를 참조 - 각 행마다 서브쿼리가 실행됨 →
N회 반복
💡 용도:
- 행마다 달라지는 동적 비교
- "내 부서 평균보다 많이 받는 직원" 등
⚠️ 성능 주의:
- 데이터 양이 많을 경우 JOIN으로 리팩토링 권장
✅ 6. CASE WHEN
안의 서브쿼리
SELECT
name,
CASE
WHEN (
SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id
) > 0 THEN '주문한 고객'
ELSE '미주문 고객'
END AS order_status
FROM users u;
📌 설명:
- 조건 분기에 따라 서브쿼리 결과를 조건식으로 사용
IF
,CASE
,IFNULL
안에도 서브쿼리 삽입 가능
✅ 7. 실전 예제 — 최근 주문일 포함 사용자 조회
SELECT
u.id, u.name,
(SELECT MAX(order_date)
FROM orders o
WHERE o.user_id = u.id) AS last_order_date
FROM users u
WHERE u.status = 'active';
📌 설명:
- 사용자별로 마지막 주문 날짜를 서브쿼리로 조회
💡 JOIN 대체 버전 (성능 개선):
SELECT u.id, u.name, MAX(o.order_date) AS last_order
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active'
GROUP BY u.id, u.name;
✅ 8. 서브쿼리 vs JOIN — 성능 비교
조건 | JOIN 추천 | 서브쿼리 추천 |
---|---|---|
결과 재사용 필요 | ✅ | ❌ |
값 1개만 비교 | ❌ | ✅ |
조건 분기 | ❌ | ✅ |
대용량 데이터 | ✅ | ❌ (상관 서브쿼리는 느림) |
✅ 9. 성능 요약 팁
팁 | 설명 |
---|---|
SELECT 절 서브쿼리는 반복 실행됨 |
→ JOIN + GROUP BY 로 대체 |
EXISTS 는 빠르고 가볍다 |
IN 보다 좋을 때 많음 |
FROM 절 서브쿼리는 가독성 좋지만 인덱스 비효율적일 수 있음 |
필요한 경우 뷰나 CTE 사용 |
상관 서브쿼리 는 느림 |
→ 리팩토링 대상 1순위 |
'CS > Database' 카테고리의 다른 글
[MySQL] UNION / UNION ALL (0) | 2025.04.13 |
---|---|
[MySQL] MySQL의 JOIN 완전 정복 + FULL OUTER JOIN 대체 방법 (0) | 2025.04.13 |
[MySQL] GROUP BY / HAVING 관련 SELECT 컬럼 정리 + 인라인 뷰 (inline view) 활용법 (0) | 2025.04.13 |
[MySQL] MySQL의 JOIN, 기본키(PK) (0) | 2025.04.08 |
[MySQL] MySQL의 `SELECT` 문에 나올 수 있는 요소들 (0) | 2025.04.04 |
댓글