티스토리 뷰

300x250

SQL을 작성하다 보면 한 번쯤 이런 고민을 하게 됩니다.

"이건 JOIN으로 푸는 게 맞을까, 아니면 서브쿼리가 더 나을까?"

실무에서는 둘 다 정말 자주 쓰입니다. 그런데 많은 분들이 JOIN이 무조건 빠르다, 혹은 서브쿼리는 느리다처럼 단순하게 외우는 경우가 많습니다.

하지만 실제로는 그렇게 단순하지 않습니다.

먼저 핵심부터
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 → 테이블을 연결해서 한 번에 처리
  • 서브쿼리 → 바깥 쿼리 안에서 값을 한 번 더 구해오는 형태
쉽게 이해하면
JOIN 은 여러 테이블을 묶어서 같이 보는 느낌이고, 서브쿼리 는 필요한 값을 안쪽 쿼리에서 한 번 더 꺼내오는 느낌입니다.
728x90
JOIN이 자주 유리한 이유
관계형 데이터베이스는 JOIN 최적화에 강한 경우가 많습니다.

대부분의 관계형 DBMS는 JOIN 을 매우 중요하게 다루기 때문에, 옵티마이저가 JOIN 실행계획을 잘 최적화하는 경우가 많습니다.

특히 다음 상황에서 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 으로 바꾸는 편이 더 나은 경우가 많습니다.

핵심 정리
성능 문제에서 자주 느린 쪽은 서브쿼리라는 문법 자체보다, 반복 실행 구조인 경우가 많습니다.
실무에서 자주 보는 성능 차이 포인트
아래 상황에서는 JOIN이 더 유리하게 보이는 경우가 많습니다.

1) 상관 서브쿼리가 반복 실행될 때

바깥 테이블 행 수만큼 안쪽 쿼리가 반복되면 비용이 커질 수 있습니다.

2) 여러 테이블을 한 번에 묶어서 집계할 때

JOIN으로 합쳐서 GROUP BY를 처리하는 편이 더 단순한 경우가 많습니다.

3) 조인 키 인덱스가 잘 되어 있을 때

JOIN은 인덱스를 활용한 실행계획이 잘 잡히면 매우 효율적으로 동작할 수 있습니다.

4) 옵티마이저가 JOIN 형태를 더 잘 최적화할 수 있을 때

DBMS에 따라 JOIN 쪽이 더 유리한 계획으로 떨어지는 경우가 많습니다.

반대로 서브쿼리가 나쁜 게 아니라, 다음처럼 문제 자체가 서브쿼리로 읽히는 구조라면 충분히 괜찮습니다.

  • 평균값과 비교
  • 최대값/최소값과 비교
  • EXISTS / NOT EXISTS 기반 존재 여부 판단
  • 단계적으로 조건을 설명하는 쿼리
구분 서브쿼리 JOIN
장점 문제 단계가 잘 드러나고 읽기 쉬운 경우가 많음 여러 테이블 관계를 직접 풀기에 자연스러움
주의점 상관 서브쿼리는 반복 실행 비용 주의 잘못 쓰면 중복 행 증가나 조인 폭발 가능
자주 유리한 상황 존재 여부, 평균값 비교, 단계적 조건 관계 조회, 대량 데이터 연결, 집계 처리
실무 판단 가독성과 반복 실행 여부 체크 인덱스와 실행계획 확인
중요한 건 성능보다 먼저 '의도'다
문제를 어떻게 표현하는지가 먼저고, 그 다음에 성능을 본다.

실무에서는 처음부터 무조건 JOIN으로 몰아가는 것보다, 먼저 문제를 가장 명확하게 표현하는 방식으로 쿼리를 쓴 뒤, 필요하면 실행계획 보고 튜닝하는 흐름이 더 현실적입니다.

예를 들어,

  • “값 하나를 구해서 비교” → 서브쿼리
  • “관계된 데이터를 함께 조회” → JOIN
  • “존재 여부 확인” → EXISTS 계열 서브쿼리

이렇게 시작하면 쿼리 구조를 훨씬 자연스럽게 가져갈 수 있습니다.

정말 중요한 포인트
JOIN이냐 서브쿼리냐보다, 실행계획상 반복 비용과 인덱스 활용이 어떠냐가 훨씬 더 중요합니다.
실무에서는 어떻게 선택하면 좋을까?
아래 기준으로 판단하면 대부분 정리됩니다.

실무에서는 보통 이렇게 생각하면 쉽습니다.

  • 여러 테이블 관계를 직접 풀어야 하면 JOIN 우선
  • 값 하나를 구해서 비교하면 서브쿼리도 자연스러움
  • 상관 서브쿼리는 반복 비용을 꼭 의심
  • 성능 이슈가 있으면 실행계획으로 JOIN / 서브쿼리 형태를 비교
  • 최종 선택 기준은 문법 취향이 아니라 가독성 + 실행계획
실무 한 줄 요약
관계 조회는 JOIN, 단계적 비교는 서브쿼리, 단 반복 실행 구조는 꼭 실행계획으로 확인하면 됩니다.
마무리 정리
`서브쿼리` 와 `JOIN` 은 서로 대체 가능한 경우도 많지만, 무조건 누가 더 빠르다고 단정할 수는 없습니다. 관계를 직접 연결하는 구조라면 JOIN이 유리한 경우가 많고, 값 비교나 존재 여부 같은 문제는 서브쿼리가 더 읽기 쉬울 수 있습니다. 결국 핵심은 실행계획과 인덱스를 함께 보는 습관입니다.
JOIN = 관계된 데이터를 직접 연결
서브쿼리 = 쿼리 안에서 값을 한 번 더 계산/조회
성능 핵심 = 상관 서브쿼리 반복 비용 + 인덱스 + 실행계획
실무 SQL은 문법 우열을 외우는 것보다, 이 문제가 관계 조회인지, 값 비교인지, 존재 확인인지를 먼저 구분하는 습관이 더 중요합니다.
728x90
댓글
반응형
최근에 올라온 글
글 보관함
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30