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는 조건을 만족한 행 중 금액이 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은 기록된 성공 금액의 합입니다. 성공 이벤트 하나의 금액이 빠져 있으므로 실제 성공 금액이 완전하게 집계됐다는 뜻은 아닙니다.

집계 대상의 행 단위도 확인하세요. 조인으로 동일 이벤트가 여러 행으로 늘어나면 올바른 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 실행 순서 실습으로 이어집니다.
'데이터베이스(SQL) > 데이터베이스 개념' 카테고리의 다른 글
| SQL 상위 3명과 상위 3점수 구분: ROW_NUMBER·RANK·DENSE_RANK (0) | 2026.10.08 |
|---|---|
| SQL 평균의 평균이 틀리는 이유: SUM·COUNT로 가중 평균 계산 (0) | 2026.10.06 |
| SQL 누적합이 같은 날짜에서 뛰는 이유: ROWS와 RANGE 실습 (0) | 2026.10.04 |
| SQL JOIN 후 SUM이 커지는 이유: 상세 테이블 선집계로 중복 막기 (0) | 2026.10.03 |
| PostgreSQL ON CONFLICT로 중복 INSERT 처리하기: 유니크 제약과 RETURNING (0) | 2026.09.29 |
댓글