티스토리 뷰
SQL을 작성하다 보면 한 번쯤 이런 고민을 하게 됩니다.
"이건 JOIN으로 푸는 게 맞을까, 아니면 서브쿼리가 더 나을까?"
실무에서는 둘 다 정말 자주 쓰입니다. 그런데 많은 분들이 JOIN이 무조건 빠르다, 혹은 서브쿼리는 느리다처럼 단순하게 외우는 경우가 많습니다.
하지만 실제로는 그렇게 단순하지 않습니다.
이번 글에서는 서브쿼리 와 JOIN 의 차이, 성능 비교 포인트, 그리고 실무에서 어떤 기준으로 선택하면 좋은지 쉽게 정리해보겠습니다.
예를 들어 주문과 고객 정보를 함께 조회한다고 해보겠습니다.
JOIN 으로 쓰면 이렇게 됩니다.
SELECT o.id, o.order_date, c.name
FROM orders o
JOIN customer c ON o.customer_id = c.id;
반면 서브쿼리로 풀면 이런 식도 가능합니다.
SELECT o.id,
o.order_date,
(SELECT c.name FROM customer c WHERE c.id = o.customer_id) AS customer_name
FROM orders o;
둘 다 비슷한 결과를 만들 수 있지만, DB가 내부적으로 처리하는 방식과 최적화 포인트는 달라질 수 있습니다.
JOIN→ 테이블을 연결해서 한 번에 처리서브쿼리→ 바깥 쿼리 안에서 값을 한 번 더 구해오는 형태
대부분의 관계형 DBMS는 JOIN 을 매우 중요하게 다루기 때문에, 옵티마이저가 JOIN 실행계획을 잘 최적화하는 경우가 많습니다.
특히 다음 상황에서 JOIN이 자주 유리합니다.
- 여러 테이블을 함께 조회해야 할 때
- 조인 키에 인덱스가 잘 잡혀 있을 때
- 한 번에 집계/정렬까지 처리해야 할 때
- 중복 없이 명확한 관계를 바로 연결할 수 있을 때
즉, 관계형 구조를 자연스럽게 표현하는 기본 문법이라는 점에서 JOIN은 실무에서 많이 선택됩니다.
서브쿼리는 특정 값을 “한 번 더 계산해서 가져온다”는 느낌이라, 문제에 따라 오히려 더 직관적일 때가 있습니다.
예를 들어 평균 이상 급여를 받는 직원을 찾는 경우:
SELECT *
FROM employee
WHERE salary > (
SELECT AVG(salary)
FROM employee
);
이건 JOIN으로 억지로 푸는 것보다, 서브쿼리 한 줄이 의도를 더 명확하게 보여줍니다.
즉, 값 하나를 구해서 비교하거나, 존재 여부를 판단하거나, 단계적으로 읽히는 구조가 중요할 때는 서브쿼리가 더 읽기 쉬울 수 있습니다.
여기서 가장 중요한 오해를 먼저 정리해야 합니다.
JOIN이 무조건 더 빠르다- 서브쿼리는 무조건 느리다
이렇게 외우면 안 됩니다.
실제 성능은 아래에 따라 달라집니다.
- 서브쿼리가 상관 서브쿼리인지 여부
- 옵티마이저가 JOIN 형태로 변환 가능한지 여부
- 인덱스 상태
- 데이터 건수
- GROUP BY / ORDER BY / DISTINCT 여부
- DBMS 종류
특히 상관 서브쿼리(Correlated Subquery) 는 바깥 행마다 안쪽 쿼리가 반복 실행되는 구조라, 조건에 따라 성능이 급격히 나빠질 수 있습니다.
예를 들어 이런 형태입니다.
SELECT o.id,
(SELECT c.name FROM customer c WHERE c.id = o.customer_id) AS customer_name
FROM orders o;
이 구조는 데이터 양이 많아지면 부담이 커질 수 있어서, JOIN 으로 바꾸는 편이 더 나은 경우가 많습니다.
1) 상관 서브쿼리가 반복 실행될 때
바깥 테이블 행 수만큼 안쪽 쿼리가 반복되면 비용이 커질 수 있습니다.
2) 여러 테이블을 한 번에 묶어서 집계할 때
JOIN으로 합쳐서 GROUP BY를 처리하는 편이 더 단순한 경우가 많습니다.
3) 조인 키 인덱스가 잘 되어 있을 때
JOIN은 인덱스를 활용한 실행계획이 잘 잡히면 매우 효율적으로 동작할 수 있습니다.
4) 옵티마이저가 JOIN 형태를 더 잘 최적화할 수 있을 때
DBMS에 따라 JOIN 쪽이 더 유리한 계획으로 떨어지는 경우가 많습니다.
반대로 서브쿼리가 나쁜 게 아니라, 다음처럼 문제 자체가 서브쿼리로 읽히는 구조라면 충분히 괜찮습니다.
- 평균값과 비교
- 최대값/최소값과 비교
- EXISTS / NOT EXISTS 기반 존재 여부 판단
- 단계적으로 조건을 설명하는 쿼리
| 구분 | 서브쿼리 | JOIN |
|---|---|---|
| 장점 | 문제 단계가 잘 드러나고 읽기 쉬운 경우가 많음 | 여러 테이블 관계를 직접 풀기에 자연스러움 |
| 주의점 | 상관 서브쿼리는 반복 실행 비용 주의 | 잘못 쓰면 중복 행 증가나 조인 폭발 가능 |
| 자주 유리한 상황 | 존재 여부, 평균값 비교, 단계적 조건 | 관계 조회, 대량 데이터 연결, 집계 처리 |
| 실무 판단 | 가독성과 반복 실행 여부 체크 | 인덱스와 실행계획 확인 |
실무에서는 처음부터 무조건 JOIN으로 몰아가는 것보다, 먼저 문제를 가장 명확하게 표현하는 방식으로 쿼리를 쓴 뒤, 필요하면 실행계획 보고 튜닝하는 흐름이 더 현실적입니다.
예를 들어,
- “값 하나를 구해서 비교” → 서브쿼리
- “관계된 데이터를 함께 조회” → JOIN
- “존재 여부 확인” → EXISTS 계열 서브쿼리
이렇게 시작하면 쿼리 구조를 훨씬 자연스럽게 가져갈 수 있습니다.
실무에서는 보통 이렇게 생각하면 쉽습니다.
- 여러 테이블 관계를 직접 풀어야 하면 JOIN 우선
- 값 하나를 구해서 비교하면 서브쿼리도 자연스러움
- 상관 서브쿼리는 반복 비용을 꼭 의심
- 성능 이슈가 있으면 실행계획으로 JOIN / 서브쿼리 형태를 비교
- 최종 선택 기준은 문법 취향이 아니라 가독성 + 실행계획
실무 SQL은 문법 우열을 외우는 것보다, 이 문제가 관계 조회인지, 값 비교인지, 존재 확인인지를 먼저 구분하는 습관이 더 중요합니다.
'IT > SQL·DB' 카테고리의 다른 글
| MySQL vs PostgreSQL 차이 | 어떤 걸 선택해야 할까 쉽게 정리 (0) | 2026.03.26 |
|---|---|
| EXISTS vs IN 차이 | 성능 비교와 사용 기준 쉽게 정리 (0) | 2026.03.26 |
| 인덱스를 걸었는데도 느린 이유 7가지 | 실무에서 자주 틀리는 포인트 (0) | 2026.03.25 |
| 트랜잭션(Transaction)이란? ACID와 격리수준까지 쉽게 이해하기 (0) | 2026.03.25 |
| 인덱스란 무엇인가? 초보도 이해하는 원리 + 성능 차이 (0) | 2026.03.25 |

