공공부하자개발 · 영어 학습 노트
SQL
SQL 고급분석 함수·계층·피벗·집계 확장0/10 완료
  • 01순위 분석 함수
  • 02집계 분석 함수와 윈도 프레임
  • 03행 비교 분석 함수
  • 04계층 쿼리: CONNECT BY 와 재귀 CTE
  • 05행과 열 바꾸기
  • 06소계와 총계
  • 07WITH 절(CTE)로 쿼리 구조화
  • 08MERGE 와 UPSERT
  • 09정규식 함수
  • 10다른 테이블 기준으로 수정·삭제
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › SQL 고급 › 02 / 10

집계 분석 함수와 윈도 프레임

SUM OVER, 누적합, 이동 평균, 비율
섹션 6진행 0 / 10
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6정리‹ 이전다음 ›

4. 응용 변형 예제

변형 1: 기본 프레임의 함정, 같은 날짜 행이 함께 더해진다

02 데이터에서 id 3·4 는 같은 날짜(2024-01-07)입니다. ORDER BY ord_date 만 쓰고 프레임을 생략한 누적합을 봅니다.

sql
SELECT
       id
     , ord_date
     , amt
     , SUM(amt) OVER (ORDER BY ord_date) AS default_frame
  FROM orders
 ORDER BY ord_date, id;
text
ID | ORD_DATE   | AMT | DEFAULT_FRAME
---+------------+-----+--------------
1  | 2024-01-05 | 100 | 100
2  | 2024-01-06 | 200 | 300
3  | 2024-01-07 | 300 | 1000
4  | 2024-01-07 | 400 | 1000
5  | 2024-01-08 | 500 | 1500
6  | 2024-01-09 | 600 | 2100
(6행)

id 3 의 누적합이 600(100+200+300)이 아니라 1000 입니다. 기본 프레임이 RANGE 이므로 정렬 값이 같은 id 4 의 400 까지 한꺼번에 더했기 때문입니다.

주의

오류도 경고도 없이 id 3 의 누적합만 "다음 행"까지 포함합니다. ORDER BY 만 쓴 누적합은 정렬 기준이 유일한지 확인하세요. 유일하지 않으면 동점 행이 같은 값을 받는 것이 표준 동작이고 Oracle·Tibero·MySQL·MSSQL 이 모두 같습니다.

변형 2: ROWS 로 고치기

sql
SELECT
       id
     , ord_date
     , amt
     , SUM(amt) OVER (ORDER BY ord_date, id
                      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS rows_frame
  FROM orders
 ORDER BY ord_date, id;
text
ID | ORD_DATE   | AMT | ROWS_FRAME
---+------------+-----+-----------
1  | 2024-01-05 | 100 | 100
2  | 2024-01-06 | 200 | 300
3  | 2024-01-07 | 300 | 600
4  | 2024-01-07 | 400 | 1000
5  | 2024-01-08 | 500 | 1500
6  | 2024-01-09 | 600 | 2100
(6행)

이제 id 3 은 600, id 4 는 1000 으로 행마다 하나씩 쌓입니다. ORDER BY ord_date, id 로 정렬을 유일하게 만들었기 때문에 동점 행 사이의 순서(id 3 이 먼저)도 고정됩니다.

ROWS 프레임은 정렬이 유일하지 않으면 동점 행의 순서를 DB 가 임의로 정합니다. ROWS 를 쓸 때는 ORDER BY 를 유일하게 만드는 습관을 함께 둡니다.

변형 3: ROWS 와 RANGE 를 나란히 비교

sql
SELECT
       id
     , ord_date
     , amt
     , SUM(amt) OVER (ORDER BY ord_date
                      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS rows_sum
     , SUM(amt) OVER (ORDER BY ord_date
                      RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS range_sum
  FROM orders
 ORDER BY ord_date, id;
text
ID | ORD_DATE   | AMT | ROWS_SUM | RANGE_SUM
---+------------+-----+----------+----------
1  | 2024-01-05 | 100 | 100      | 100
2  | 2024-01-06 | 200 | 300      | 300
3  | 2024-01-07 | 300 | 600      | 1000
4  | 2024-01-07 | 400 | 1000     | 1000
5  | 2024-01-08 | 500 | 1500     | 1500
6  | 2024-01-09 | 600 | 2100     | 2100
(6행)

동점이 없는 행(id 1·2·5·6)은 두 값이 같고, 같은 날짜인 id 3·4 에서만 RANGE 가 1000 을 함께 받습니다. 정렬 값이 모두 다르면 ROWS 와 RANGE 는 같은 결과입니다.

이 쿼리는 ORDER BY ord_date 만 썼으므로 ROWS_SUM 열의 id 3·4 순서는 실제 DB 에서 바뀔 수 있습니다. 비교용 예제이니 그대로 쓰지 말고 변형 2 처럼 id 를 덧붙입니다.

변형 4: UNBOUNDED FOLLOWING 으로 전체 합

sql
SELECT
       id
     , amt
     , SUM(amt) OVER (ORDER BY id
                      ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS total_amt
  FROM orders
 ORDER BY id;
text
ID | AMT | TOTAL_AMT
---+-----+----------
1  | 100 | 2100
2  | 200 | 2100
3  | 300 | 2100
4  | 400 | 2100
5  | 500 | 2100
6  | 600 | 2100
(6행)

프레임을 첫 행부터 마지막 행까지 넓히면 SUM(amt) OVER () 와 같은 전체 합입니다. 프레임의 양 끝을 직접 고르는 감각은 프레임에 영향을 받는 FIRST_VALUE·LAST_VALUE 를 다음 레슨에서 쓸 때 필요합니다.

팁

정렬 기준에 유일한 값이 없는 누적합은 ORDER BY ord_date, id 처럼 id 를 덧붙이는 방법과 ROWS 프레임 명시, 두 가지로 고칩니다. 실무 리포트에서는 둘을 함께 써서 프레임 의도를 코드에 남기는 편이 안전합니다.

변형 5: DB 별 문법 비교

누적합과 이동 평균은 세 DB 가 같은 문법입니다. 03 파일은 같은 문장을 SET MODE 별로 실행했고 결과가 세 모드에서 같았습니다.

sql
-- Oracle · Tibero, MySQL 8.0, MSSQL 2012 이상 모두 같은 문장
SELECT
       id
     , amt
     , SUM(amt) OVER (ORDER BY ord_date) AS running_total
     , AVG(amt) OVER (ORDER BY ord_date
                      ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg3
  FROM orders
 ORDER BY id;
text
ID | AMT | RUNNING_TOTAL | MOVING_AVG3
---+-----+---------------+------------
1  | 100 | 100           | 100.0
2  | 200 | 300           | 150.0
3  | 300 | 600           | 200.0
4  | 400 | 1000          | 300.0
5  | 500 | 1500          | 400.0
(5행)

H2 는 방언을 흉내 낼 뿐이라 이 결과가 실제 DB 동작의 근거는 아닙니다. 다만 위 문법은 2.4절의 버전 조건(MySQL 8.0, MSSQL 2012 이상)을 만족하면 세 DB 에서 같습니다.

MSSQL 2008 이하에서는 SUM(amt) OVER (ORDER BY ...) 자체가 오류이므로 상관 서브쿼리로 누적합을 만들어야 합니다.

변형 6: 비율, RATIO_TO_REPORT 와 공통 방식

Oracle·Tibero 전용 함수인 RATIO_TO_REPORT 는 "현재 값 / 전체 합"을 바로 돌려줍니다.

sql
-- Oracle · Tibero 전용
SELECT
       id
     , amt
     , ROUND(RATIO_TO_REPORT(amt) OVER () * 100, 1) AS pct
  FROM orders
 ORDER BY id;
text
ID | AMT | PCT
---+-----+-----
1  | 100 | 6.7
2  | 200 | 13.3
3  | 300 | 20.0
4  | 400 | 26.7
5  | 500 | 33.3
(5행)

MySQL 과 MSSQL 에는 이 함수가 없으니, 네 DB 공통인 SUM OVER () 방식을 씁니다. 결과는 위와 같습니다.

sql
-- 공통(Oracle · Tibero · MySQL · MSSQL)
SELECT
       id
     , amt
     , ROUND(amt * 100.0 / SUM(amt) OVER (), 1) AS pct
  FROM orders
 ORDER BY id;
text
ID | AMT | PCT
---+-----+-----
1  | 100 | 6.7
2  | 200 | 13.3
3  | 300 | 20.0
4  | 400 | 26.7
5  | 500 | 33.3
(5행)

H2 2.3.232 는 RATIO_TO_REPORT 를 실행하지만, 이 함수는 Oracle·Tibero 전용이므로 다른 DB 로 옮길 코드라면 공통 방식을 쓰세요.

응용 변형 예제
  • 변형 1: 기본 프레임의 함정, 같은 날짜 행이 함께 더해진다
  • 변형 2: ROWS 로 고치기
  • 변형 3: ROWS 와 RANGE 를 나란히 비교
  • 변형 4: UNBOUNDED FOLLOWING 으로 전체 합
  • 변형 5: DB 별 문법 비교
  • 변형 6: 비율, RATIO_TO_REPORT 와 공통 방식
이전 섹션3 코드 예제4 / 6다음 섹션5 자주 하는 실수 (Tip)