1. 집계란 무엇인가
집계는 여러 행을 하나의 숫자로 요약하는 작업이다.
예를 들어 다음과 같은 질문은 모두 집계를 필요로 한다.
- 전체 주문 건수는 몇 개인가
- 총 매출은 얼마인가
- 평균 점수는 얼마인가
이 질문들의 공통점은 개별 행이 아니라, 여러 행을 하나의 값으로 요약한다는 점이다.
SQL에서는 이를 집계 함수로 처리한다.
2. 기본 집계 함수 살펴보기
자주 사용하는 집계 함수는 다음과 같다.
• COUNT: 조건을 만족하는 행의 개수를 센다.
• SUM: 숫자형 컬럼의 총합
• AVG: 숫자형 컬럼의 평균값
• MIN: 가장 작은 값을 반환
• MAX: 가장 큰 값을 반환
SELECT
COUNT(unit_price) AS item_rows,
SUM(unit_price) AS item_price_sum,
AVG(unit_price) AS item_price_avg,
MIN(unit_price) AS item_price_min,
MAX(unit_price) AS item_price_max
FROM basic.order_items;
#일반적으로 집계 함수는 NULL을 무시한다. 일반적으로 집계 함수는 NULL 값을 자동으로 제외하고 계산
• SUM, AVG, MIN, MAX, COUNT(컬럼) → NULL 무시
• COUNT(*) → NULL 포함 (행 단위 계산)
이 차이 때문에, COUNT 계열은 특히 주의해서 사용해야 한다.
2.1) COUNT 사용법 3종
COUNT(*)
• NULL 여부와 상관없이 결과 행(row) 자체의 개수를 카운트
COUNT(컬럼)
• 해당 컬럼이 NULL이 아닌 행만 카운트
COUNT(DISTINCT 컬럼)
- NULL값 제외한을 제외한 서로 다른 값의 개수를 센다. (=고유값)
[DISTINCT는 중복된 값을 제거하고 유일한 값만 남긴다.]
예제: 전체 주문 건수 구하기
수도코드
| orders 테이블에 있는 모든 주문의 개수를 센다 |
실제 SQL 코드
SELECT COUNT(*) AS order_count
FROM orders;
실행 흐름 (Line by Line)
| 1. orders 테이블의 모든 행을 가져온다. 2. 가져온 행 전체를 대상으로 개수를 계산한다. 3. 결과는 하나의 행, 하나의 컬럼으로 반환된다. |
이처럼 집계 함수는 여러 행을 하나의 결과로 줄이는 연산이다.
3. GROUP BY () — 기준을 나누어 집계하기
전체 합계만으로는 부족한 경우가 많다. 대부분의 분석에서는 “기준별 요약”이 필요하다.
GROUP BY는 집계의 기준을 나누는 역할을 한다.
즉, 여러 행을 특정 기준으로 묶어서 그룹 단위로 집계 결과를 만들기 위해 사용한다.
3.1) GROUP BY ()를 사용하는 이유?
GROUP BY()를 사용하면 모든 행에 대해 값을 집계해 계산하기 때문에 행은 1개로 줄어든다.
허나 실제로 사용자들이 원하는 것은
• 전체 매출이 아닌 카테고리별 매출
• 전체 인원이 아닌 부서별 인원의 수
• 전체 주문이 아닌 사용자별 주문 횟수
대부분의 상황에선 전체가 아닌, 특정 기준별로 나눠서 보고 싶어 한다.
이런 상황에서의 해결책이 바로 GROUP BY 이다.
3.2) GROUP BY ()의 역할
• 해당 컬럼(괄호안의 컬럼)의 값이 같은 행들을 묶여 그룹이 된다. (Ex. 주문번호, 결제번호 등등)
• 각 묶음마다 집계 함수를 계산한다
• 결과는 그룹의 개수만큼 행이 나온다.
3.3) SELECT절과 GROUP BY와의 관계
만약 GROUP BY를 사용했을 경우 SELECT 절에는 다음과 같은 컬럼만 올수 있다,
• GROUP BY에 사용한 컬럼
• 집계 함수로 감싼 컬럼
그 이유는.
그룹 안에 여러행이 있는데 그중에 어떠한 값을 보여줘야 할지 SQL은 알수 없기 때문이다.
- 어렵다면 GROUP BY한 컬럼과 SELECT절에서 출력한 컬럼이 매치가 안되면 오류 발생
예제: 사용자별 주문 건수 구하기
수도코드
| 사용자별로 주문을 묶는다 각 사용자 그룹마다 주문 개수를 센다 |
실제 SQL 코드
SELECT
user_id,
COUNT(*) AS order_count
FROM orders
GROUP BY user_id;
실행 흐름 (Line by Line)
| 1. orders 테이블의 모든 행을 가져온다. 2. user_id 기준으로 행을 그룹으로 나눈다. 3. 각 그룹마다 COUNT(*)를 계산한다. 4. 결과는 사용자 수만큼의 행으로 반환된다. |
GROUP BY를 사용하는 순간부터 집계는 그룹 단위로 계산된다는 점이 중요하다.
4. HAVING — 집계 결과를 필터링하는 조건
HAVING은 집계가 끝난 결과를 대상으로 조건을 거는 절로 GROUP BY로 만들어진 그룹 단위의 결과를 걸러내기 위해 사용한다.
SQL에서 COUNT, SUM, AVG 같은 집계 함수는 개별 행이 아니라 그룹 전체를 계산한 뒤에야 값이 만들어진다.
HAVING은 바로 이 집계 결과가 만들어진 이후 단계에서 동작한다.
예제 주문이 3건 이상인 사용자만 보기
수도코드
| 사용자별로 주문 개수를 센다 주문 개수가 3 이상인 사용자만 남긴다 |
SELECT
user_id,
COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 3;
실행 흐름 (Line by Line)
| 1. 전체 주문 데이터를 가져온다. 2. 사용자별로 그룹을 만든다. 3. 각 그룹의 주문 개수를 계산한다. 4. 계산된 개수가 3 이상인 그룹만 남긴다. |
WHERE와 HAVING의 차이
집계를 하다 보면 조건을 어디에 써야 할지 헷갈리는 경우가 많다.
핵심 차이는 다음과 같다.
- WHERE: 집계 전에 개별 행(row) 을 필터링한다
- HAVING: 집계 후에 그룹(group) 을 필터링한다
5. 조건부 집계
실무에서 매우 자주 사용되는 패턴이 조건부 집계다.
GROUP BY된 그룹 내부에서 특정 조건을 만족하는 행만 대상으로 집계하는 방법이다.
SUM(CASE WHEN) 패턴
“조건을 만족한 대상의 총합”을 구할 때 사용한다. COUNT와 달리 ELSE 0이 매우 중요 (NULL이면 합산 누락)
SUM(CASE WHEN 조건 THEN 값 ELSE 0 END)
CASE로 조건을 만족하면 값 반환, 조건을 만족하지 않으면 0 반환한다.
SUM 결과는 조건을 만족한 값만 누적된 합계이다.
COUNT(CASE WHEN) 패턴
“조건을 만족한 행의 개수”를 세고 싶을 때 사용한다. TRUE / FALSE를 세는 것이 아닌 NULL 여부를 이용해 필터링
COUNT(CASE WHEN 조건 THEN PK END)
CASE로 조건을 만족하면 NULL이 아닌 값, 조건을 만족하지 않으면 NULL을 반환한다.
COUNT는 NULL을 세지 않기 때문에 결과적으로 조건을 만족한 행만 카운트하게 된다.
SUM vs COUNT 차이 정리
| 조건부 SUM | 조건부 COUNT |
| 얼마인가? (규모, 금액, 수치) | 몇 건인가? (개수) |
| 조건 불만족 시 0으로 누적 차단 | NULL 여부를 이용한 필터링 |
예제: 상태별 주문 건수 계산하기
주문 상태가 다음과 같다고 가정한다.
- status = 'PAID'
- status = 'CANCELLED'
수도코드
|
주문 상태가 PAID인 경우만 1로 센다
주문 상태가 CANCELLED인 경우만 1로 센다. |
실제 SQL 코드
SELECT
SUM(CASE
WHEN status = 'PAID' THEN 1
ELSE 0
END) AS paid_count,
SUM(CASE
WHEN status = 'CANCELLED' THEN 1
ELSE 0
END) AS cancelled_count
FROM orders;
실행 흐름 (Line by Line)
| 1. 모든 주문 행을 가져온다 2. 각 행마다 조건에 따라 1 또는 0을 반환한다 3. 반환된 숫자들을 모두 더한다.. |
오늘 학습한 내용 정리
이번 글에서는 데이터를 숫자로 요약하는 집계와 그룹화 그리고 조건부 집계에 대해 복습했다.
- 집계는 여러 행을 하나의 값으로 줄이는 연산이다. (COUNT, SUM, AVG, MIN, MAX)
- GROUP BY는 집계를 위한 기준을 만들어 행을 그룹 단위로 묶는다.
- WHERE는 집계 이후에 그룹 결과를 필터링한다.
- SUM, COUNT를 사용해 조건부 집계를 할수 있다.
'F.SQL > SQL 기초 복습' 카테고리의 다른 글
| [SQL] 복잡한 쿼리를 다루는 방법 — 서브쿼리와 CTE (0) | 2026.01.04 |
|---|---|
| [SQL] JOIN 실전 — 테이블 연결하기 (0) | 2026.01.04 |
| [SQL] 테이블 구조 이해 & JOIN 준비 운동 (0) | 2026.01.04 |
| [SQL] 조건을 조금 더 똑똑하게 — CASE로 데이터 분류하기 (0) | 2026.01.03 |
| [SQL] 기본 흐름 — SELECT부터 정렬까지 (0) | 2026.01.03 |
