SELECT 절의 처리 순서
MySQL에서 SELECT 쿼리는 우리가 작성한 순서대로 실행되지 않고, 내부적으로는 다음과 같은 순서로 처리됩니다.
✅ SELECT 절의 처리 순서 (논리적 처리 순서, Logical Query Processing Order)
처리 순서 | 절 | 설명 |
---|---|---|
1 | FROM | 테이블/뷰를 로딩 |
2 | ON | 조인 조건을 적용 (JOIN 사용 시) |
3 | JOIN | 실제 조인 수행 |
4 | WHERE | 로우(행) 필터링 – 그룹 이전 |
5 | GROUP BY | 지정된 컬럼 기준으로 그룹핑 |
6 | WITH CUBE / ROLLUP | 집계 연산 보조 옵션 (선택 사항) |
7 | HAVING | 그룹핑 결과 필터링 |
8 | SELECT | 반환할 컬럼 및 표현식 선택 |
9 | DISTINCT | 중복 제거 |
10 | ORDER BY | 결과 정렬 |
11 | LIMIT | 결과 개수 제한 (OFFSET 포함 가능) |
1. 🔍 SELECT 절의 처리 순서와 에러의 관계
SELECT
보다WHERE
이 먼저 처리되므로,WHERE
에서는SELECT
에서 만든 별칭(AS alias
)을 사용할 수 없음
→ 반면,HAVING
에서는SELECT
이후 처리되므로 별칭 사용 가능
SELECT salary * 2 AS double_salary
FROM employees
WHERE double_salary > 10000; -- ❌ 에러
SELECT salary * 2 AS double_salary
FROM employees
HAVING double_salary > 10000; -- ✅ 가능
2. 🧠 실제 쿼리 실행 최적화 순서와는 다름
- 논리적 처리 순서 : SQL 언어의 규칙에 따라 쿼리가 해석되는 순서
- MySQL의 쿼리 최적화기(Query Optimizer)는 논리적 순서와 무관하게 최적의 실행 계획을 선택함
- 하지만 개발자(사용자)가 작성한 SQL은 항상 위의 논리적 처리 순서에 따라 해석됨 (→ 사용자 입장에서는 이 순서를 이해하는 게 중요)
ex)
실제 실행 순서 (MySQL 엔진 내부 최적화 순서 - 예시)
MySQL은 WHERE 조건에 인덱스가 있다면, 먼저 WHERE hire_date > '2020-01-01' 조건에 맞는 데이터만 인덱스를 이용해서 빠르게 조회하고, 그 다음에 GROUP BY를 실행할 수 있어요.
또한 LIMIT 10이 붙어 있으니까, 전체 정렬을 하지 않고 상위 10개만 빠르게 찾아내는 방법을 사용할 수도 있어요.
➡ 즉, MySQL은 내부적으로 실행 계획을 분석해서 "무슨 작업을 언제 먼저 하면 가장 빠를까?"를 따져서 순서를 최적화해서 실행합니다.
💡 SELECT 처리 순서 (논리적 처리 순서)는
우리가 SQL을 작성할 때 MySQL은 항상 아래와 같은 순서로 해석
개발자(사용자)가 SQL을 쓸 때 이해하고 써야 하는 순서.
🔸 실제 실행 순서 (물리적 실행 순서, 실제 동작 순서)
MySQL은 내부적으로 SQL을 실행할 때, 위 순서대로 하나하나 "무식하게" 실행하는 게 아니라,
가장 빠르게 결과를 낼 수 있는 최적의 순서로 실행 계획을 바꿔서 실행.
즉, 우리가 작성한 순서나 해석 순서랑 다른 방식으로 실행할 수도 있다.
📌 요약
항목 | 설명 |
🔤 논리적 처리 순서 | SQL 문법상 해석되는 순서 (항상 고정) |
⚙️ 실제 실행 순서 | MySQL이 내부에서 가장 효율적으로 실행하기 위해 순서를 최적화하는 것 |
📣 중요한 점 | 개발자는 논리적 처리 순서로 SQL을 정확히 써야 하고, 실제 실행 순서는 MySQL에게 맡기면 됨 |
🧪 실행 계획 보기 | EXPLAIN 키워드로 실제 실행 계획 확인 가능 |
3. 📌 WITH (CTE, Common Table Expression) 설명
- CTE는 논리적 처리 순서에는 포함되지 않음 (사전 정의 역할)
- CTE(Common Table Expression)는 SQL 쿼리의 메인 SELECT 절이 해석되기 전에 실행됩니다.
WITH
절이 있는 경우, 내부적으로는 서브쿼리처럼 미리 평가되며 FROM 이전 단계에서 처리- MySQL 8.0 이상부터 지원됨
- WITH 절은 FROM 절 내부 서브쿼리와 동일한 역할을 하지만, 가독성 향상, 재사용, 재귀 등 확장성 면에서 더 유리합니다.
서브쿼리 사용
SELECT department, COUNT(*)
FROM (
SELECT * FROM employees WHERE hire_date > '2020-01-01'
) AS recent
GROUP BY department;
CTE 사용
WITH recent_employees AS (
SELECT * FROM employees WHERE hire_date > '2020-01-01'
)
SELECT department, COUNT(*)
FROM recent_employees
GROUP BY department;
위 쿼리에서 실제로는 먼저 recent_employees라는 가상 테이블을 생성한 후,
이후 본 쿼리의 FROM 절에 사용됩니다.
즉, 실제로는 이런 구조라고 생각하면 됩니다:
-- (비공식) 내부 처리 순서:
1. WITH (CTE) 평가 → recent_employees 생성
2. FROM recent_employees
3. GROUP BY ...
4. SELECT ...
🎯 공식 처리 순서에는 안 들어가는 이유?
SELECT 절 처리 순서는 “하나의 SELECT 문 내부에서 논리적으로 해석되는 절의 순서”를 말합니다.
CTE는 **SELECT 바깥에 존재하는 ‘보조 구문’**이기 때문에 일반적으로 이 처리 순서 리스트에는 포함되지 않지만:
💡 "FROM 절 이전에 먼저 평가되는 서브쿼리"라는 점에서 SELECT 처리 순서와 밀접한 관계가 있습니다.
4. 📈 실무 팁: SELECT 처리 순서를 활용한 성능 튜닝
WHERE
는 GROUP 전에 필터링되므로 WHERE에서 최대한 레코드를 줄이는 것이 성능상 유리HAVING
을 남용하면 모든 그룹이 만들어진 뒤 필터링되므로 비효율적일 수 있음DISTINCT
는 정렬/해시 연산이 필요하므로 ORDER BY와 같이 쓰면 성능 저하될 수 있음
📌 최종 요약 도식
1. FROM
2. ON
3. JOIN
4. WHERE
5. GROUP BY
6. WITH ROLLUP / CUBE
7. HAVING
8. SELECT
9. DISTINCT
10. ORDER BY
11. LIMIT (OFFSET)
✅ 예시 (모든 절 포함)
WITH recent AS (
SELECT * FROM employees WHERE hire_date > '2020-01-01'
)
SELECT department, COUNT(*) AS cnt
FROM recent
WHERE salary > 3000
GROUP BY department
HAVING cnt > 5
ORDER BY cnt DESC
LIMIT 10;
'CS > Database' 카테고리의 다른 글
[Database] FROM DUAL 구문 (0) | 2025.04.03 |
---|---|
[Database] 데이터베이스 용어 정리 (0) | 2025.03.27 |
[DB] DB 접속 정보 (0) | 2025.03.17 |
[MySQL/Oracle] MySQL과 Oracle 데이터베이스 및 스키마 구조 비교 (0) | 2025.03.12 |
[MySQL] MySQL 데이터베이스 생성, 권한 부여 (0) | 2025.03.10 |
댓글