SUM(amount) OVER (ORDER BY 날짜)로 누적합을 구했는데 같은 날짜의 첫 거래부터 합계가 한꺼번에 늘었다면, 동일한 정렬값의 행이 같은 윈도 범위에 들어가는지 확인해야 합니다. 거래마다 차례로 누적하려면 고유한 정렬 기준과 ROWS 범위를 명시합니다.
아래 실습은 SQLite 3.53.1에서 검증했습니다. 같은 날짜에 두 거래를 넣고 거래별 누적과 날짜별 누적을 비교합니다. 다른 DB에서는 윈도 함수와 기본 프레임 지원을 해당 버전에서 확인하세요.

1. 같은 날짜에 거래가 두 건 있는 데이터
CREATE TABLE sales (
id INTEGER PRIMARY KEY,
sold_on TEXT NOT NULL,
amount INTEGER NOT NULL
);
INSERT INTO sales VALUES
(1, '2026-09-01', 100),
(2, '2026-09-01', 50),
(3, '2026-09-02', 30),
(4, '2026-09-03', 20);
첫날 거래는 100과 50, 다음 날은 30, 그다음 날은 20입니다. 전체 합계는 200입니다. 날짜는 이 예제에서 정렬 가능한 YYYY-MM-DD 문자열로 저장했으며, 실제 시스템의 날짜·시간 열과 시간대 규칙은 별도로 정해야 합니다.
2. 기본 누적과 거래별 누적을 나란히 실행하기
SELECT
id, sold_on, amount,
SUM(amount) OVER (
ORDER BY sold_on
) AS default_total,
SUM(amount) OVER (
ORDER BY sold_on, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS row_total
FROM sales
ORDER BY sold_on, id;
| id | 날짜 | 금액 | default_total | row_total |
| 1 | 2026-09-01 | 100 | 150 | 100 |
| 2 | 2026-09-01 | 50 | 150 | 150 |
| 3 | 2026-09-02 | 30 | 180 | 180 |
| 4 | 2026-09-03 | 20 | 200 | 200 |
날짜만 정렬한 default_total은 첫 행에서 150이 나옵니다. SQLite의 기본 프레임은 파티션 시작부터 현재 행과 같은 정렬값의 행까지 포함하므로 첫날 두 거래가 함께 계산됩니다. 이 결과는 같은 날짜까지의 합계를 나타내므로 질문에 따라 올바른 결과일 수 있습니다.
row_total은 날짜와 고유 id로 순서를 정하고 시작부터 현재 행까지의 ROWS 범위를 명시했습니다. 따라서 100, 150, 180, 200으로 거래 하나씩 누적됩니다.

3. ROWS만 추가하면 동점 순서까지 정해질까?
ROWS는 범위를 행 단위로 잡습니다. 그러나 날짜만으로 정렬하면 같은 날짜 안에서 어느 거래가 먼저인지 명확하지 않습니다. 화면의 마지막 ORDER BY만 바꾸어도 윈도 계산 순서를 정한 것이라고 볼 수 없습니다.
거래별 누적이 필요하면 OVER 안에 업무상 순서를 나타내는 열을 넣고, 여전히 동점이 있으면 고유 id 같은 보조 기준을 추가하세요. 이 예제의 id 순서는 실습용 기준입니다. 실제 거래 순서가 결제 시각으로 정해진다면 그 시각과 고유 id를 함께 검토해야 합니다.
4. 날짜별 한 줄 보고서는 먼저 날짜별로 합치기
원하는 결과가 거래 목록이 아니라 날짜별 한 줄이라면 원본 거래에 누적합을 붙인 뒤 중복 날짜를 무작정 지우지 마세요. 먼저 날짜별 합계를 만든 다음 그 결과에 누적합을 적용하면 계산 단위가 분명해집니다.
WITH daily AS (
SELECT sold_on, SUM(amount) AS day_amount
FROM sales
GROUP BY sold_on
)
SELECT
sold_on, day_amount,
SUM(day_amount) OVER (
ORDER BY sold_on
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total
FROM daily
ORDER BY sold_on;
| 날짜 | day_amount | running_total |
| 2026-09-01 | 150 | 150 |
| 2026-09-02 | 30 | 180 |
| 2026-09-03 | 20 | 200 |
이 쿼리는 거래가 있는 날짜만 표시합니다. 거래가 없는 날도 포함한 달력 보고서가 필요하면 날짜 목록과 결합하는 단계가 추가로 필요합니다. 계정별로 누적해야 한다면 날짜별 집계 단위와 PARTITION BY에 계정 기준을 함께 반영하세요.
5. 결과를 점검하는 세 가지 질문
- 한 줄이 무엇인가? 거래 한 건인지, 하루인지, 계정별 하루인지 먼저 정합니다.
- 동점의 순서는 무엇인가? 거래별 누적이라면 OVER 안의 정렬 기준이 순서를 확정하는지 확인합니다.
- 마지막 누적합이 맞는가? 이 예제에서는 원본 합계 200과 마지막 누적합 200을 대조합니다.
예제의 금액 열은 결측을 허용하지 않습니다. 결측이나 환불 금액을 포함하는 실제 자료는 입력 규칙과 합계의 의미도 확인해야 합니다. RANGE의 값 간격 프레임이나 DB별 특수 문법은 이번 비교 범위에 포함하지 않았습니다.
핵심 정리: 같은 날짜에서 누적합이 뛰는 현상은 동점 행을 묶는 계산 범위와 관련될 수 있습니다. 거래별 누적은 확정된 행 순서와 ROWS, 날짜별 보고서는 날짜별 선집계로 목적을 분명히 하세요.
윈도 정렬에서 동점 기준을 더 확인하려면 고객별 최신 주문의 ROW_NUMBER와 동점 처리를 이어서 읽어 보세요.
'데이터베이스(SQL) > 데이터베이스 개념' 카테고리의 다른 글
| SQL 조건부 집계가 전체 행을 세는 이유: COUNT·SUM·CASE 실습 (0) | 2026.10.07 |
|---|---|
| SQL 평균의 평균이 틀리는 이유: SUM·COUNT로 가중 평균 계산 (0) | 2026.10.06 |
| SQL JOIN 후 SUM이 커지는 이유: 상세 테이블 선집계로 중복 막기 (0) | 2026.10.03 |
| PostgreSQL ON CONFLICT로 중복 INSERT 처리하기: 유니크 제약과 RETURNING (0) | 2026.09.29 |
| SQL UNION과 UNION ALL 차이: 중복 제거·정렬·성능 선택 기준 (0) | 2026.09.22 |
댓글