← 아티클 목록

SQL CTE(WITH 절) 활용 쉽게 이해하기

2028-11-20#database#CTE#SQL

서브쿼리가 두세 단계로 중첩되기 시작하면, 쿼리를 읽는 순서가 안에서 바깥으로 거꾸로 가면서 머리가 아파집니다. CTE(Common Table Expression)는 이 문제를 풉니다. WITH 절로 중간 결과에 이름을 붙여 미리 정의해 두고, 본 쿼리에서는 그 이름을 테이블처럼 부릅니다. 복잡한 한 덩어리를 위에서 아래로 읽히는 여러 단계로 쪼개주는 도구입니다.

서브쿼리와 무엇이 다른가

구분중첩 서브쿼리CTE (WITH 절)
위치쿼리 안에 박혀 있음쿼리 맨 위에 분리
읽는 순서안쪽부터 거꾸로위에서 아래로
재사용같은 서브쿼리를 반복 작성이름으로 여러 번 참조
이름없음의미 있는 이름 부여

결과 자체는 같지만, CTE는 "이 중간 결과가 무엇인지" 이름으로 설명해 주기 때문에 협업과 유지보수에서 차이가 큽니다.

기본 문법

WITH 이름 AS ( ... ) 로 정의하고, 그 아래 본 쿼리에서 이름을 그냥 씁니다.

SQL
WITH dept_avg AS (
  SELECT dept, AVG(salary) AS avg_salary
  FROM employee
  GROUP BY dept
)
SELECT e.name, e.dept, e.salary, d.avg_salary
FROM employee e
JOIN dept_avg d ON e.dept = d.dept
WHERE e.salary `>` d.avg_salary;

dept_avg라는 임시 결과(부서별 평균 급여)를 먼저 만들고, 본 쿼리에서 그걸 일반 테이블처럼 조인합니다. "부서 평균보다 많이 받는 직원"이라는 의도가 쿼리 구조에 그대로 드러납니다. 같은 걸 서브쿼리로 쓰면 SELECT 안에 평균 계산이 두 번 들어가 지저분해집니다.

단계를 여러 개로 이어 쓰기

CTE는 쉼표로 여러 개를 연결해, 앞 단계 결과를 뒤 단계가 참조할 수 있습니다.

SQL
WITH high_paid AS (
  SELECT * FROM employee WHERE salary `>` 5000
),
by_dept AS (
  SELECT dept, COUNT(*) AS cnt
  FROM high_paid
  GROUP BY dept
)
SELECT * FROM by_dept WHERE cnt `>=` 3;

"고액 연봉자를 추리고 → 부서별로 세고 → 3명 이상인 부서만" 이라는 3단계 사고 흐름이 그대로 코드 순서가 됩니다. 한 덩어리 중첩 쿼리보다 디버깅도 쉽습니다. 중간 CTE 하나만 SELECT 해보면 그 단계 결과를 바로 확인할 수 있으니까요.

요점 정리

  • CTE는 WITH로 중간 결과에 이름을 붙여 미리 정의하는 기능이다.
  • 중첩 서브쿼리를 위에서 아래로 읽히는 단계로 풀어 가독성을 높인다.
  • 여러 CTE를 이어 복잡한 로직을 사고 흐름 순서대로 표현할 수 있다.

서브쿼리를 CTE로 바꿔보며 같은 결과가 얼마나 읽기 쉬워지는지 직접 실행하는 실습은 데이터베이스 트랙에서 회원가입 없이 무료로 할 수 있습니다.