Skip to content
난이도 면접 빈출문제은행 5

SELECT 작성과 처리 순서 ​

SELECT 문은 적힌 순서(SELECT → FROM → WHERE …)와 의미상 처리되는 순서가 다르다. 이 순서를 알면 "WHERE에서 별칭(alias)을 왜 못 쓰지?", "GROUP BY 뒤에 왜 HAVING이 따로 있지?" 같은 의문이 한 번에 풀린다. 이 문서는 그 순서와 함께, 결과를 조용히 틀리게 하거나 인덱스를 못 쓰게 만드는 비교 조건을 다룬다.

논리적 처리 순서 ​

논리적 처리 순서(logical processing order)는 "각 절이 어떤 결과를 입력으로 받는가"를 정한 약속이다. 실제 실행은 옵티마이저(optimizer)가 인덱스 순서를 이용하거나 LIMIT에서 일찍 멈추는 식으로 바꿀 수 있지만, 결과는 항상 이 순서대로 처리한 것과 같아야 한다.

다이어그램 로딩…

이 순서에서 바로 나오는 규칙이 몇 가지 있다.

  • WHERE에서는 SELECT 별칭을 쓸 수 없다. WHERE를 처리할 때는 SELECT 목록의 값이 아직 계산되지 않았기 때문이다. MySQL은 GROUP BY·HAVING·ORDER BY에서는 별칭을 허용한다. 이때 별칭을 'a'처럼 작은따옴표로 감싸면 안 된다. 칼럼이 아니라 문자열 상수로 읽혀 엉뚱하게 묶이기 때문이다. 따옴표가 필요하면 백틱(backtick, `)을 쓴다.
  • 집계 결과로 거르려면 HAVING을 쓴다. COUNT(*) > 10 같은 조건은 묶은 뒤에야 값이 생기므로 WHERE에 둘 수 없다. 반대로 묶기 전에 거를 수 있는 조건을 HAVING에 두면, 버릴 행까지 다 묶은 뒤에 버리게 되어 일만 늘어난다.
  • 윈도우 함수(window function)는 WHERE에 쓸 수 없다. 윈도우 함수는 WHERE·GROUP BY·HAVING이 끝난 뒤 계산되고, SELECT 목록과 ORDER BY에만 올 수 있다. 순위로 거르려면 한 번 감싼 바깥 쿼리에서 거른다(서브쿼리·CTE·윈도우 함수 3).

비교 조건의 함정 ​

1. 타입이 다른 비교 — 문자열 칼럼과 숫자. MySQL은 타입이 다른 두 값을 비교할 때 한쪽을 바꾸는 타입 변환(type conversion)을 한다. 문자열과 숫자를 비교하면 둘 다 부동 소수점(floating point) 수로 바꿔 비교한다. 그래서 문자열 칼럼 code에 인덱스가 있어도 WHERE code = 1은 그 인덱스로 값을 바로 찾지 못한다. '1', ' 1', '1a'처럼 숫자 1로 바뀌는 문자열이 여러 가지라 인덱스의 한 지점을 정할 수 없기 때문이다. 결과도 틀어질 수 있다. 숫자로 바꿀 수 없는 문자열은 0이 되므로, WHERE code = 0은 'apple' 같은 행을 전부 돌려준다. 상수는 칼럼 타입에 맞춰 WHERE code = '1'로 쓴다.

2. NULL 비교 — = NULL은 참이 아니다. NULL은 "모르는 값"이라 NULL과의 비교 결과는 참도 거짓도 아닌 NULL이다. WHERE는 참인 행만 남기므로 WHERE col = NULL은 한 행도 돌려주지 않는다. NULL인지 확인할 때는 IS NULL·IS NOT NULL을 쓴다. 두 값이 모두 NULL일 때도 같다고 보려면 NULL 안전 비교 연산자(NULL-safe equal operator) <=>를 쓴다. 더 흔한 함정은 부정 조건이다. WHERE status <> 'DONE'은 status가 NULL인 행도 빼 버린다. NULL 행이 필요하면 OR status IS NULL을 붙인다.

3. 칼럼을 가공한 비교 — 인덱스가 있어도 못 쓴다.B-Tree 인덱스는 칼럼의 원래 값 순서로 정렬돼 있다. 그래서 조건에서 칼럼에 함수나 연산을 씌우면 비교할 값이 인덱스에 없는 값이 되어, 인덱스 레인지 스캔(index range scan)으로 범위를 좁히지 못하고 행마다 계산해 봐야 한다.

인덱스를 못 쓰는 형태인덱스를 쓰는 형태로 바꾸기
WHERE DATE(created_at) = '2026-09-25'WHERE created_at >= '2026-09-25' AND created_at < '2026-09-26'
WHERE price * 1.1 > 1100WHERE price > 1000
WHERE name LIKE '%kim'같은 뜻으로는 못 바꾼다. 앞부분이 고정된 LIKE 'kim%'만 범위 탐색 가능

칼럼 쪽을 그대로 두고 상수 쪽을 계산하는 것이 원칙이다. 가공한 값으로 꼭 찾아야 하면 칼럼 대신 그 식의 결과로 인덱스를 만드는 함수 키 파트(functional key part)를 쓸 수 있다(특수 인덱스). 인덱스를 어떤 순서로 타는지는 B-Tree 활용에서 다룬다.

이어지는 내용은 SELECT 작성과 처리 순서 2에서 다룬다. DISTINCT·COUNT·LIMIT을 쓸 때의 함정과, 서버가 SQL을 얼마나 엄격하게 검사할지 정하는 SQL 모드(SQL mode), 이름의 대소문자·예약어 규칙을 본다.

면접 포인트 ​

  • "SELECT 처리 순서는?" → FROM·JOIN → WHERE → GROUP BY → HAVING → SELECT(윈도우 함수 포함) → DISTINCT → ORDER BY → LIMIT. 그래서 WHERE에 별칭·집계·윈도우 함수를 못 쓴다.
  • "인덱스가 있는데 안 타요" → 칼럼 가공, 문자열 칼럼과 숫자 비교, 앞이 %인 LIKE부터 의심한다. 칼럼은 그대로 두고 상수를 바꾼다.
  • "<> 'X' 결과에 NULL 행이 없어요" → NULL 비교는 NULL이라 WHERE에서 빠진다. IS NULL을 따로 붙인다.

관련 문서