SQL GROUP BY와 집계 함수 정리 (COUNT, SUM, HAVING)
“카테고리별 글 수”, “회원별 주문 합계”, “날짜별 방문자 수”처럼 데이터를 묶어서 세고 더하는 일은 SQL에서 가장 자주 하는 작업이다. 이 글에서는 SQL GROUP BY 문법과 COUNT, SUM, AVG 같은 집계 함수, 그리고 자주 헷갈리는 WHERE와 HAVING의 차이를 예제로 정리한다. 조건절 기초는 SQL WHERE 조건 정리에 먼저 정리해 두었다.

예제 테이블
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
customer VARCHAR(20),
category VARCHAR(20),
amount INT,
ordered_at DATE
);
INSERT INTO orders (customer, category, amount, ordered_at) VALUES
('kim', 'book', 15000, '2026-10-01'),
('kim', 'food', 8000, '2026-10-02'),
('lee', 'book', 22000, '2026-10-02'),
('park', 'food', 12000, '2026-10-03'),
('lee', 'book', 18000, '2026-10-05');INSERT 문법은 SQL INSERT, SELECT 이해하기에 있다.
집계 함수 다섯 가지
| 함수 | 뜻 |
|---|---|
COUNT(*) | 행 개수 |
COUNT(컬럼) | NULL이 아닌 값의 개수 |
SUM(컬럼) | 합계 |
AVG(컬럼) | 평균 |
MAX(컬럼) / MIN(컬럼) | 최댓값 / 최솟값 |
SELECT COUNT(*), SUM(amount), AVG(amount), MAX(amount) FROM orders;SQL GROUP BY 기본
-- 카테고리별 주문 수와 합계
SELECT category, COUNT(*) AS cnt, SUM(amount) AS total
FROM orders
GROUP BY category;
-- 결과
-- book 3 55000
-- food 2 20000GROUP BY에 적은 컬럼 값이 같은 행끼리 하나로 묶이고, 집계 함수는 각 묶음 안에서 계산된다.
여러 컬럼으로 묶기
SELECT customer, category, SUM(amount) AS total
FROM orders
GROUP BY customer, category
ORDER BY customer, total DESC;SQL GROUP BY 결과 거르기: HAVING과 WHERE 차이
WHERE는 묶기 전에 행을 거르고, HAVING은 묶은 뒤 결과를 거른다. 집계 함수 결과로 조건을 걸 때는 HAVING을 써야 한다.
-- 10월 2일 이후 주문만 모아서, 합계가 20000 이상인 고객
SELECT customer, SUM(amount) AS total
FROM orders
WHERE ordered_at >= '2026-10-02' -- 묶기 전 필터
GROUP BY customer
HAVING SUM(amount) >= 20000; -- 묶은 뒤 필터| 구분 | WHERE | HAVING |
|---|---|---|
| 적용 시점 | GROUP BY 전 | GROUP BY 후 |
| 집계 함수 사용 | 불가 | 가능 |
| 성능 | 먼저 줄여서 유리 | 묶은 뒤라 상대적으로 무거움 |
집계와 상관없는 조건은 WHERE로 먼저 걸러야 묶을 데이터가 줄어 빠르다.
자주 만나는 오류: ONLY_FULL_GROUP_BY
SELECT customer, category, SUM(amount) FROM orders GROUP BY customer;
-- ERROR 1055: Expression #2 of SELECT list is not in GROUP BY clause ...MySQL 5.7 이후 기본 설정에서는 SELECT에 GROUP BY에도 없고 집계 함수도 아닌 컬럼을 쓰면 오류가 난다. 고객 한 명에게 카테고리가 여러 개라 어떤 값을 보여 줄지 정할 수 없기 때문이다. 그 컬럼을 GROUP BY에 추가하거나, MAX(category)처럼 집계 함수로 감싸서 해결한다. 설정을 끄는 방법도 있지만 결과가 임의로 나오니 권하지 않는다.
SQL GROUP BY 실전 예제
-- 날짜별 매출
SELECT ordered_at, SUM(amount) AS daily_total
FROM orders
GROUP BY ordered_at
ORDER BY ordered_at;
-- 월별 매출
SELECT DATE_FORMAT(ordered_at, '%Y-%m') AS ym, SUM(amount)
FROM orders
GROUP BY ym;
-- 워드프레스: 상태별 글 수
SELECT post_status, COUNT(*) FROM wp_posts
WHERE post_type = 'post'
GROUP BY post_status;집계 쿼리가 느리다면 SQL 실행 계획(EXPLAIN)을 먼저 확인해 어떤 인덱스를 쓰는지 살펴보자.
여러 테이블을 합친 뒤 묶을 때는 JOIN 정리와 함께 쓰면 된다. GROUP BY에 쓰는 컬럼에 인덱스가 있으면 훨씬 빨라지는데, 이 부분은 MySQL 인덱스 설계 원칙에서 다뤘다. MySQL의 GROUP BY 처리 규칙은 MySQL 공식 문서에 있다.
마무리
GROUP BY는 “무엇을 기준으로 묶을지”, 집계 함수는 “묶은 것을 어떻게 계산할지”, HAVING은 “계산한 결과 중 무엇을 남길지”로 기억하면 된다. 서버 권한 관리는 전날 글인 sudo 권한 설정에 있다.
첫 댓글을 남겨보세요