독서 통계 집계
65분 안팎
학습 목표
COUNT·SUM과 빈 결과의 의미를 설명합니다.
개념
한 행이 아니라 여러 기록을 요약합니다
운영자는 기록 목록 전체보다 완료 여부별 건수와 총 쪽수가 필요할 수 있습니다. COUNT와 SUM은 여러 입력 행을 하나의 숫자로 요약하는 집계 함수입니다. COUNT(*)는 행 수, SUM(pages)는 알려진 쪽수의 합입니다. 제목 목록이 필요할 때와 현황 숫자가 필요할 때는 출력의 단위가 다릅니다. 이번 레슨에서는 원본 한 행이 기록 한 건이고 집계 결과 한 행은 완료 상태 한 그룹이라는 차이를 먼저 정합니다.
GROUP BY completed는 완료 표시가 같은 기록을 묶습니다. 0은 미완료, 1은 완료 그룹입니다. 조회용 fixture에서 완료 표시가 NULL이면 미확인 기록끼리 별도의 그룹이 됩니다. NULL을 자동으로 0으로 바꾸지 않으므로 미확인 기록이 미완료 건수에 섞이지 않습니다. 집계 결과의 열은 completed, COUNT(*), COALESCE(SUM(pages),0) 순서입니다. 여기서 0 대체는 합계 출력에만 적용합니다.
건수와 총 쪽수를 함께 보여 주면 값의 범위를 해석하기 쉽습니다. 3건 30쪽과 1건 30쪽은 합계는 같아도 기록 구성이 다릅니다. 이 프로그램은 한 사람의 독서 기록을 다루므로 기록 수를 독자 수나 전체 이용자 수라고 부르지 않습니다. 같은 제목을 다시 읽은 기록도 별개의 ID이면 각각 한 건입니다. 통계의 관측 단위를 정하지 않으면 올바른 SQL 결과도 잘못된 설명으로 전달될 수 있습니다.
COUNT의 대상과 SUM의 누락 처리
COUNT(*)는 pages가 NULL인 행도 셉니다. COUNT(pages)는 pages가 NULL이 아닌 행만 셉니다. pages가 0인 행은 두 방식 모두 포함됩니다. 전체 기록 수라는 요청에는 COUNT(*)가 맞습니다. 측정된 쪽수가 있는 기록 수라는 요청에는 COUNT(pages)를 사용할 수 있습니다. 별표와 열 이름의 차이를 단순한 문법 변형으로 보면 NULL fixture에서 건수가 어긋나게 됩니다.
SUM(pages)는 NULL을 더하지 않습니다. 10, NULL, 0이 있으면 합계는 10이고 COUNT(*)는 3, COUNT(pages)는 2입니다. 모든 쪽수가 NULL인 그룹에서는 SUM이 NULL입니다. NULL을 제외한 합계를 보여 주는 것과 모든 기록의 쪽수가 알려졌다고 주장하는 것은 다릅니다. 누락이 있는 통계에는 측정 범위를 설명하고 필요하면 누락 건수도 별도로 점검합니다.
COALESCE(SUM(pages),0)는 SUM 결과가 NULL일 때만 0을 반환합니다. 이번 보고서는 표시할 합계가 없는 경우 0을 쓰기로 정합니다. 이는 원본의 NULL을 0으로 덮어쓰는 작업이 아닙니다. NULL만 있는 그룹과 실제 0쪽 그룹을 이 열 하나로 구별할 수 없으므로 건수와 원본 상태를 함께 확인합니다. 숫자 출력이 편리하다는 이유로 미측정을 실제 0이라고 해석하지 않습니다.
빈 표와 빈 그룹을 구분합니다
GROUP BY 없이 SELECT COUNT(*), SUM(pages) FROM reading_records를 실행하면 표가 비어 있어도 집계 결과 한 행이 나오고 값은 0과 NULL입니다. COALESCE로 합계를 감싸면 0과 0입니다. 반면 GROUP BY completed가 있는 조회는 빈 표에서 그룹을 만들 수 없으므로 결과가 0행입니다. 빈 문자열 기대 출력과 숫자 0의 기대 출력은 다른 계약이며 임의로 맞바꾸지 않습니다.
완료 기록이 없다고 해서 GROUP BY가 완료 1 그룹을 0건으로 만들어 주지 않습니다. 표에 실제 있는 그룹만 나옵니다. 상태별 고정 행이 필요한 화면이라면 상태 목록을 기준으로 연결하는 추가 설계가 필요하지만 이번 과제는 존재하는 상태만 반환합니다. 결과에서 1 행이 빠졌을 때 문법 오류를 찾기 전에 그 상태의 입력 행이 있었는지 확인합니다.
WHERE completed = 1을 집계 앞에 적용하면 완료 기록만 집계 대상에 남습니다. GROUP BY completed만 쓰면 모든 상태를 각각 집계합니다. 완료 합계를 요청받았는데 모든 쪽수를 더하는 실수는 결과 숫자만 보면 발견하기 어렵습니다. 미완료 100쪽과 완료 3쪽을 섞은 작은 예제를 만들면 기대값 3과 잘못된 값 103의 차이가 드러납니다. 이 예제는 미션의 Python 대조 계산에도 사용합니다.
평균과 중앙값의 질문을 점검합니다
AVG(pages)는 NULL이 아닌 쪽수의 합을 그 값의 개수로 나눕니다. 10, NULL, 0이면 평균은 5입니다. SUM(pages)를 COUNT(*)로 나누면 분모가 3으로 달라집니다. 정수 나눗셈까지 적용되면 표현도 달라지므로 평균에는 AVG를 쓰고 측정된 기록의 평균인지 전체 기록의 평균인지 적습니다. 앞서 Python으로 계산한 전체 정수 기록 평균과 SQL의 누락 포함 자료 평균은 분모를 맞춘 뒤 비교합니다.
중앙값은 쪽수를 정렬한 뒤 가운데 값을 보는 지표입니다. 10, 10, 1000의 평균은 340이고 중앙값은 10입니다. 두 숫자는 다른 질문에 답합니다. 평균은 전체 합계의 영향을 반영하고 중앙값은 가운데 위치를 설명합니다. 이번 SQL 과제는 건수와 합계에 집중하며 중앙값은 앞 모듈 Python 함수로 확인할 수 있습니다. 일부 기록만 보고 독서 능력이 향상되었다는 인과 결론을 내리지는 않습니다.
개인 기록의 합계가 늘었다면 기록을 더 자주 입력했거나 책 분량이 달라졌을 수도 있습니다. 완료 표시의 의미와 기록 기간이 같아야 이전 결과와 비교할 수 있습니다. 보고서에는 집계 대상과 누락 정책을 짧게 적습니다. 수치 계산의 정확성과 해석의 타당성을 별도로 검토하는 습관은 통계 과제뿐 아니라 팀의 운영 지표를 읽을 때도 도움이 됩니다.
집계 오류를 읽고 제출을 점검합니다
SELECT completed, title, COUNT(*)처럼 그룹 기준도 집계 결과도 아닌 제목을 같이 선택하지 않습니다. 한 그룹 안에는 제목이 여러 개일 수 있어 어떤 제목을 대표로 보여 줄지 정의되지 않습니다. SQLite에서 이런 조회가 실행될 수 있어도 보고서 계약이 명확해지는 것은 아닙니다. 선택 열에는 그룹 기준과 집계 결과만 적어서 입력 순서에 기대는 결과를 피합니다.
misuse of aggregate function 같은 메시지를 만나면 SUM이나 COUNT를 WHERE에서 바로 조건으로 썼는지 살펴봅니다. WHERE는 개별 행을 거르고 집계된 그룹을 조건으로 거를 때는 HAVING을 사용합니다. 이번 과제는 그룹 조건을 추가하지 않으므로 GROUP BY와 ORDER BY를 차례로 적으면 됩니다. 집계 함수 뒤 괄호가 빠졌다면 near ...: syntax error가 나타날 수도 있습니다.
과제에서는 완료 0·1·NULL 그룹, 0쪽, 모든 쪽수가 NULL인 그룹, 빈 표를 확인합니다. 결과 순서는 ORDER BY completed로 정하며 SQLite 오름차순에서 NULL 그룹이 먼저 나옵니다. 각 건수는 같은 상태의 원본 행을 직접 세고 합계는 알려진 쪽수만 더해 대조합니다. 출력 열 이름을 별칭으로 꾸미더라도 값의 순서는 유지합니다. 평균의 분모나 고정 상태 목록이 필요한 조회는 더 읽기로 확장합니다.
따라하기
건수의 분모
NULL을 제외하는 열 집계와 행 집계를 비교합니다. 채점용 SQL 출력은 정수값인 실수를 5로 표시합니다.
CREATE TABLE r(pages INTEGER); INSERT INTO r VALUES(10),(NULL),(0); SELECT COUNT(*),COUNT(pages),SUM(pages),AVG(pages) FROM r;실행 결과
3 2 10 5
빈 집합 비교
첫 조회는 한 행을 반환하고 두 번째 그룹 조회에는 출력이 없습니다.
CREATE TABLE r(completed INTEGER,pages INTEGER); SELECT COUNT(*),SUM(pages),COALESCE(SUM(pages),0) FROM r; SELECT completed,COUNT(*) FROM r GROUP BY completed;실행 결과
0 NULL 0
상태별 합계
미확인 상태도 별도 그룹으로 보존합니다.
CREATE TABLE r(completed INTEGER,pages INTEGER); INSERT INTO r VALUES(0,10),(1,20),(1,0),(NULL,NULL); SELECT completed,COUNT(*),COALESCE(SUM(pages),0) FROM r GROUP BY completed ORDER BY completed;실행 결과
NULL 1 0 0 1 10 1 2 20
확인 문제
실습
completed별 건수와 총 쪽수를 반환합니다. 열 순서는 completed·COUNT(*)·COALESCE(SUM(pages),0)이며 completed 오름차순입니다. 빈 표는 0행이고 NULL 상태는 별도 그룹입니다.
모범 답안
SELECT completed, COUNT(*), COALESCE(SUM(pages), 0) FROM reading_records GROUP BY completed ORDER BY completed;
더 읽기
면접 질문
- 빈 표의 집계와 완료 상태별 집계의 결과 차이를 설명합니다.