1. 서브쿼리란 무엇인가?

서브쿼리는 쿼리 안에서 실행되는 또 다른 쿼리다.

SQL에서는 서브쿼리를 이용해 어떤 값을 먼저 계산한 뒤, 그 결과를 바깥 쿼리에서 조건이나 기준으로 사용한다.

 

특징

• 항상 SELECT로 시작

• 괄호 ()로 감싸서 사용

• 바깥 쿼리보다 먼저 실행되는 경우가 많음

• 결과는 바깥 쿼리의 → 조건 / 비교 대상 / 값으로 사용됨

 

사용하는 이유?

“조건을 바로 쓸 수 없고, 먼저 계산하거나 골라내야 할 때”

• 비교 기준이 다른 테이블에 있을 경우

• 먼저 계산해야 할 값이 있는 경우

• 특정 조건에 맞는 값이 필요로 할때

• 요약된 결과를 기준으로 판단해야 할 경우

서브쿼리는 항상 이 값을 먼저 알아야 다음 단계를 진행할 수 있다는 상황에서 등장한다.

 

수도코드로 이해하기

먼저 기준이 되는 값을 계산한다
그 값을 이용해 바깥 쿼리를 실행한다 

서브쿼리는 사용되는 위치에 따라 역할이 바뀌기도 한다.

• SELECT 절 : 기준값을 출력에 같이 보여줄 때

• WHERE 절  : 조건 비교를 위한 값이나 목록이 필요할 때

• FROM 절   : 중간 결과를 테이블처럼 사용할 때

• EXISTS: 존재 여부를 판정할때


2. SELECT 절에서 쓰는 서브쿼리

각 행 옆에 기준이 되는 값(평균, 합계 등) 을 같이 보여주고 싶을 때 사용한다.

SELECT
    컬럼1,
    (SELECT 집계함수(컬럼)
     FROM 테이블
     WHERE 조건) AS 기준값
FROM 테이블;

결과는 항상 한 값만 나와야 한다. (하나의 행만, 때문에 집계 함수를 사용)

각 행 옆에 비교 기준, 평균, 합계 등을 붙일 때 사용된다.

 

예시문제: 직원의 이름과 급여, 비교를 위해 평균 급여를 출력하라.

수도코드로 보기

전체 직원의 평균 급여를 구한다.
각 직원의 이름, 급여를 조사한다.
조회할 때 평균 급여를 같이 보여준다.

 

SELECT
    name,
    salary,
    (SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees;

 

실행 순서 (Line by Line)

1. 서브쿼리 실행  → 평균 급여 1개 값 생성
2. employees 테이블 한 행씩 출력
3. 각 행마다 평균급여 값을 그대로 붙임

 


3. WHERE 절에서 쓰는 서브쿼리

조건에 쓸 기준값이 필요할 때, 조건을 비교해야 할 때 사용한다.

# 값이 1개일 때 (행이 1개)
WHERE 컬럼 = (SELECT ...)

# 값이 여러개일 때 (행이 여러개)
WHERE 컬럼 IN (SELECT ...)

값이 1개일때, 값이 여러개일때로 나눠진다.

이때의 값은 행(row)를 의미하며, 만약 컬럼이 여러개인 경우 WHERE절이 아닌 FROM절에 서브쿼리를 사용해야 한다. 

 

단일값 예시문제: 급여가 평균보다 큰 사람을 출력하라

수도코드로 보기

전체 직원의 평균 급여를 계산한다.
그 결과를 하나의 테이블로 구성한다.
평균급여보다 급여가 많은 사람을 구한다.

 

SELECT name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
);

 

실행 순서 (Line by Line)

1. 서브쿼리 실행  → 평균 급여 1개 값 생성
2. employees 테이블 첫 번째 행 조회
3. salary > 평균 급여 비교
4. 조건이 참이면 출력, 거짓이면 제외
5. 모든 행에 대해 반복

 

여러 값 예시문제: 서울 소재 부서 소속 직원 조회 문제

수도코드로 보기

서울에 있는 부서의 id 목록을 구한다
그 부서에 속한 직원만 조회한다

 

SELECT *
FROM employees
WHERE department_id IN (
    SELECT department_id
    FROM departments
    WHERE location = '서울'
);

 

실행 순서 (Line by Line)

1. 서브쿼리 실행 → 서울에 위치한 부서의 department_id 목록 생성
2. employees 테이블에서 행 하나씩 조회
3. department_id가 목록 안에 있는지 확인
4. 있으면 출력, 없으면 제외

 

4. FROM 절 에서 쓰는 서브쿼리

원본 테이블을 그대로 쓰지 않고, 서브쿼리를 미리 가공한 결과를 새로운 테이블처럼 사용할때 쓰는 방식이다.

즉, “값”이 아니라 “테이블”을 만드는 서브쿼리이다

SELECT 컬럼 1, 컬럼2 
FROM ( 
	SELECT 컬럼 1, 컬럼 2
    	FROM 테이블
        GROUP BY 묶을 컬럼) AS 서브쿼리명;

예시문제: 부서별 평균 급여 기준 필터링 문제

 

수도코드로 보기

부서별 평균 급여를 계산한다
이 결과를 하나의 임시 테이블로 만든다
평균 급여가 3000 이상인 부서만 조회한다

 

SELECT department, avg_salary
FROM (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
) t
WHERE avg_salary >= 3000;

 

실행 순서 (Line by Line)

1. FROM 절 안의 서브쿼리 실행→ 부서별 평균 급여 테이블 생성
2. 생성된 임시 테이블을 t로 이름 지정
3. t 테이블에 대해 WHERE 조건 적용
4. 조건을 만족하는 행 출력

 

4-1. JOIN 옆에 사용하는 서브쿼리

FROM 서브쿼리가

FROM절에서 서브쿼리를 사용해 미리 가공한 결과를 하나의 테이블처럼 사용하는 방식이라면

 

JOIN 서브쿼리는 FROM 절에 있는 기본 테이블에 서브쿼리를 JOIN 하는 방식을 알아볼것이다.

이 방식 역시 "값"이 아닌 "테이블"을 만드는 서브쿼리이지만, 기존 테이블과의 관계를 유지한 채로 필요한 정보를

추가적으로 사용할때 사용한다.

SELECT 컬럼들
FROM 테이블 A
JOIN (
    SELECT 컬럼들
    FROM 테이블 B
    GROUP BY 컬럼
) 서브쿼리명
ON A.컬럼 = 서브쿼리명.컬럼;

# join은 용도에 따라 INNER, LEFT로 나눠짐

서브쿼리가 JOIN 대상 테이블 역할을 하고 ON 절을 통해 기존 테이블과 연결된다.

 

예시문제: 부서별 평균 급여와 직원 정보 함께 조회

수도코드로 보기

부서별 평균 급여를 계산한다.
이 결과를 임시 테이블로 구성한다.
직원 테이블과 부서 기준으로 JOIN한다.
직원정도 + 부서 평균 급여를 함께 조회한다.

 

SELECT e.name,
       e.department,
       e.salary,
       d.avg_salary
FROM employees e
JOIN (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
) d
ON e.department = d.department;

 

실행 순서 (Line by Line)

1. FROM 절 안의 JOIN 서브쿼리 실행→ 부서별 평균 급여 테이블 생성
2. 생성된 임시 테이블을 d로 이름 지정
3.  employees 테이블(e)과 d 테이블을 department 기준으로 JOIN
4. JOIN된 결과에서 SELECT 컬럼 출력

 

FROM절 서브쿼리와 JOIN서브쿼리의 차이점

공통점

• 둘 다 "값"이 아닌 "테이블을 만드는 서브쿼리문

• GROUP BY, 집계 결과를 다뤄야 할때 자주 사용됨

• 임시 테이블로써 동작함

 

차이점

구분 FROM절 서브쿼리 JOIN 서브쿼리
목적 결과 자체를 필터링 기존 테이블에 정보를 추가
중심 테이블 서브쿼리 결과 원본 테이블
사용 위치 FROMW 단독 JOIN 옆
활용 집계 결과를 필터링 집계 결과 결합

 

사용 타이밍을 자세하게

FROM절은 언제?

• 집계 결과 자체를 다시 조건으로 걸고 싶을 때

• “이 결과를 하나의 테이블로 보고 싶다”는 느낌일 때

예시

• 부서 평균 급여가 3000 이상인 부서만 조회

• 카테고리별 평균 가격 상위 그룹 추출

 

JOIN은 언제?

• 기존 테이블의 행을 유지하면서 집계된 정보를 옆에 붙이고 싶을 때

예시

• 직원 + 부서 평균 급여

• 주문 + 고객별 총 주문 금액

 

포인트!

FROM 절 서브쿼리는 결과 자체가 주인공 → “이 결과를 필터링하고 싶은가?”

JOIN 서브쿼리는 기존 테이블이 주인공 → “아니면 옆에 붙이고 싶은가?”

로 판단하기


5. EXISTS / NOT EXISTS — 존재 여부로 판단하기

EXISTS는 값을 비교하지 않고, 행이 존재하는지만 판단한다.

  값이 필요한가? → NO

  있냐 없냐만 중요한가? → YES

 

-- EXISTS
SELECT *
FROM 테이블 A
WHERE EXISTS (
	SELECT 1
	FROM 테이블 B
	WHERE 조건
); -- “이 안쪽 쿼리가 한 줄이라도 나오면 TRUE”

-- NOT EXISTS
SELECT *
FROM 테이블 A
WHERE NOT EXISTS (
	SELECT 1
	FROM 테이블 B
	WHERE 조건
);

 

예시문제: 주문 이력이 있는 고객 조회 문제

수도코드로 보기

이 고객에 대한 주문이 존재하는지 확인한다
주문이 하나라도 있으면 고객을 출력한다

 

SELECT *
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.id
);

 

실행 순서 (Line by Line)

customers 테이블의 각 행을 하나씩 확인
해당 customer_id로 orders에 행이 있는지 검사
하나라도 있으면 TRUE → 고객 출력

 


6. 상관 서브쿼리 — 바깥 쿼리를 참조하는 서브쿼리

지금까지 봤던 서브쿼리는 대부분 이러했다.

• 서브쿼리 혼자 실행 가능

• 한 번 실행 → 결과를 바깥 쿼리가 사용

허나, 상관 서브쿼리는 바깥 쿼리의 한 행마다 서브쿼리가 다시 실행된다. 

 

언제 사용하나?

“각 행마다 기준이 달라질 때”

  • 각 부서에서 평균보다 급여가 높은 직원
  • 각 고객의 주문 평균 금액보다 큰 주문
  • 각 상품의 카테고리 평균보다 비싼 상품

예시문제: 각 부서에서 자기 부서 평균 급여보다 많이 받는 직원 조회

수도코드로 보기

직원 한 명을 선택한다
이 직원이 속한 부서의 평균 급여를 계산한다
직원 급여가 그 평균보다 큰지 비교한다
크면 출력한다
다음 직원으로 이동한다

 

SELECT name, salary, department
FROM employees e
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE department = e.department
);

 

실행 순서 (Line by Line)

1. employees 테이블에서 첫 번째 직원 e 선택
2. 서브쿼리 실행 → 이 직원의 부서 평균 급여 계산
3. e.salary > 부서 평균 급여 비교
4. 조건이 참이면 출력
5. 다음 직원으로 이동하여 반복

6. CTE (WITH) — 이름 붙인 서브쿼리

CTE는 FROM 서브쿼리에 이름을 붙인 형태다.
서브쿼리를 위에서 이름 붙여서 미리 정의한다. 때문에 읽기 쉽고, 재사용도 가능하다.

• FROM 절 서브쿼리가 길어질 때

• 같은 서브쿼리를 여러 번 써야 할 때

• 실행 순서를 명확히 보여주고 싶을 때

일때 사용한다.

예시문제: 부서별 평균 급여를 구한 뒤 평균 급여가 3000 이상인 부서 조회

수도코드로 보기

부서별 평균 급여 테이블을 만든다
이 테이블에 이름을 붙인다
그 결과에서 평균 급여가 3000 이상인 부서를 조회한다

 

WITH dept_avg AS (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
)
SELECT department, avg_salary
FROM dept_avg
WHERE avg_salary >= 3000;

 

실행 순서 (Line by Line)

1. WITH 절 실행 → 부서별 평균 급여 테이블 생성 → dept_avg라는 이름으로 저장
2. 메인 SELECT 실행 FROM dept_avg
3. WHERE 조건 적용
4. 결과 출력

 


오늘 학습한 내용 정리

이번 챕터에서는 복잡한 SQL을 다루는 핵심 도구인 서브쿼리와 CTE를 복습했다.

  • 서브쿼리는 임시 계산 결과를 만드는 도구다
  • SELECT 절은 각 행 옆에 기준이 되는 값을 보여줄수 있다.
  • WHERE절은 조건에 쓸 기준값이 필요할 때, 조건을 비교해야 할 때 사용한다.
  • FROM절로 미리 가공한 결과를 임시테이블로 만들수 있다.
  • JOIN 서브쿼리로 테이블을 붙일수 있다.
  • EXISTS / NOT EXISTS 로 존재 여부를 파악할수 있다.
  • 상관 서브쿼리는 행 단위 로직에 사용된다
  • CTE는 가독성과 유지보수를 위한 구조다
 
 

+ Recent posts