
MySQL에서는 동일한 목적의 쿼리를 서브쿼리(Subquery) 또는 조인(JOIN)을 이용해서 다양하게 작성할 수 있다.
NOT IN 서브쿼리 방식SELECT customerNumber, customerName
FROM customers
WHERE customerNumber NOT IN (
SELECT customerNumber
FROM orders
);
orders 테이블에 있는 고객번호만 빼고 customers 테이블에서 고객을 가져오는 구조orders 테이블에 NULL 값이 존재하면, 결과가 아예 안 나올 수도 있다.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) 고객만 추출하는 구조NOT EXISTS 방식SELECT customerNumber, customerName
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customerNumber = c.customerNumber
);
LEFT JOIN과 유사하게 동작하지만, NOT IN의 NULL 문제를 회피할 수 있다.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;
orderdetails 테이블에서 orderNumber 별 항목 개수를 먼저 구하고, 그 집계 결과를 바탕으로 최댓값, 최솟값, 평균값을 계산하는 구조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 방식의 구조는 결국 동일하게 서브쿼리를 사용하게 된다. 따라서 위와 같은 경우에는 굳이 조인을 할 필요가 없다.