
SQL 쿼리를 작성하는 순서 (SELECT, FROM, WHERE 등)와 데이터베이스가 쿼리를 실제로 처리하는 논리적인 실행 순서는 다르다. 이 순서를 이해하는 것은 오류를 피하고 효율적으로 쿼리를 작성하는 데 매우 중요하다
SQL 쿼리의 논리적 실행 순서 (7단계)
- FROM: 가장 먼저 실행된다. 데이터를 가져올 테이블을 결정한다
- WHERE: FROM 절에서 가져온 테이블의 개별 행(Row)을 조건에 따라 필터링한다. 이 단계에서는 집계 함수를 사용할 수 없다
- 핵심: SELECT 절에서 정의한 별칭은 WHERE 절에서 사용할 수 없다. WHERE가 실행될 때는 SELECT가 아직 실행되지 않아 별칭이 존재하지 않기 때문이다
- GROUP BY: WHERE 절의 필터링을 통과한 행들을 지정된 컬럼을 기준으로 그룹으로 묶는다
- HAVING: GROUP BY 절을 통해 만들어진 그룹(Group)들을 조건에 따라 필터링한다. 이 단계에서는 집계 함수를 사용할 수 있다
- SELECT: HAVING 절까지 통과한 최종 그룹들에 대해 조회할 컬럼을 선택하고 집계 함수 계산, 별칭 (AS) 부여 등이 이루어진다
- ORDER BY: SELECT 절에서 선택된 최종 결과 후보들을 지정된 순서로 정렬한다. SELECT가 ORDER BY 보다 먼저 실행되므로, ORDER BY 절에서는 SELECT 절에서 만든 별칭을 사용할 수 있다
- LIMIT: 정렬된 결과 중에서 최종적으로 사용자에게 반환할 행의 개수를 제한한다
예제로 따라가는 실행 순서
문제
- 2025년 5월 14일 이전에 들어온 주문들 중에서, 고객별로 그룹화하여, 주문 건수가 2회 이상인 고객을 찾아서, 해당 고객의 이름과 총 구매금액을 조회하고, 총 구매 금액을 기준으로 내림차순 정렬한 후, 하나의 데이터만 출력하라
SELECT
customer_name,
SUM(price * quantity) AS total_purchase -- 5단계: SELECT 실행
FROM
order_stat -- 1단계: FROM 실행
WHERE
order_date < '2025-05-14' -- 2단계: WHERE 실행
GROUP BY
customer_name -- 3단계: GROUP BY 실행
HAVING
COUNT(*) >= 2 -- 4단계: HAVING 실행
ORDER BY
total_purchase DESC -- 6단계: ORDER BY 실행
LIMIT 1; -- 7단계: LIMIT 실행
단계별 추적
FROM order_stat
- order_stat 테이블의 모든 11개 행을 가져온다

WHERE order_date < ‘2025-05-14’
- order_stat가 ‘2025-05-14’ 이전인 행들만 필터링한다. 이 단계에서 5월 15일 이후의 주문 5건이 제외되고 6개 행이 남는다
- 남은 데이터: 이순신(2건), 세종대왕(2건), 신사임당(1건), 장영실(1건)

GROUP BY customer_name
- 남아있는 6개 행을 customer_name 기준으로 그룹화한다
- 그룹: 이순신, 세종대왕, 신사임당, 장영실 – 총 4개 그룹


HAVING COUNT(*) >= 2
- 각 그룹의 행 개수 ( COUNT(*) )가 2 이상인지 검사한다
- 신사임당 (1건)과 장영실(1건) 그룹은 제외된다. 이순신 (2건), 세종대왕 (2건) 그룹만 남는다

SELECT customer_name, SUM(price * quantity) AS total_purchase
- 남은 두 그룹에 대해 SELECT 절을 실행하여 customer_name과 SUM(price * quantity) 값을 계산하고 total_purchase 별칭을 부여한다
- 이순신: 230,000, 세종대왕: 520,000

ORDER BY total_purchase DESC
- total_purchase 값을 기준으로 내림차순 정렬한다
- 결과 순서: 세종대왕, 이순신

LIMIT 1
- 정렬된 결과 중 첫 번째 행만 선택한다

출처 – 김영한 님의 강의 김영한의 실전 데이터베이스 입문 – 모든 IT인을 위한 SQL 첫걸음(SQL부터 차근차근)