분석 SQL — 윈도우 함수 · 누계 · 기간 비교 · 소계 · 피벗

순위, 누계와 이동 평균, 전월·전년 비교, ROLLUP 소계, 조건부 집계 피벗까지 SAP 리포트의 분석 문법을 익힙니다.

1. 순위 매기기 — ROW_NUMBER · RANK · DENSE_RANK

윈도우 함수는 행을 줄이지 않고 각 행 옆에 계산 결과를 붙입니다.

SELECT KUNNR, SUM(NETWR) AS SALES,
       RANK() OVER (ORDER BY SUM(NETWR) DESC) AS SALES_RANK
FROM VBAK
GROUP BY KUNNR;
함수같은 값(동점)일 때
ROW_NUMBER()동점이어도 1, 2, 3 처럼 모두 다른 번호
RANK()동점은 같은 순위, 다음 순위는 건너뜀 (1, 1, 3)
DENSE_RANK()동점은 같은 순위, 건너뛰지 않음 (1, 1, 2)
NTILE(n)정렬 순서대로 n 개 묶음으로 나눔
  • PARTITION BY 는 묶음마다 순위를 다시 매깁니다. 없으면 전체가 한 묶음입니다.
  • 그룹별 상위 N 은 윈도우 함수를 파생 테이블에 넣고 바깥에서 번호를 거릅니다. WHERE 에 윈도우 함수를 바로 쓸 수 없습니다.
  • 동점 순서가 결과에 영향을 주면 ORDER BY 에 행 번호 같은 타이브레이커를 추가하세요.

문제

  1. 화성 공장 구매 품목 금액 상위 5위 (초급)
  2. 고객별 2025년 상위 두 금액대 표준 오더 (중급)
  3. 플랜트별 판매 문서 건수 상위 3단계 자재 (중급)
  4. 고객별 2025년 두 번째로 이른 거래 월의 표준 오더 (중급)
  5. 고객별 최근 두 문서 일자의 표준 오더 (중급)
  6. 자재 그룹별 수주 금액 2위 이내 자재 (중급)
  7. 판매 사무소별 상위 고객과 매출 비중 (고급)
  8. 상위 고객의 2024·2025년 매출 순위 변동 (고급)

2. 누계와 이동 평균 — SUM() OVER

집계 함수에 OVER (ORDER BY …) 를 붙이면 누계·이동 집계가 됩니다.

SELECT TO_VARCHAR(FKDAT, 'YYYY-MM') AS YM,
       SUM(NETWR) AS SALES,
       SUM(SUM(NETWR)) OVER (ORDER BY TO_VARCHAR(FKDAT, 'YYYY-MM')) AS RUNNING_TOTAL,
       AVG(SUM(NETWR)) OVER (ORDER BY TO_VARCHAR(FKDAT, 'YYYY-MM')
                             ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MOVING_AVG_3
FROM VBRK
WHERE FKDAT BETWEEN '20250101' AND '20251231'
GROUP BY TO_VARCHAR(FKDAT, 'YYYY-MM');
  • ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 는 "현재 행과 앞의 두 행", 곧 3행 이동 평균입니다.
  • 기본 프레임은 ORDER BY 값이 같은 행을 함께 묶습니다. 월 단위처럼 동점이 없으면 문제가 없지만, 명시하는 편이 안전합니다.
  • 누계는 월별로 먼저 집계한 뒤 그 위에 윈도우 함수를 적용합니다. 원본 행에 바로 적용하면 문서 건수만큼 누적됩니다.
  • 연간 매출의 절반이 쌓인 달처럼 "누계가 기준을 넘는 첫 행"은 바깥 쿼리에서 조건을 겁니다.

문제

  1. 2024년 월별 입고 평가 금액과 연초 누계 (초급)
  2. 2025년 월별 청구 매출과 연초 누계 (중급)
  3. 월별 출하 수량의 3개월 이동 평균 (중급)
  4. 플랜트별 2025년 고객 출하 수량 누계 (중급)
  5. 고객별 연간 매출의 절반이 쌓인 달 (고급)
  6. 직전 3개월 평균 대비 월별 청구 금액 차이 (고급)

3. 기간 비교 — LAG · LEAD

LAG(값, n) 은 정렬 순서상 n 행 앞의 값을, LEAD(값, n) 은 n 행 뒤의 값을 가져옵니다(n 을 빼면 1).

SELECT YM, SALES,
       LAG(SALES) OVER (ORDER BY YM) AS PREV_SALES,
       ROUND((SALES - LAG(SALES) OVER (ORDER BY YM)) * 100
             / NULLIF(LAG(SALES) OVER (ORDER BY YM), 0), 2) AS GROWTH_PCT
FROM (
  SELECT TO_VARCHAR(FKDAT, 'YYYY-MM') AS YM, SUM(NETWR) AS SALES
  FROM VBRK
  WHERE FKDAT BETWEEN '20250101' AND '20251231'
  GROUP BY TO_VARCHAR(FKDAT, 'YYYY-MM')
);
  • LAG 은 행 기준입니다. 중간 달이 없으면 전월이 아니라 그 전 데이터 행과 비교하게 됩니다. 월 목록을 먼저 채운 뒤 비교하세요.
  • 전년 동월 비교는 12행 전 값을 쓰거나 연도·월 키로 셀프 조인합니다.
  • 증감률은 분모가 0 이 될 수 있으니 NULLIF/NDIV0 로 처리합니다.
  • 첫 행은 이전 값이 없어 NULL 입니다. 기본값이 필요하면 LAG(값, 1, 0).

문제

  1. 2025년 월별 청구 금액과 전월 대비 증감률 (중급)
  2. 플랜트별 분기 원화 청구 금액의 전년 동기 대비 증감 (중급)
  3. 2025년 월별 구매 금액의 전월·다음 달 비교 (중급)
  4. 고객별 최근 청구 송장과 직전 송장 금액 비교 (중급)
  5. 2025년 월별 청구 금액과 누계의 전년 동기 대비 (고급)
  6. 월별 구매 금액의 전년 동월 대비 증감률 (고급)
  7. 자재 그룹별 4분기 월 청구 금액의 전년 동월 대비 증감률 (고급)

4. 소계와 합계 — ROLLUP · GROUPING SETS

GROUP BY ROLLUP(a, b) 는 (a, b) 조합, a 소계, 전체 합계를 한 번에 냅니다. GROUPING SETS 는 필요한 조합을 직접 고릅니다.

SELECT WERKS, MATKL,
       GROUPING(WERKS) AS IS_TOTAL,
       SUM(NETWR) AS SALES
FROM VBRP
GROUP BY ROLLUP(WERKS, MATKL);
  • 소계·합계 행은 묶인 컬럼이 NULL 로 나옵니다. GROUPING(컬럼) 이 1 이면 그 컬럼이 집계 때문에 NULL 인 것입니다.
  • 실제 NULL 값과 소계 행을 구분하려면 반드시 GROUPING() 을 쓰세요. IFNULL 로 먼저 바꾸면 구분할 수 없습니다.
  • 소계 행을 빼고 상세만 보고 싶으면 HAVING GROUPING(컬럼) = 0 을 겁니다.
  • 화면 표시용 라벨은 CASE WHEN GROUPING(WERKS) = 1 THEN '전체' ELSE WERKS END 처럼 만듭니다.

문제

  1. 플랜트·저장 위치별 2025년 입고 라인 수와 금액 (소계·합계 포함) (초급)
  2. 플랜트·자재 그룹별 청구 순금액과 소계·총계 (중급)
  3. 플랜트별·이동 유형별 출고 금액을 한 표로 대사 (중급)
  4. 고객 국가별·분기별 청구 채권 합계와 총계를 한 표로 (중급)
  5. 분기·플랜트별 매출총이익률과 소계·총계, 총계 대비 비중 (고급)
  6. 자재 그룹별 상위 3위 판매처와 자재 그룹 소계·총계 (고급)

5. 행을 열로 — 조건부 집계 피벗

SUM(CASE WHEN … THEN 금액 ELSE 0 END) 로 범주별 값을 각각의 열로 만듭니다.

SELECT KUNNR,
       SUM(CASE WHEN QUARTER(FKDAT) = 1 THEN NETWR ELSE 0 END) AS Q1,
       SUM(CASE WHEN QUARTER(FKDAT) = 2 THEN NETWR ELSE 0 END) AS Q2,
       SUM(NETWR) AS TOTAL,
       ROUND(SUM(CASE WHEN QUARTER(FKDAT) = 1 THEN NETWR ELSE 0 END) * 100
             / NULLIF(SUM(NETWR), 0), 2) AS Q1_PCT
FROM VBRK
WHERE FKDAT BETWEEN '20250101' AND '20251231'
GROUP BY KUNNR;
  • 열의 범주를 고정해야 합니다. 데이터에 없는 범주도 열로 만들려면 CASE 에 ELSE 0 을 둡니다.
  • 전체 대비 비중은 같은 GROUP BY 안에서 SUM(SUM(금액)) OVER () 또는 전체 합계로 나눠 구합니다.
  • HANA 의 MAP 는 단순 코드 치환에 유용하지만, 집계 피벗은 CASE 가 분명합니다.
  • 분모가 0 인 그룹은 비중이 NULL 이 됩니다. 0 으로 보여야 하는지 정하세요.

문제

  1. 2025년 월별 송장·반품 대변 건수, 반품 비율과 금액 (초급)
  2. 2025년 월별 입고·생산 출고·출하 수량과 연간 대비 비중 (중급)
  3. 고객별 2025년 분기별 청구 금액과 연간 비중 (중급)
  4. 2025년 자재별 생산 출고 금액: 상·하반기와 전체 대비 비중 (중급)
  5. 고객별 미수금 연령 구간과 전체 대비 비중 (고급)
  6. 2025년 청구 건의 반제 소요 기간 구간별 금액과 고객 비중 (고급)
  7. 2025년 청산된 송장의 고객별 지급 지연 구간과 전체 대비 비중 (고급)