SQL: 집계함수

김기현·2025년 6월 15일

Database

목록 보기
11/24

집계 함수는 데이터를 그룹화하고 요약할 때 사용하는 SQL이다. 통계, 리포트, 대시보드, 매출 분석, 사용자 활동 분석 등 거의 모든 실무 쿼리에 사용된다.

집계 함수

여러 행의 값을 하나의 결과로 요약하는 함수이다.

함수설명
COUNT()행의 개수
SUM()합계
AVG()평균
MAX()최대값
MIN()최소값

이 함수들은 일반적으로 GROUP BY와 함께 사용하여 카테고리별 집계를 수행한다.

예제

CREATE TABLE orders
(
    id         INT PRIMARY KEY,
    user_id    INT,
    product    VARCHAR(100),
    amount     DECIMAL(10, 2),
    order_date DATE
);

-- 샘플 데이터
INSERT INTO orders
VALUES (1, 1, 'Keyboard', 50.00, '2024-01-01'),
       (2, 1, 'Mouse', 20.00, '2024-01-03'),
       (3, 2, 'Monitor', 200.00, '2024-01-05'),
       (4, 1, 'Chair', 150.00, '2024-02-10'),
       (5, 2, 'Desk', 300.00, '2024-02-15');

COUNT() - 행 개수 세기

SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id;

사용자 별 주문 횟수를 검색하는 쿼리문이다.

user_idorder_count
13
22

SUM() - 총합 구하기

SELECT user_id, SUM(amount) AS total_spent
FROM orders
GROUP BY user_id;

사용자 별 지출액을 집계하는 쿼리문이다.

user_idtotal_spent
1220.00
2500.00

AVG() - 평균 구하기

SELECT user_id, AVG(amount) AS avg_order
FROM orders
GROUP BY user_id;

사용자별 평균 주문 금액을 집계하는 쿼리문이다.

user_idavg_order
173.33
2250.00

MAX(), MIN()

SELECT user_id, MAX(amount) AS max_order, MIN(amount) AS min_order
FROM orders
GROUP BY user_id;

사용자별 최고 및 최소 주문 금액을 집계하는 쿼리문이다.

user_idmax_ordermin_order
1150.0020.00
2300.00200.00

날짜 기준 그룹화

SELECT MONTH(order_date) AS month, SUM(amount) AS monthly_sales
FROM orders
GROUP BY MONTH(order_date);

월별 매출을 집계하는 쿼리문이다.

monthmonthly_sales
1270.00
2450.00

조건부 집계

SELECT user_id, COUNT(*) AS jan_orders
FROM orders
WHERE MONTH(order_date) = 1
GROUP BY user_id;

1월에 주문한 사용자만 집계하는 쿼리문이다.

user_idjan_orders
12
21

HAVING으로 그룹 조건 필터링

SELECT user_id, SUM(amount) AS total_spent
FROM orders
GROUP BY user_id
HAVING total_spent > 300;

총 주문액이 300을 초과한 사용자를 집계하는 쿼리문이다.

user_idtotal_spent
2500.00
  • WHERE은 행 필터링
  • HAVING은 그룹 필터링

상황 별 알맞은 집계 방식

상황추천
전체 건수 확인SELECT COUNT(*) FROM table;
필드의 null 제외 카운트COUNT(column)
월/연도별 요약GROUP BY MONTH(date) or YEAR(date)
다중 그룹핑GROUP BY user_id, MONTH(order_date)
정렬ORDER BY SUM(amount) DESC 등과 함께 사용

고급 집계

SQL에서는 ROLLUPCUBE라는 고급 집계 보고서가 존재한다.

기능설명MySQL 지원 여부
ROLLUP계층적 요약 (소계, 총계)지원
CUBE다차원 조합 요약MySQL은 직접 미지원 (우회 가능)

예제

CREATE TABLE cafe_orders
(
    주문월 VARCHAR(7),  -- '2024-01', '2024-02'
    지점명 VARCHAR(20), -- '강남점', '홍대점'
    메뉴  VARCHAR(50), -- '아메리카노', '라떼'
    수량  INT
);

INSERT INTO cafe_orders
VALUES ('2024-01', '강남점', '아메리카노', 100),
       ('2024-01', '강남점', '라떼', 50),
       ('2024-01', '홍대점', '아메리카노', 80),
       ('2024-02', '강남점', '아메리카노', 120),
       ('2024-02', '홍대점', '라떼', 60);

ROLLUP

SELECT 주문월, 지점명, SUM(수량) AS 총수량
FROM cafe_orders
GROUP BY 주문월, 지점명
WITH ROLLUP;

이처럼 쿼리문을 날리면 아래와 같은 결과가 나온다.

주문월지점명총수량
2024-01강남점150
2024-01홍대점80
2024-01NULL230 ← 1월 총합
2024-02강남점120
2024-02홍대점60
2024-02NULL180 ← 2월 총합
NULLNULL410 ← 전체 총합
  • NULL은 요약 행을 의미한다.
  • ROLLUP은 왼쪽부터 순차적으로 소계하며 층계를 자동으로 생성한다.

CUBE

MySQL에서는 CUBE를 지원하지 않기 때문에 우회해야 한다. 주로 UNION ALL + GROUP BY조합으로 구현한다.

-- 주문월 + 지점명
SELECT 주문월, 지점명, SUM(수량) AS 총수량
FROM cafe_orders
GROUP BY 주문월, 지점명

UNION ALL

-- 주문월만 (지점 소계)
SELECT 주문월, NULL AS 지점명, SUM(수량) AS 총수량
FROM cafe_orders
GROUP BY 주문월

UNION ALL

-- 지점명만 (월 소계)
SELECT NULL AS 주문월, 지점명, SUM(수량) AS 총수량
FROM cafe_orders
GROUP BY 지점명

UNION ALL

-- 전체 합계
SELECT NULL AS 주문월, NULL AS 지점명, SUM(수량) AS 총수량
FROM cafe_orders;

이렇게 조회하면 마지막 전체 합계의 경우 아래와 같은 결과가 나온다.

주문월지점명총수량
2024-01강남점150
2024-01홍대점80
2024-02강남점120
2024-02홍대점60
2024-01NULL230 (1월 소계)
2024-02NULL180 (2월 소계)
NULL강남점270 (강남점 소계)
NULL홍대점140 (홍대점 소계)
NULLNULL410 (전체 총합)

각 행의 의미는 아래와 같다.

조합의미
(주문월, 지점명)기본 상세 집계
(주문월, NULL)해당 월 전체 소계
(NULL, 지점명)해당 지점 전체 소계
(NULL, NULL)전체 총합

윈도우 함수

윈도우 함수는 집계 함수처럼 작동하지만 그룹핑 없이 각 행에 결과를 붙인다. 이는 GROUP BY없이도 누적합, 순위, 이동 평균 등을 계산할 수 있다는 의미이다.

함수설명
ROW_NUMBER()순번 (중복 없음)
RANK()순위 (동점 허용)
DENSE_RANK()밀집 순위 (순위 건너뛰지 않음)
SUM() OVER()누적합
AVG() OVER()누적 평균
LAG() / LEAD()이전값 / 다음값 참조

예제

CREATE TABLE sales
(
    판매일자 DATE,
    직원명  VARCHAR(20),
    매출액  INT
);

INSERT INTO sales
VALUES ('2024-06-01', '홍길동', 200),
       ('2024-06-02', '홍길동', 300),
       ('2024-06-03', '홍길동', 250),
       ('2024-06-01', '김철수', 150),
       ('2024-06-02', '김철수', 400),
       ('2024-06-03', '김철수', 350);

누적합, SUM() OVER

SELECT 직원명,
       판매일자,
       매출액,
       SUM(매출액) OVER (PARTITION BY 직원명 ORDER BY 판매일자) AS 누적매출
FROM sales;
직원명판매일자매출액누적매출
김철수2024-06-01150150
김철수2024-06-02400550
김철수2024-06-03350900
홍길동2024-06-01200200
홍길동2024-06-02300500
홍길동2024-06-03250750
  • PARTITION BY는 직원 별로 누적된다.
  • ORGER BY는 날짜순으로 누적된다.

순위 함수, RANK

SELECT 직원명,
       판매일자,
       매출액,
       RANK() OVER (PARTITION BY 직원명 ORDER BY 매출액 DESC) AS 순위
FROM sales;
직원명매출액순위
김철수4001
김철수3502
김철수1503
홍길동3001
홍길동2502
홍길동2003

이전 행 참조, LAG()

SELECT 직원명,
       판매일자,
       매출액,
       LAG(매출액) OVER (PARTITION BY 직원명 ORDER BY 판매일자) AS 전날매출
FROM sales;
직원명판매일자매출액전날매출
김철수2024-06-01150NULL
김철수2024-06-02400150
김철수2024-06-03350400
홍길동2024-06-01200NULL
홍길동2024-06-02300200
홍길동2024-06-03250300
profile
백엔드 개발자를 목표로 공부하는 대학생

0개의 댓글