서론
SELECT 문의 WHERE, ORDER BY 등 여러 절들에서 동작이 제일 모호하게 느껴졌던 것이 GROUP BY입니다. 책에서 예제와 출력 결과를 봤을 때는 대충 이해되는 선에서 '아 그렇구나' 하고 넘어갔지만 정확한 동작에 대해 이해하지 못했습니다. 이번 포스티에서 GROUP BY의 동작 원리에 대해 알아보겠습니다.
GROP BY 의 원리에 대해 설명하고 있지는 않지만 GROUP BY 사용에 대해 애매하게 알고 있다 싶으면 MySQL 공식 문서에서 GROUP BY 관한 내용을 읽어보는 것이 좋습니다. 필자 또한 공식문서를 통해 모호했던 GROUP BY 사용에 대해 맥락을 잡았습니다.
다음 링크는 필자가 해석한 MySQL 8.5 버전 공식문 GROUP BY 번역글입니다.
https://devnona.tistory.com/148
[번역] 14.19.3 MySQL Handling of GROUP BY
SQL-92 및 이전에서는 GROUP BY 절에 이름이 지정되지 않은 nonaggregated 컬럼을 참조하는 select 리스트(SELECT ... FROM 사이에 명시하는 컬럼 리스트를 말함), HAVING 조건 또는 ORDER BY 리스트의 쿼리는 허용
devnona.tistory.com
GROUP BY 처리 방식
GROUP BY 작업은 인덱스를 사용하는 경우와 그렇지 못한 경우로 나눠 볼 수 있습니다. 인덱스를 사용하지 못하는 쿼리에서 GROUP BY 작업은 임시 테이블을 사용합니다. 먼저 GROUP BY 임시 테이블이 만들어지는 과정에 대해 알아보겠습니다.
임시 테이블을 이용한 GROUP BY
일반적인 SELECT 쿼리의 실행 순서는 FROM 절의 JOIN과 WHERE 절을 수행 후 GROUP BY > HAVING > ORDER BY가 진행됩니다. 따라서 GROUP BY 동작 전에 JOIN과 WHERE 절이 수행된 행 단위 데이터가 존재합니다.
MySQL은 GROUP BY로 그룹핑할 준비를 위해 메모리 안에 임시 테이블을 준비합니다. 임시테이블 컬럼은 GROUP BY 에 명시한 컬럼과 SELECT에서 명시한 컬럼으로 구성됩니다.
그루핑을 위한 임시 테이블이 준비되면 데이터를 하나씩 읽어가며 GROUP BY 키 값(GROUP BY 절에 명시한 컬럼들)을 확인하고 임시 테이블에 이 그룹이 없으면 새 그룹을 하나 만들고 이미 같은 그룹이 있으면 기존 그룹에 집계 함수(SUM, COUNT 등)를 누적 갱신합니다. 이때 임시 테이블에서 일치하는 그룹을 찾는 방법에는 "해시" 나 "정렬"을 이용한 방법이 있습니다. 앞서 임시 테이블을 메모리에 만든다고 했지만 테이블의 크기가 커지면 디스크로 넘어갈 수 있습니다.
임시 테이블로 그룹핑 작업이 끝나면 HAVIN과 ORDER BY 같은 필터링과 정렬 처리가 추가로 적용됩니다.
인덱스를 이용한 GROUP BY
GROUP BY에도 인덱스를 활용할 수 있습니다. 대신 조건이 까다롭습니다. GROUP BY에 인덱스를 사용하기 위해서는 드라이빙 테이블에 속한 컬럼만 이용해 그룹핑할 때 GROUP BY 칼럼으로 인덱스가 있다면 그 인덱스를 차례대로 읽으면서 그루핑 작업을 수행합니다. 다만, GROUP BY가 인덱스를 사용해서 처리된다 하더라도 그룹 함수 등의 그룹값을 처리해야 해서 임시 테이블이 필요할 때도 있습니다.
salaries 테이블에 (emp_no, from_date)로 생성된 인덱스가 있을 때 다음 쿼리는 아래의 결과가 나옵니다.
mysql> EXPLAIN
-> SELECT emp_no, MAX(from_date)
-> FROM salaries
-> GROUP BY emp_no;
+----+-------------+----------+------------+-------+----------------+---------+---------+------+--------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+----------------+---------+---------+------+--------+----------+--------------------------+
| 1 | SIMPLE | salaries | NULL | range | PRIMARY,emp_no | PRIMARY | 4 | NULL | 295999 | 100.00 | Using index for group-by |
+----+-------------+----------+------------+-------+----------------+---------+---------+------+--------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
결과 테이블의 Extra 컬럼을 보면 'Using index for group-by' 코멘트를 확인할 수 있습니다. (emp_no, from_date) 인덱스 스캔만으로 GROUP BY와 집계 함수를 처리할 수 있기 때문에 임시 테이블을 만들지 않고 처리됨을 볼 수 있습니다.
'데이터베이스' 카테고리의 다른 글
[도서] Real MySQL : 인덱스 - 질문지 (0) | 2025.05.06 |
---|---|
MySQL 스레드 풀 적용 및 성능 비교 (0) | 2025.05.04 |
존재하지 않는 레코드에 비관적 락으로 동시성 제어가 가능할까 (0) | 2025.05.02 |
[도서] Real MySQL : InnoDB 스토리지 엔진 아키텍처 - 질문지 (0) | 2025.04.26 |
[도서] Real MySQL : MySQL 아키텍처 - 질문지 (0) | 2025.04.25 |