SQL 서브쿼리 정리: 개념부터 실무 활용까지
SQL을 다루다 보면 '서브쿼리(Subquery)' 를 만나게 됩니다.
서브쿼리는 말 그대로 쿼리 안에 포함된 또 다른 쿼리로, 다른 쿼리의 결과를 입력으로 활용할 수 있어 강력한 기능을 제공합니다.
이번 글에서는 서브쿼리의 기본 개념, 종류, 사용 위치, 예제, 자주 쓰이는 키워드, 그리고 CTE(With 구문) 활용법까지 함께 정리해보았습니다.
1. 서브쿼리란?
서브쿼리는 SQL 안에 작성된 또 다른 쿼리입니다.
- 일반 쿼리: 단독 실행
- 서브쿼리: 다른 쿼리의 입력으로 사용 가능
예를 들어, employees 테이블에서 평균 급여 이상을 받는 직원을 조회하려고 할 때, 서브쿼리를 이렇게 사용할 수 있습니다.
SELECT *
FROM employees
WHERE salary > (
SELECT MAX(salary)
FROM employees
); -- 서브쿼리
2. 서브쿼리의 종류
2.1 단일 행 서브쿼리 (Scalar Subquery)
- 특징: 하나의 값만 반환
- 사용 시 주의: 결과가 여러 행이면 오류 발생 (single-row subquery returns more than one row)
SELECT name
FROM employees
WHERE salary = (
SELECT MAX(salary)
FROM employees
); -- 단일 행 서브쿼리
2.2 다중 행 서브쿼리 (Multi-row Subquery)
- 특징: 여러 행 반환 가능
- 사용 시 주의: 단일 값 연산자 = 사용 금지 / 반드시 IN, ANY, ALL 등 사용
SELECT name
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location_id = 1000
); -- 다중 행 서브쿼리
2.3 다중 열 서브쿼리 (Multi-column Subquery)
- 특징: 여러 컬럼 반환 가능, (col1, col2) 형태로 비교
- 사용 시 주의: 외부 쿼리와 컬럼 개수 및 순서가 반드시 일치해야 함
SELECT name
FROM employees
WHERE (department_id, job_id) IN (
SELECT department_id, job_id
FROM job_history
WHERE end_date > '2025-01-01'
); -- 다중 열 서브쿼리
2.4 상관 서브쿼리 (Correlated Subquery)
- 특징: 외부 쿼리 값을 참조, 외부 쿼리 행마다 서브쿼리 실행
- 사용 시 주의: 데이터량 많으면 성능 저하 가능, 가능하면 JOIN이나 CTE로 대체
SELECT e1.name, e1.salary
FROM employees e1
WHERE e1.salary > (
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
); -- 상관 서브쿼리
💡팁:
- 단일 행 서브쿼리 → 집계 함수와 함께 사용
- 다중 행 서브쿼리 → IN, ANY, ALL 활용
- 다중 열 서브쿼리 → 비교 대상 컬럼 개수 주의
- 상관 서브쿼리 → 성능 문제 발생 가능, 복잡하면 CTE + JOIN 추천
3. 서브쿼리 사용 위치
3.1 SELECT 절
- 특징: 컬럼 계산, 동적 값 조회에 사용
- 주의사항: 서브쿼리가 행을 여러 개 반환하면 오류 발생
SELECT name,
(SELECT department_name
FROM departments
WHERE departments.id = employees.department_id) AS dept_name
FROM employees; -- SELECT절 서브쿼리
3.2 FROM 절
- 특징: 서브쿼리를 임시 테이블로 활용, 집계 등 처리 가능
- 주의사항: 반드시 별칭(alias) 필요
SELECT dept_id, AVG(salary) AS avg_salary
FROM (
SELECT department_id AS dept_id, salary
FROM employees
) sub -- FROM절 서브쿼리
GROUP BY dept_id;
3.3 WHERE 절
- 특징: 조건 필터링에 가장 많이 사용
- 주의사항: 단일 행 서브쿼리는 = 사용, 다중 행 서브쿼리는 IN, ANY, ALL 사용
SELECT name
FROM employees
WHERE department_id = (
SELECT id
FROM departments
WHERE name = 'IT'
); -- WHERE 절 서브쿼리
3.4 HAVING 절
- 특징: 그룹별 조건 비교 시 사용
- 주의사항: 서브쿼리 결과가 그룹별 집계와 일치해야 함
SELECT department_id, COUNT(*) AS emp_count
FROM employees
GROUP BY department_id
HAVING COUNT(*) > (
SELECT AVG(emp_count)
FROM (
SELECT department_id, COUNT(*) AS emp_count
FROM employees
GROUP BY department_id
) sub
); -- HAVING 절 서브쿼리
💡 팁:
- SELECT 절 → 컬럼 계산
- FROM 절 → 임시 테이블 생성 및 집계
- WHERE 절 → 조건 필터링
- HAVING 절 → 그룹별 조건 비교
4. '서브쿼리' 와 'JOIN'
| 구분 | 서브쿼리 | JOIN |
| 실행 구조 | 내부 쿼리 → 외부 쿼리 | 테이블 결합 후 필터링 |
| 가독성 | 조건별로 직관적 | 테이블 관계 명확 |
| 성능 | 데이터 많으면 느릴 수 있음 | 일반적으로 효율적 |
| 사용 예 | 복잡한 조건, 집계 비교 | 테이블 연결, 전체 데이터 조회 |
> 단순 조인으로 해결 가능하면 JOIN, 조건별 필터링에는 서브쿼리 사용 추천.
5. 서브쿼리에서 자주 사용되는 키워드와 연산자
| 키워드 / 연산자 | 설명 |
| IN | 서브쿼리 결과 중 하나라도 일치하면 참 |
| NOT IN | 서브쿼리 결과와 일치하지 않을 때 |
| EXISTS | 서브쿼리 결과가 존재하면 참 |
| NOT EXISTS | 서브쿼리 결과가 없으면 참 |
| ANY / SOME | 비교 연산자와 함께 사용, 하나라도 만족하면 참 |
| ALL | 비교 연산자와 함께 사용, 모두 만족해야 참 |
| = | 단일 행 결과와 비교 |
| <, >, <=, >= | 단일 값이나 ALL/ANY와 함께 비교 |
💡 사용 시 주의사항 & 팁:
- =: 단일 행 서브쿼리에서만 사용 가능, 결과가 여러 행이면 오류 발생
- IN / NOT IN: 다중 행 반환 시 사용
- ANY / ALL: 비교 연산자와 함께 사용해야 함
- EXISTS / NOT EXISTS: 존재 여부만 확인, 실제 값은 필요 없으면 SELECT 1 사용
- 성능 팁: EXISTS는 다수 행 처리에서 IN보다 빠를 때 있음
6. WITH (CTE, Common Table Expression)
WITH는 서브쿼리를 미리 정의하여 재사용할 수 있게 만드는 구문입니다.
즉, 가독성과 재사용성을 높인 서브쿼리라고 생각하면 됩니다.
예제: 부서별 평균 급여 이상인 직원 조회
WITH dept_avg AS (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
)
SELECT e.name, e.salary, d.avg_salary
FROM employees e
JOIN dept_avg d
ON e.department_id = d.department_id
WHERE e.salary > d.avg_salary; -- CTE 활용
💡 팁:
- 복잡한 서브쿼리를 반복해서 쓰는 대신, WITH로 한 번 정의 후 메인 쿼리에서 사용 가능
- 읽기 쉽고 유지보수 편리
- 상관 서브쿼리 대신 CTE + JOIN으로 성능 향상 가능
7. 실무 활용 팁
- 가독성: 서브쿼리와 CTE에 별칭을 사용하고, 들여쓰기 규칙을 준수합니다.
- 성능 최적화: 상관 서브쿼리 남발을 피하고, 필요 시 JOIN이나 CTE로 대체합니다.
- 주의 사항: 다중 행 반환 시 단일 행 연산자 = 사용 금지. 인덱스를 활용해 검색 성능을 개선합니다.
- CTE 활용: 반복되는 계산이나 집계 결과를 재사용하면 가독성과 유지보수성이 높아지고, 쿼리를 단계별로 나누어 성능 최적화도 가능합니다.
8. 결론
서브쿼리는 SQL에서 조건별 필터링과 집계 비교를 직관적으로 표현할 수 있는 강력한 도구입니다.
단일 행, 다중 행, 다중 열, 상관 서브쿼리 등 종류와 사용 위치(SELECT, FROM, WHERE, HAVING)에 따라 적절히 활용해야 하며, 성능과 쿼리 구조에도 차이가 있습니다.
성능 측면에서는 상관 서브쿼리를 남발하거나 단일 행 연산자를 잘못 사용하는 것을 피하는 것이 중요합니다. 가능하다면 JOIN이나 CTE를 활용해 효율을 높이는 것이 좋습니다. 특히 CTE를 사용하면 복잡한 서브쿼리를 단계별로 나누어 재사용할 수 있어, 가독성과 유지보수성을 동시에 개선할 수 있습니다.
결론적으로, 서브쿼리를 올바르게 이해하고 상황에 맞게 JOIN, CTE와 적절히 조합해 활용하는 것이 실무에서 가장 효과적인 접근법입니다.
'데이터 분석 > SQL' 카테고리의 다른 글
| SQL 정규표현식 쉽게 이해하기 (0) | 2025.09.23 |
|---|