지금까지 "JOIN"과 서브 쿼리라는두 가지 강력한 기술을 배웠다. 그러면서 아마도 이런 의문이 들었을 것이다. "어? 어떤 문제는 두 가지 방법으로 모두 풀 수 있네?" 맞다. 실제로 많은 문제는 "JOIN"으로도, 서브쿼리로도 해결할 수 있다. 그렇다면 우리는 무엇을 선택해야 할까? 성능과 가독성 측면에서 둘은 어떤 차이가 있을까?
문제 상황: "서울에 거주하는 모든 고객들의 주문 목록을 조회해라."
해결 방법 1. 서브 쿼리 사용
서브쿼리를 이용한 접근법은 우리의 사고 흐름과 매우 유사하다.
- 먼저, 서울에 사는 고객들의 "user_id" 목록을 찾는다 (users 테이블).
- 그다음, 이 "user_id" 목록에 포함된 "order_id"를 가진 주문들을 찾는다 (orders 테이블).
select *
from orders o
where o.user_id in (
select user_id
from users
where address like '서울%'
);

- 이 쿼리는 읽기가 매우 쉽다. "users 테이블에서 address가 '서울'인 고객 "user_id" 목록 안에(IN), "user_id"가 포함된 "orders"를 찾아줘"라고 말하는 것과 같다.
해결 방법 2. Join 사용
"JOIN"을 이용한 접근법은 필요한 테이블들을 일단 모두 연결한 뒤, 원하는 조건을 필터링하는 방식이다.
select
o.order_id,
o.user_id,
o.product_id,
o.order_date
from orders o
join users u on o.user_id = u.user_id
where u.address like '서울%';

- "주문(orders)과 고객(users) 테이블을 "user_id"로 연결한 다음, 그중에서 고객 주소가 "서울"인 데이터만 걸러줘"와 동일하다
- 결과는 당연히 서브쿼리를 사용했을 때와 동일하다.
성능 vs 가독성: 실무 가이드
성능 (Performance): 일반적으로, 데이터베이스는 "JOIN"이 서브쿼리보다 성능이 더 좋거나 최소한 동일한 경우가 많다.
왜 그럴까? 그 비밀은 데이터베이스의 "두뇌" 역할을 하는 쿼리 옵티마이저(Query Optimizer)에 있다.
- "JOIN" 구문은 옵티마이저에게 더 많은 정보를 제공한다. "A와 B 테이블을 특정 조건으로 연결해야 한다"는 전체 그림을 미리 보여주기 때문에, 옵티마이저는 인덱스를 어떻게 활용하고 어떤 테이블을 먼저 읽을지 등 가장 효율적인 실행 계획을 선택할 수 있는 더 넓은 선택지를 갖는다.
- 반면, 서브쿼리는 (특히 과거의 데이터베이스에서는) 단계적으로 실행되는 경우가 많았다. 서브쿼리를 먼저 실행해서 나온 결과를 메모리에 담아두고, 그다음 메인쿼리가 그 결과를 참조하는 방식으로 동작하여 비효율을 야기할 수 있었다.
하지만! 요즘 데이터베이스의 옵티마이저는 매우 똑똑해져서, 우리가 작성한 예제처럼 간단한 "IN" 서브쿼리는 내부적으로 최적의 "JOIN" 구문으로 자동 변환해서 실행하는 경우가 많다. 따라서 위 예제의 두 쿼리는 사실상 동일한 성능을 낼 확률이 높다. 참고로 이런 최적화는 항상 가능한 것은 아니기 때문에 쿼리 실행 계획 등을 확인하는 것이 좋다.
가독성 (Readability) : 가독성은 주관적인 영역이지만, 쿼리의 유지보수 측면에서 성능만큼이나 중요하다.
- 서브쿼리는 쿼리의 논리적 단계를 명확하게 구분해 주어, 복잡한 로직을 더 이해하기 쉽게 만들어주는 경우가 많다.
- "JOIN"은 쿼리에 필요한 모든 데이터 소스를 한눈에 보여주고, 여러 테이블의 컬럼을 함께 조회해야 할 때는 구조적으로 더 깔끔하다.
최종 결론: 언제 무엇을 써야 할까?
정답은 없다. 하지만 실무에서 적용할 수 있는 가이드라인은 다음과 같다.
- "JOIN"을 우선적으로 고려하라.
- 일반적인 성능 우위와 범용성을 고려할 때, 문제를 해결할 방법을 "JOIN"에서 먼저 찾아보는 것이 좋은 출발점이다.
- "JOIN"으로 표현하기 너무 복잡하거나, 서브쿼리의 가독성이 훨씬 좋다면 서브쿼리를 사용하라.
- 성능이 아주 중요한 쿼리가 아니라면, 동료가 이해하기 쉬운 코드를 작성하는 것이 장기적으로 더 가치 있을 수 있다. 특히 인라인 뷰를 사용해야만 깔끔하게 풀리는 문제는 서브쿼리가 정답이다.
- "EXISTS"를 활용하라.
- "IN" 서브쿼리의 대안으로, "EXISTS"라는 서브쿼리 연산자도 있다. "EXISTS"는 서브쿼리의 결괏값이 존재하는지 여부만 체크하기 때문에, 특정 상황에서 더 효율적으로 동작하기도 한다.
- 성능이 의심될 때는 반드시 측정하라.
- 가장 중요한 원칙이다. 추측하지 말고, "EXPLAIN"과 같은 도구를 사용해 데이터베이스가 어떻게 쿼리를 실행하는지 계획을 분석하고, 실제 실행 시간을 측정하여 더 나은 방법을 선택해야 한다.
"JOIN"과 서브쿼리는 대립하는 기술이 아니라, 데이터라는 재료를 요리하는 두 가지 필수 도구다. 각각의 장단점을 이해하고 상황에 맞게 꺼내 사용할 수 있어야 한다.
'데이터 베이스 > 데이터베이스 기본' 카테고리의 다른 글
| Ch05. CASE문 - 그룹핑 (0) | 2026.06.02 |
|---|---|
| Ch05. CASE문 - CASE문 기본 (0) | 2026.06.02 |
| Ch04. UNION - UNION 정렬 (0) | 2026.05.31 |
| Ch04. UNION - UNION ALL (0) | 2026.05.31 |
| Ch04. UNION - UNION (0) | 2026.05.31 |