SQL 실행 순서, SELECT는 5번째다

많은 SQL 초보자들이 SELECT로 쿼리를 시작하지만, 실제 실행 순서는 전혀 다릅니다. 이 글에서는 SELECT 쿼리가 내부적으로 어떤 순서로 처리되는지 살펴보겠습니다.

어제 윈도우 함수에 대한 설명을 준비하다가 "윈도우 함수 결과를 WHERE나 HAVING에서 필터링할 수 있을까?"라는 질문에 직면했습니다. 결론적으로 윈도우 함수는 WHERE와 GROUP BY 이후에 실행되므로 불가능하다는 것을 알게 되었습니다. 이 과정에서 SQL 쿼리의 실제 실행 순서에 대한 깊은 이해가 필요함을 느꼈습니다.

SQL 쿼리의 논리적 처리 순서

수많은 SQL 쿼리를 작성했지만, 정작 이 순서를 명확히 설명하지 못한다는 사실에 충격을 받았습니다. 다음은 표준 SQL의 논리적 처리 순서입니다:

  1. FROM/JOIN - 테이블 결합 및 ON 조건 처리
  2. WHERE - 행 단위 필터링
  3. GROUP BY - 그룹화
  4. HAVING - 그룹 조건 필터링
  5. SELECT - 컬럼 선택 및 윈도우 함수 실행
  6. ORDER BY - 정렬
  7. LIMIT - 결과 제한

이 순서는 SQL 쿼리의 의미론적 이해를 돕습니다. 다음과 같은 질문에 답할 수 있습니다:

  • GROUP BY 결과를 WHERE에서 사용할 수 있나? (아니요! WHERE가 GROUP BY보다 먼저 실행됩니다)
  • 윈도우 함수 결과로 필터링할 수 있나? (아니요! 윈도우 함수는 SELECT 단계에서 실행되며, WHERE와 GROUP BY 이후입니다)
  • GROUP BY 컬럼으로 ORDER BY 할 수 있나? (네! ORDER BY는 마지막 단계이므로 가능합니다)
  • LIMIT은 언제 적용되나? (가장 마지막에 적용됩니다)

컬럼 별칭의 함정

일부 SQL 구현에서는 GROUP BY에서 SELECT의 별칭을 참조할 수 있습니다:

SELECT CONCAT(first_name, ' ', last_name) AS full_name, COUNT(*) 
FROM users 
GROUP BY full_name

이 구문이 GROUP BY가 SELECT 이후에 실행되는 것처럼 보이지만, 실제로는 데이터베이스 엔진이 내부적으로 다음과 같이 재작성합니다:

SELECT CONCAT(first_name, ' ', last_name) AS full_name, COUNT(*) 
FROM users 
GROUP BY CONCAT(first_name, ' ', last_name)

따라서 GROUP BY는 여전히 SELECT보다 먼저 실행됩니다. 데이터베이스는 실행 계획을 수립하기 전에 전체 쿼리를 검증합니다.

실제 실행 계획과의 차이

현실의 데이터베이스 엔진은 성능 최적화를 위해 이 순서를 따르지 않을 수 있습니다. 예를 들어:

SELECT * 
FROM owners 
LEFT JOIN cats ON owners.id = cats.owner_id 
WHERE cats.name = 'mr darcy'

이 쿼리에서 LEFT JOIN 전체를 수행하는 대신, 먼저 'mr darcy'라는 고양이만 필터링하는 것이 효율적입니다. 결과가 변하지 않는다면 데이터베이스는 자유롭게 실행 순서를 변경합니다.

다른 언어에서의 유사 패턴

C#의 LINQ는 FROM → WHERE → SELECT 순서를 따릅니다:

var results = from student in studentList
              where student.Age > 12 && student.Age < 20
              select student;

Python pandas에서도 유사한 패턴이 자주 사용됩니다:

df = data.join(other_data)                    # JOIN
df = df[df.created_at > 1000]                # WHERE
df = df.groupby('category').agg({'value': 'sum'})  # GROUP BY
df = df[df.value > 10]                       # HAVING
df = df[['category', 'value']]               # SELECT
df = df.sort_values('value', ascending=False)[:30]  # ORDER BY + LIMIT

R의 dplyr도 비슷한 논리적 순서를 제공합니다.

이 순서를 이해하면 SQL 쿼리가 왜 특정 방식으로 동작하는지 직관적으로 파악할 수 있습니다. WHERE와 HAVING의 차이, 윈도우 함수의 제약 조건, GROUP BY와 SELECT의 관계 등이 명확해집니다.

태그: SQL 데이터베이스 쿼리실행순서 select FROM

7월 11일 19:04에 게시됨