개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › DB › SQL

SELECT가 처리되는 순서

WHERE에서는 왜 별칭과 집계 함수를 쓸 수 없나

SELECT 문은 적힌 순서가 아니라 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT 순서로 처리된다고 보면 됩니다. 어떤 이름을 쓸 수 있는지는 그 절이 몇 번째로 처리되는지에 달려 있습니다.

실행으로 대조 · MySQL 8.4.11에서 서른두 가지 조합의 SQL을 그대로 실행해 오류 번호와 메시지, 최종 결과의 모든 행을 대조. 표준 SQL과의 차이는 MySQL 문서를 따름

이 실습에 나오는 개념

SELECT 문의 논리적 처리 순서
SELECT 문의 각 절이 어떤 순서로 처리된다고 보고 결과를 따지는지를 정한 순서이다. FROM(JOIN 포함) → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT 순이며, 문장에 적는 순서와 다르다. 어떤 절에서 쓸 수 있는 이름은 그 절보다 먼저 처리된 단계에서 만들어진 것뿐이다. 그래서 SELECT에서 붙인 별칭은 WHERE에서 쓸 수 없고 ORDER BY에서는 쓸 수 있으며, 집계 함수는 그룹이 만들어진 뒤인 HAVING부터 쓸 수 있다. 데이터베이스가 실제로 이 순서대로 실행한다는 뜻은 아니며, 옵티마이저는 결과가 같은 범위에서 실행 순서를 바꾼다.
GROUP BY, WHERE, HAVING
GROUP BY는 지정한 컬럼의 값이 같은 행들을 묶어 그룹별 집계(합계, 개수 등)를 구한다. WHERE는 그룹으로 묶기 전에 개별 행을 걸러내고, HAVING은 묶은 뒤에 그룹을 걸러낸다. 따라서 집계 결과에 대한 조건은 HAVING에, 그 외의 조건은 WHERE에 쓴다.
컬럼 별칭(AS)
SELECT 절에서 컬럼이나 계산식에 붙이는 다른 이름이다. SUM(amount) AS total처럼 적으며 결과의 컬럼 이름이 된다. 별칭은 SELECT 단계에서 만들어지므로 그 뒤에 처리되는 ORDER BY에서는 쓸 수 있고, 먼저 처리되는 WHERE에서는 쓸 수 없다. MySQL은 GROUP BY와 HAVING에서도 별칭을 받아 주지만 이것은 표준 SQL에 없는 확장이어서 다른 데이터베이스에서는 오류가 날 수 있다. WHERE에서 같은 값으로 거르려면 별칭 대신 식을 그대로 다시 적는다.
집계 함수
여러 행의 값을 모아 값 하나를 돌려주는 함수이다. COUNT(개수), SUM(합계), AVG(평균), MAX(최댓값), MIN(최솟값)이 대표적이다. GROUP BY가 있으면 그룹마다 한 번씩 계산하고, 없으면 조건에 맞는 행 전체를 한 그룹으로 보고 계산한다. 그룹이 만들어진 뒤에야 계산할 수 있으므로 WHERE에는 쓸 수 없고 HAVING, SELECT, ORDER BY에 쓴다. COUNT(*)를 뺀 집계 함수는 NULL인 값을 건너뛰고 계산한다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. FROM orders: 가장 먼저 처리됩니다. orders 테이블의 6행을 모두 가져옵니다. 이 시점에 쓸 수 있는 이름은 테이블의 컬럼인 id, member, amount뿐입니다.
  2. WHERE total >= 300: total은 SELECT에서 SUM(amount)에 붙이는 별칭인데, SELECT는 WHERE보다 나중에 처리됩니다. 이 시점에는 total이라는 이름이 아직 없으므로 MySQL은 오류 1054(Unknown column 'total' in 'where clause')로 문장을 거부합니다.
  3. 오류가 난 문장은 한 행도 돌려주지 않습니다: 뒤의 단계는 처리되지 않습니다. MySQL은 실행을 시작하기 전에 문장을 분석하다가 이 오류를 내므로, 실제로는 테이블을 읽기도 전에 멈춥니다.

실행 결과

코드

CREATE TABLE orders (
  id     INT PRIMARY KEY,
  member VARCHAR(20) NOT NULL,
  amount INT NOT NULL
);

INSERT INTO orders (id, member, amount) VALUES
  (1, '김철수', 300),
  (2, '김철수', 150),
  (3, '이영희', 120),
  (4, '이영희', 200),
  (5, '박민수', 250),
  (6, '최지우', 80);

-- 처리되는 순서: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
SELECT member, SUM(amount) AS total
  FROM orders
 WHERE total >= 300
 GROUP BY member;

이 모형의 전제

주문 6건의 작은 예입니다. 서른두 가지 조합(조건 네 가지 × 적는 곳 두 가지 × GROUP BY의 별칭 × 정렬과 자르기) 모두, 화면의 SQL을 실제 MySQL 8.4.11(기본 sql_mode, ONLY_FULL_GROUP_BY 포함)에서 그대로 실행해 오류 번호와 메시지, 최종 결과의 모든 행, WHERE 조건만으로 센 행 수가 모형과 같은 것을 대조했습니다. ORDER BY가 없는 조합은 행이 나오는 순서가 보장되지 않으므로 순서를 빼고 행의 내용만 비교했습니다. 일곱 단계는 "이 순서로 처리한 것과 같은 결과가 나온다"는 논리적 순서입니다. 실제 MySQL은 문장을 실행하기 전에 분석하는 단계에서 이름을 찾지 못하면 오류를 내므로, 오류가 나는 문장은 앞 단계도 실행되지 않습니다. 또 옵티마이저가 실제 실행 순서를 바꾸기도 하지만 결과는 논리적 순서대로 처리한 것과 같습니다. GROUP BY와 HAVING에서 SELECT의 별칭을 쓸 수 있다는 것은 MySQL에서 실행해 확인했고, 그것이 표준 SQL에 없는 확장이라는 설명은 실행이 아니라 MySQL 8.4 참조 설명서의 "MySQL Handling of GROUP BY" 절과 "Problems with Column Aliases" 절을 따랐습니다. 다른 DB에서 어떻게 되는지는 이 PC에서 실행해 보지 않았습니다. SELECT 절 안의 DISTINCT와 윈도 함수, JOIN이 처리되는 자리는 이 실습에서 다루지 않습니다(JOIN과 GROUP BY의 결과는 "JOIN과 GROUP BY" 실습에서 다룹니다).

앞 실습: 정규화 · 다음 실습: JOIN과 GROUP BY

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요