서브쿼리(Subquery)의 모든 것: 복잡한 데이터도 쉽게 다루는 SQL 비법
서브쿼리(Subquery)의 모든 것
: 복잡한 데이터도 쉽게 다루는 SQL 비법
SQL 초보자라면 꼭 알아야 할 핵심 스킬! 서브쿼리만 잘 써도 데이터 조회, 갱신, 삭제가 훨씬 쉬워집니다!
안녕하세요, 데이터베이스를 처음 배우시는 분들께 정말 중요한 주제를 들고 왔습니다.
바로 서브쿼리(Subquery)입니다!
처음에는 좀 헷갈릴 수도 있지만, 개념만 제대로 잡고 나면 생각보다 쉽게 활용할 수 있답니다.
특히 오늘은 단순한 이론 설명에 그치지 않고, 실무에서 바로 써먹을 수 있는 실습 예제까지 준비했어요.
서브쿼리는 단일행/다중행 서브쿼리부터 시작해, 평균보다 비싼 상품 찾기, 데이터 갱신/삭제까지 아주 유용한 기능들을 갖추고 있습니다.
이번 글에서는 실제 SQL 쿼리와 결과를 함께 보면서 여러분이 서브쿼리를 완전히 이해할 수 있도록 도와드릴게요.
그럼 지금부터 하나씩 차근차근, 함께 살펴볼까요? 😊
목차
1. 서브쿼리란? 기본 개념과 종류 정리
SQL을 공부하다 보면 이런 상황 한 번쯤은 겪게 됩니다.
“조건에 맞는 데이터를 조회하고 싶은데, 조건 자체가 또 다른 쿼리 결과여야 한다?”
이럴 때 서브쿼리(Subquery)가 등장하죠.
쉽게 말해,
쿼리 안에 또 다른 쿼리를 넣는 것입니다.
말 그대로 ‘하위 쿼리’라고도 불러요.
서브쿼리의 정의
서브쿼리는 SELECT, INSERT, UPDATE, DELETE 문의 내부에 포함되어 실행되는 SQL 쿼리입니다.
보통 괄호(()
) 안에 위치하며, 외부 쿼리(Main Query)의 조건을 보조하는 역할을 해요.
서브쿼리의 위치별 분류
- WHERE 절 서브쿼리: 특정 조건에 맞는 데이터를 필터링할 때 사용
- FROM 절 서브쿼리: 서브쿼리를 테이블처럼 사용
- SELECT 절 서브쿼리: 컬럼 값으로 서브쿼리 결과를 가져올 때 사용
서브쿼리의 종류: 단일행 vs 다중행
구분 | 설명 | 사용 예 |
---|---|---|
단일행 서브쿼리 | 결과가 한 행만 반환됨 | =, >, < 등의 연산자 사용 |
다중행 서브쿼리 | 결과가 여러 행을 반환함 | IN, ANY, ALL 연산자 사용 |
✅ 실전에서 어떤 걸 써야 할까?
단일 결과가 필요한 조건(예: 평균, 최대값, 특정 ID)이라면 단일행 서브쿼리가 적합하고,
여러 조건 중 하나라도 일치하는 경우(예: 다수의 ID 목록, 특정 그룹)라면 다중행 서브쿼리가 필요합니다.
다음 단계에서는 이 두 가지 서브쿼리를 예제 중심으로 비교해보고, 실무에서 어떻게 활용되는지를 구체적으로 살펴볼게요. 🚀
2. 단일행 vs 다중행 서브쿼리 비교하기
서브쿼리를 사용하다 보면 가장 먼저 마주치는 갈림길이 바로 이것입니다.
"이 서브쿼리는 단일행일까, 다중행일까?" 이 둘은 쓰는 문법도 다르고, 오류 발생 가능성도 달라요.
그러니까 잘 구분해서 사용해야 합니다!
📌 단일행 서브쿼리
단일행 서브쿼리는 결과가 딱 하나만 나오는 경우입니다.
주로 집계 함수(AVG
, MAX
, MIN
)를 쓴 서브쿼리에서 많이 볼 수 있어요.
💡 예제: 평균 급여보다 높은 급여를 받는 사원 찾기
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
서브쿼리 (SELECT AVG(salary) ...)
는 하나의 숫자(평균 급여)만 반환하죠?
그래서 외부 쿼리에서 salary > 평균
조건으로 비교할 수 있어요.
📌 다중행 서브쿼리
다중행 서브쿼리는 결과가 여러 행 나올 수 있는 경우예요.
이럴 땐 IN
, ANY
, ALL
같은 연산자를 사용해야 합니다.
💡 예제: 서울에 있는 고객에게만 상품을 판매한 직원 찾기
SELECT name
FROM employees
WHERE region_id IN (SELECT region_id FROM regions WHERE city = 'Seoul');
SELECT region_id ...
서브쿼리는 여러 개의 지역 ID를 반환할 수 있어요.
그럼 외부 쿼리는 IN (여러 값)
형태로 처리해야죠.
🎯 단일/다중행 서브쿼리, 이렇게 구분하세요!
항목 | 단일행 서브쿼리 | 다중행 서브쿼리 |
---|---|---|
결과 수 | 1행 (1개 값) | 여러 행 |
대표 연산자 | =, >, <, >= 등 | IN, ANY, ALL |
예외 발생 | 다중 결과시 오류 발생 | 단일 연산자 사용시 오류 |
정리하자면,
결과 행의 수를 기준으로 서브쿼리를 판단하고, 그에 맞는 연산자를 선택해야 합니다.
다음 장에서는 이 개념을 바탕으로 실제로 서브쿼리를 활용해서 복잡한 데이터 조회를 어떻게 수행하는지 보여드릴게요!
3. 복잡한 데이터 조회에 서브쿼리 활용하기
서브쿼리를 진짜 멋지게 활용할 수 있는 타이밍이 바로 복잡한 조건의 데이터 조회입니다.
단일 테이블에서 끝나는 단순한 SELECT 쿼리는 물론, 두 개 이상의 테이블을 연결하거나 특정 조건을 가진 집계 데이터를 찾을 때도 서브쿼리는 빛을 발하죠.
🎯 활용 예 1: 평균보다 높은 급여를 받는 부서의 사원 목록
SELECT name, department_id, salary
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees
);
이 쿼리는 모든 직원의 평균 급여를 먼저 계산한 후, 그보다 높은 급여를 받는 직원만 조회합니다.
복잡해 보이지만, 서브쿼리 하나로 매우 직관적인 쿼리를 완성할 수 있어요!
🎯 활용 예 2: 최근 주문한 고객 정보 조회
SELECT customer_id, name
FROM customers
WHERE customer_id IN (
SELECT customer_id
FROM orders
WHERE order_date >= CURDATE() - INTERVAL 30 DAY
);
orders
테이블에서 최근 30일 내 주문한 고객 ID만 추출한 후, 그 ID들을 바탕으로 customers
테이블에서 상세 정보를 가져오는 방식입니다.
이중 조회가 필요한 케이스에서 아주 강력한 무기예요.
🎯 활용 예 3: 가장 많이 주문한 상품 조회
SELECT product_name
FROM products
WHERE product_id = (
SELECT product_id
FROM order_items
GROUP BY product_id
ORDER BY COUNT(*) DESC
LIMIT 1
);
이 쿼리는 서브쿼리 안에서 ORDER BY + LIMIT
를 사용해 가장 많이 팔린 상품 하나를 찾고, 그 상품의 이름을 외부 쿼리로 가져오는 방식입니다.
💡 FROM 절에 쓰는 인라인 서브쿼리도 있다?
서브쿼리는 WHERE절에만 사용하는 게 아니에요.
FROM
절에 서브쿼리를 넣으면, 그것 자체가 하나의 가상 테이블처럼 작동합니다.
SELECT dept_summary.department_id, dept_summary.total_salary
FROM (
SELECT department_id, SUM(salary) AS total_salary
FROM employees
GROUP BY department_id
) AS dept_summary
WHERE total_salary > 1000000;
이런 방식은 복잡한 그룹 연산을 미리 처리하고, 그 결과를 다시 필터링하거나 정렬할 때 정말 유용해요.
📌 Tip: JOIN보다 서브쿼리가 유리한 상황
- 집계 결과로 필터링해야 할 때 (ex. 평균, 최대값 기반 조건)
- 조건에 맞는 특정 값만 필요할 때 (ex. 최신 데이터 1건만)
- 중첩 필터링이 필요한 복잡한 조건문 구성 시
이제 서브쿼리를 어떻게 응용하면 복잡한 조건에서도 깔끔하게 원하는 데이터를 뽑을 수 있는지 감이 좀 오셨죠?
다음은 바로 실습 시간입니다! 😊
AVG
를 활용해 평균보다 비싼 상품만 골라보는 예제로 넘어가 보죠!
4. 실습예제 ① : 평균보다 비싼 상품 조회
실습 예제를 통해 서브쿼리를 직접 써보면 확실히 개념이 잘 잡히죠!
이번 예제에서는 상품 테이블(products)에서 전체 평균 가격보다 비싼 상품들만 골라보겠습니다.
🎯 목표
- 전체 상품의 평균 가격을 계산한다.
- 평균보다 높은 가격의 상품들만 조회한다.
📋 사용 테이블 구조 (products)
컬럼명 | 데이터 타입 | 설명 |
---|---|---|
product_id | INT | 상품 고유 ID |
product_name | VARCHAR | 상품명 |
price | DECIMAL | 상품 가격 |
💻 실습 SQL
SELECT product_name, price
FROM products
WHERE price > (
SELECT AVG(price) FROM products
);
이 쿼리는 단일행 서브쿼리를 사용한 대표적인 예입니다.
내부 쿼리 SELECT AVG(price)
는 하나의 평균값을 반환하고, 외부 쿼리는 그 값을 기준으로 조건을 걸어줍니다.
💬 실습 팁
- 서브쿼리가 먼저 실행되고, 그 결과를 외부 쿼리가 이용해 조건 비교합니다.
- 테이블에 따라 AVG 결과가 소수점이 나오기 때문에
ROUND()
함수를 함께 사용해도 좋습니다.
이제 서브쿼리를 직접 써봤으니 감이 조금 오시죠? 😎
다음은 서브쿼리로 데이터를 갱신하거나 삭제하는 예제로 이어가 볼게요.
5. 실습예제 ② : 서브쿼리로 데이터 갱신/삭제하기
서브쿼리는 단순히 데이터를 조회하는 데만 쓰이는 게 아닙니다!
UPDATE나 DELETE에도 아주 효과적으로 활용할 수 있어요.
이번엔 데이터를 바꾸거나 지울 때 서브쿼리를 어떻게 활용하는지 실습 예제를 통해 익혀볼게요.
🎯 예제 1: 가장 적게 팔린 상품의 가격 10% 인하
UPDATE products
SET price = price * 0.9
WHERE product_id = (
SELECT product_id
FROM order_items
GROUP BY product_id
ORDER BY COUNT(*) ASC
LIMIT 1
);
위 쿼리는 order_items 테이블에서 가장 적게 팔린 상품을 찾아내고, 해당 상품의 가격을 10% 인하하는 업데이트 작업을 수행합니다.
서브쿼리를 통해 조건 대상을 정하고, 외부 쿼리에서 그 값을 기반으로 갱신하는 방식이죠.
🎯 예제 2: 주문한 적 없는 고객 삭제
DELETE FROM customers
WHERE customer_id NOT IN (
SELECT DISTINCT customer_id
FROM orders
);
이번엔 삭제입니다!
orders
테이블에 등장하지 않은 고객 ID만 찾아내서, customers
테이블에서 해당 고객들을 제거합니다.
NOT IN
과 서브쿼리 조합은 이런 "조건부 삭제"에 자주 사용됩니다.
💬 실무 꿀팁
- UPDATE/DELETE에 서브쿼리를 쓸 때는
SELECT 먼저 테스트
해보는 게 안전합니다. - 서브쿼리가 다중 결과를 반환할 가능성이 있다면
IN
,EXISTS
를 고려해 보세요. - DELETE 전에 항상
백업
을! 데이터는 소중하니까요 😥
서브쿼리를 단순 조회뿐 아니라 갱신과 삭제에도 유용하게 활용할 수 있다는 것, 이제 확실히 느끼셨을 거예요.
6. 마무리
지금까지 서브쿼리의 개념부터 다양한 활용 예제까지 쭉 따라오신 여러분, 정말 고생 많으셨어요!
처음엔 다소 낯설고 복잡하게 느껴질 수 있지만, 막상 실무에 들어가 보면 서브쿼리를 얼마나 자주 쓰는지 놀라실 거예요.
이제는 단순한 SELECT 문을 넘어서 데이터를 요리하듯 다룰 수 있는 능력이 생긴 겁니다!
📌 실무 활용 꿀팁 요약
- 서브쿼리 결과는 꼭 행 수를 예측하고 연산자를 선택해야 합니다 (= vs IN).
- SELECT문 외에도 UPDATE, DELETE에도 서브쿼리를 적극 활용해보세요.
- FROM 절에서 사용하는 인라인 뷰도 실무에서 많이 등장합니다.
- 서브쿼리로 처리 가능한 로직은 JOIN보다 더 깔끔하고 효율적일 수 있음!
끝으로 하나만 더 강조드릴게요.
서브쿼리는 조건을 동적으로 만들 수 있는 강력한 도구입니다.
SQL 초보 단계에서 서브쿼리를 자유자재로 다룰 수 있게 되면, 그 자체로 실력자라고 불릴 만한 자격이 충분해요! 🔥
오늘 배운 예제들, 꼭 실습하면서 손에 익혀보시길 추천드릴게요.