본문 바로가기
데이터베이스(SQL)/데이터베이스 개념

SQL 조건부 집계가 전체 행을 세는 이유: COUNT·SUM·CASE 실습

by char_lie 2026. 10. 7.
반응형

paid 상태가 두 행인데 조건부 COUNT가 다섯 행을 세었다면, CASE의 ELSE 결과를 확인하세요. COUNT는 0도 값으로 세므로 COUNT(CASE ... ELSE 0 END)는 조건을 만족하지 않는 행까지 포함합니다. 건수는 조건에 맞을 때만 값을 반환하는 COUNT나 1·0의 SUM으로 구합니다.

SQLite 3.53.1에서 실행한 가상 이벤트 자료입니다. 성공 이벤트 건수와 금액이 기록된 성공 건수를 따로 비교합니다. 사용 중인 DBMS의 자료형과 함수 지원도 확인하세요.

같은 데이터 묶음에서 조건에 맞는 행만 골라 집계하는 그림
조건부 집계는 각 행의 조건 결과와 집계 함수의 규칙을 함께 확인합니다.

1. 집계 전에 행별 조건 결과를 확인합니다

CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    team TEXT NOT NULL,
    status TEXT,
    amount INTEGER
);
INSERT INTO events VALUES
    (1, 'A', 'paid', 100), (2, 'A', 'pending', 50),
    (3, 'A', 'paid', NULL), (4, 'B', 'canceled', 30),
    (5, 'B', NULL, 40);

SELECT id, status,
       CASE WHEN status = 'paid' THEN 1 ELSE 0 END AS flag
FROM events
ORDER BY id;
id | status   | flag
1  | paid     | 1
2  | pending  | 0
3  | paid     | 1
4  | canceled | 0
5  | NULL     | 0

paid는 1번·3번 두 행입니다. 3번의 금액은 NULL이며, 5번은 상태 자체가 없습니다. status = 'paid'가 참이 아닌 행은 이 CASE의 ELSE 경로로 들어갑니다. NULL 상태를 따로 보고하려면 IS NULL 조건이 필요합니다.

2. 0도 COUNT가 세는 값입니다

SELECT
    COUNT(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS wrong_n,
    COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_n,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_sum,
    COUNT(CASE WHEN status = 'paid' THEN amount END) AS known_amount_n
FROM events;
wrong_n | paid_n | paid_sum | known_amount_n
5       | 2      | 2        | 1

첫 COUNT는 모든 행에서 1 또는 0을 받으므로 5입니다. 두 번째 CASE는 ELSE를 생략했습니다. 조건에 맞지 않으면 NULL이 되어 COUNT에서 제외되므로 2입니다. SUM은 1·0을 더해 역시 2를 얻습니다.

내용이 있는 빈 수량 카드와 값 자체가 없는 윤곽 카드의 차이를 보여 주는 그림
COUNT는 0도 값으로 셉니다. 조건에 맞지 않는 행을 세지 않으려면 NULL을 반환하도록 구성합니다.

마지막 COUNT는 조건을 만족한 행 중 금액이 NULL이 아닌 행만 셉니다. 성공 이벤트는 두 개지만 금액이 채워진 성공 이벤트는 하나입니다. THEN 1과 THEN amount는 같은 질문이 아닙니다.

3. 그룹별 건수와 알려진 금액을 함께 점검합니다

SELECT team,
       COUNT(*) AS all_n,
       COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_n,
       COUNT(CASE WHEN status = 'paid' THEN amount END) AS known_amount_n,
       SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS known_paid_total
FROM events
GROUP BY team
ORDER BY team;

팀전체 행paid 건수paid 금액 유효 건수기록된 paid 금액 합계

A 3 2 1 100
B 2 0 0 0

B는 입력 행이 있지만 조건에 맞는 행이 없습니다. ELSE 0이 있는 SUM은 B에서 0이 됩니다. A의 합계 100은 기록된 성공 금액의 합입니다. 성공 이벤트 하나의 금액이 빠져 있으므로 실제 성공 금액이 완전하게 집계됐다는 뜻은 아닙니다.

행 단위 검토표와 그룹별 집계 결과를 나란히 확인하는 그림
성공 행 수와 성공 금액의 유효 개수는 NULL 때문에 달라질 수 있습니다.

집계 대상의 행 단위도 확인하세요. 조인으로 동일 이벤트가 여러 행으로 늘어나면 올바른 CASE를 써도 건수가 중복됩니다. 조건 식만 고치기 전에 원본의 고유 이벤트 ID와 집계 전 행 수를 비교해야 합니다.

4. 입력 행이 하나도 없는 경우는 다릅니다

SELECT
    COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_n,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS raw_sum,
    COALESCE(SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END), 0) AS shown_n
FROM events
WHERE 1 = 0;
paid_n | raw_sum | shown_n
0      | NULL    | 0

아예 빈 입력에서는 SUM에 ELSE 0을 썼더라도 더할 행이 없어 NULL이 됩니다. COUNT는 0을 반환합니다. 화면에서 ‘이벤트 없음’을 0건으로 표시하기로 정했다면 COALESCE를 명시할 수 있지만, 관측되지 않은 금액까지 무조건 0원으로 바꾸는 규칙과는 구분해야 합니다.

SELECT COUNT(*) AS all_n,
       COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_n,
       COUNT(CASE WHEN status = 'pending' THEN 1 END) AS pending_n,
       COUNT(CASE WHEN status = 'canceled' THEN 1 END) AS canceled_n,
       COUNT(CASE WHEN status IS NULL THEN 1 END) AS unknown_n
FROM events;
all_n | paid_n | pending_n | canceled_n | unknown_n
5     | 2      | 1         | 1          | 1

이번 자료에서는 2+1+1+1=5로 전체 행 수와 일치합니다. 실제 상태에 refunded 같은 새 값이 추가되면 별도 기타 상태 분류가 필요합니다. 조건들이 겹치면 여러 집계에 같은 행이 들어갈 수 있어 합계를 곧바로 전체 건수로 해석하면 안 됩니다.

5. 핵심 정리

COUNT는 NULL이 아닌 값의 개수, SUM은 반환 값의 합입니다. 조건부 건수를 셀 때 CASE의 불일치 결과, THEN 값의 NULL 가능성, 빈 입력, 조인 중복을 확인하세요. 행별 조건 결과를 먼저 출력하면 잘못된 집계를 빠르게 찾을 수 있습니다.

기본 집계 규칙은 COUNT(*)와 COUNT(컬럼)의 NULL 차이에서 확인할 수 있습니다. 전체 입력 행을 줄이는 WHERE와 그룹을 거르는 HAVING은 WHERE·HAVING 실행 순서 실습으로 이어집니다.

반응형

댓글