[MySQL] MySQL 서브쿼리 vs 조인 사용 방식 비교

SQL

목록 보기
6/8
post-thumbnail

MySQL에서는 동일한 목적의 쿼리를 서브쿼리(Subquery) 또는 조인(JOIN)을 이용해서 다양하게 작성할 수 있다.


Case 1. 주문하지 않은 고객 찾기

1. NOT IN 서브쿼리 방식

SELECT customerNumber, customerName
FROM customers
WHERE customerNumber NOT IN (
    SELECT customerNumber
    FROM orders
);
  • orders 테이블에 있는 고객번호만 빼고 customers 테이블에서 고객을 가져오는 구조
  • 비교적 간단하고 직관적인 문법
  • ⚠️ orders 테이블에 NULL 값이 존재하면, 결과가 아예 안 나올 수도 있다.
  • ⚠️ 서브쿼리의 크기가 커지면 성능이 저하될 가능성이 있다.

2. LEFT JOIN + IS NULL 방식

SELECT c.customerNumber, c.customerName
FROM customers c
LEFT JOIN orders o ON c.customerNumber = o.customerNumber
WHERE o.customerNumber IS NULL;
  • customer 테이블을 기준으로 order 테이블과 LEFT JOIN을 수행한 후, 주문정보가 없는(NULL) 고객만 추출하는 구조
  • NULL 처리 안전
  • 대부분의 DBMS에서 성능 최적화가 잘 되어 있어서 실무에서 자주 사용된다.

3. NOT EXISTS 방식

SELECT customerNumber, customerName
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 
    FROM orders o 
    WHERE o.customerNumber = c.customerNumber
);
  • LEFT JOIN과 유사하게 동작하지만, NOT INNULL 문제를 회피할 수 있다.
  • 실제 실행 시에 매우 효율적으로 처리되는 경우가 많아 대규모 데이터 환경에서도 우수한 성능을 보인다.
  • 실무에서 가장 많이 추천되는 방식

Case 2. 주문 상세 수 통계 (MAX, MIN, AVG)

1. 서브쿼리 방식

SELECT 
	MAX(od.orderCount) AS max
    , MIN(od.orderCount) AS min
    , ROUND(AVG(od.orderCount), 0) AS avg
FROM (
    SELECT orderNumber, COUNT(orderNumber) AS orderCount
    FROM orderdetails
    GROUP BY orderNumber
) od;
  • 인라인 뷰(Inline View) : FROM 문에 나타나며 하나의 테이블처럼 사용함, Alias 지정 필요
  • orderdetails 테이블에서 orderNumber 별 항목 개수를 먼저 구하고, 그 집계 결과를 바탕으로 최댓값, 최솟값, 평균값을 계산하는 구조
  • 서브쿼리를 통해 중간 결과를 만들고 그 위에서 다시 집계를 하는, 가장 전통적이고 직관적인 방법

2. JOIN 방식(WITH 또는 서브쿼리 없이 조인만 활용한 형태)

❌ 중첩 집계 함수 사용 시도 (안 되는 예시)

SELECT MAX(COUNT(od.orderLineNumber)) AS max_count
FROM orders o
JOIN orderdetails od ON o.orderNumber = od.orderNumber;

➡️ 예상 오류 메시지: Error Code: 1111. Invalid use of group function
❗ MySQL에서 MAX(COUNT(...))처럼 집계함수를 중첩해서 바로 쓰는 건 불가능하다.

COUNT()GROUP BY 결과마다 작동하는 집계 함수고,
MAX()AVG()는 그 집계들의 전체적인 통계를 내는 함수이기 때문
중간에 집계한 결과를 먼저 만들어야 하므로, 서브쿼리나 CTE는 필수

해결 방안: 서브쿼리(또는 CTE)를 써서 중간 집계를 먼저 수행해야 한다. (이번 예시에서는 서브쿼리만 사용)
JOIN과 서브쿼리 사용

SELECT 
	MAX(od.count_per_order) AS max
	, MIN(od.count_per_order) AS min
    , ROUND(AVG(od.count_per_order), 0) AS avg
FROM (
    SELECT 
    	o.orderNumber
        , COUNT(od.orderLineNumber) AS count_per_order
    FROM orders o
    JOIN orderdetails od ON o.orderNumber = od.orderNumber
    GROUP BY o.orderNumber
) od;

하지만, 1의 서브쿼리 방식2의 JOIN 방식의 구조는 결국 동일하게 서브쿼리를 사용하게 된다. 따라서 위와 같은 경우에는 굳이 조인을 할 필요가 없다.


0개의 댓글