1. 왜 분위수 함수가 실무 분석의 핵심인가
매출 데이터, 응답 시간, 재고 회전율 같은 수치형 지표를 다룰 때 평균(AVG)만으로는 데이터의 진짜 모습을 놓치기 쉽습니다. 한 명의 초대형 고객이 전체 평균을 왜곡시키거나, 응답 시간의 상위 5%가 서비스 품질을 좌우하는 상황은 SAP HANA 기반 분석 환경에서 흔하게 마주칩니다. 이런 이유로 SLA 관리, 이상치 탐지, 성과 등급화, 위험 관리 등 실전 시나리오에서는 분위수(percentile)가 훨씬 유효한 지표가 됩니다.
SAP HANA는 SQL 표준을 따라 PERCENTILE_CONT와 PERCENTILE_DISC 두 가지 분위수 함수를 제공합니다. 이름만 보면 비슷하지만 계산 방식이 근본적으로 다르고, 잘못 선택하면 리포트 수치가 미묘하게 어긋나거나 감사 대응 시 곤란을 겪을 수 있습니다. 이 글은 두 함수의 내부 동작을 나눠서 정리하고, 매출 분석 시나리오에서 어떻게 선택·조합해야 하는지 구체적으로 다룹니다.
- 체크리스트: PERCENTILE_CONT의 선형 보간 방식 이해
- 체크리스트: PERCENTILE_DISC의 CDF 기반 실제값 선택 로직 이해
- 체크리스트: WITHIN GROUP 문법과 window 형태의 차이 구분
- 체크리스트: 성능 관점에서의 데이터 스캔·정렬 비용 파악
2. 이 글을 읽기 전 갖추면 좋은 배경
표준 SQL의 집계 함수(AVG, MIN, MAX)와 윈도우 함수 문법(OVER (PARTITION BY ... ORDER BY ...))을 다뤄본 경험이 있으면 이해가 훨씬 수월합니다. 통계 개념에서는 사분위수(Q1, Q2, Q3), 중앙값(median), 누적분포함수(CDF)에 대한 기초적 감각이 있으면 충분합니다. SAP HANA Cloud 또는 HANA 2.0 SPS05 이상 환경에 SQL을 실행할 수 있어야 하며, DBeaver, SAP HANA Database Explorer, hdbsql 중 어느 것을 써도 무방합니다.
3. 실습 환경과 준비물
이 글의 예제는 아래 환경을 전제로 작성했습니다. 버전에 따라 함수 지원 범위가 달라질 수 있으니 실제 시스템의 릴리스 노트를 함께 확인하는 것을 권장합니다.
- SAP HANA Cloud (2026 QRC 기준) 또는 SAP HANA 2.0 SPS05 이상 (일반적으로 두 함수 모두 지원)
- 클라이언트: SAP HANA Database Explorer, DBeaver 24.x, 또는 hdbsql
- 권한: 대상 스키마에 대한
SELECT권한, 실습용 테이블 생성 시CREATE TABLE - 샘플 도메인: 매출 주문(SalesOrder), 제품 카테고리(ProductCategory), 지역(Region)을 기반으로 한 판매 실적 테이블
- 선택: SAP Analytics Cloud, SAP Datasphere 연동 시 계산 뷰(Calculation View)의 SQL 스크립트 노드에서도 동일 구문 사용 가능
예제 데이터는 약 100만 건 규모의 SALES_ORDER_FACT 테이블을 가정합니다. 실제 프로젝트에서는 컬럼 스토어 테이블(row 대신 column)로 설계된 팩트 테이블이 일반적이며, HANA의 in-memory 컬럼 엔진과 궁합이 잘 맞습니다.
4. 핵심 개념: 두 분위수 함수의 계산 로직
분위수는 정렬된 값의 목록에서 특정 위치(예: 50%, 90%)에 있는 값을 뜻합니다. 문제는 데이터 개수가 짝수이거나 요청한 백분율이 정확히 데이터 포인트와 일치하지 않을 때입니다. 이 지점에서 두 함수의 접근 방식이 갈립니다.
비유로 이해하기 — 계단식 강의실을 떠올려 봅시다. 강의실 계단 중 "50% 지점 좌석"을 물었을 때,
PERCENTILE_DISC는 "실제로 존재하는 계단 중 50% 이상 위치한 첫 번째 계단"을 짚어줍니다. 반면PERCENTILE_CONT는 "두 계단 사이 어딘가"라며 가상의 좌표를 계산해서 반환합니다. 실제 자리에 앉으려면 DISC, 이상적인 위치를 좌표로 표현하려면 CONT가 자연스럽습니다.
수식으로 표현하면 다음과 같은 차이가 있습니다. N개의 정렬된 값 x1, x2, ..., xN이 있을 때 백분위수 p(0~1)에 대해
- PERCENTILE_CONT(p): 위치 인덱스 RN = 1 + p × (N - 1)을 계산한 뒤, RN의 정수부(FLOOR)와 소수부(CEIL)에 해당하는 두 값을 선형 보간합니다. 결과가 원본 데이터에 존재하지 않을 수 있습니다.
- PERCENTILE_DISC(p): 누적분포함수(CDF)를 훑으면서 CDF(x) >= p를 만족하는 가장 작은 실제 값 x를 반환합니다. 결과는 항상 원본 데이터에 존재합니다.
이 차이 때문에 짝수 개 데이터의 중앙값(P50)에서 두 함수의 결과가 갈라집니다. 예를 들어 {100, 200, 300, 400}에서 CONT는 (200+300)/2 = 250을 돌려주지만, DISC는 200(누적분포 0.5 이상 첫 값)을 돌려줍니다. 산업별로 채택 관행이 달라서 재무·회계 리포트에서는 실제 거래 금액을 보존하려는 DISC가, 통계·과학 분야에서는 부드러운 분포 추정을 위한 CONT가 흔히 선호됩니다.
HANA에서 두 함수는 집계 형태와 윈도우 형태 두 가지 문법으로 쓸 수 있습니다. 집계 형태는 WITHIN GROUP (ORDER BY col)를 필수로 요구하고, 윈도우 형태는 OVER (PARTITION BY ...) 절과 결합해 행마다 그룹의 분위수를 반환합니다.
5. 실전 예제 1단계: 기본 문법으로 P50/P90 구하기
먼저 판매 주문 팩트 테이블을 준비합니다. 지역별·카테고리별로 주문 금액이 다양하게 분포하도록 값을 넣었다고 가정합니다.
CREATE COLUMN TABLE SALES_ORDER_FACT (
ORDER_ID NVARCHAR(20) PRIMARY KEY,
ORDER_DATE DATE,
REGION_CODE NVARCHAR(4),
CATEGORY_CODE NVARCHAR(10),
NET_AMOUNT DECIMAL(15,2),
CURRENCY_CODE NVARCHAR(3)
);
-- 지역별 중앙값과 90분위 매출을 두 방식으로 비교
SELECT
REGION_CODE,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY NET_AMOUNT) AS MEDIAN_CONT,
PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY NET_AMOUNT) AS MEDIAN_DISC,
PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY NET_AMOUNT) AS P90_CONT,
PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY NET_AMOUNT) AS P90_DISC
FROM SALES_ORDER_FACT
GROUP BY REGION_CODE
ORDER BY REGION_CODE;
결과를 보면 지역 KR, JP, DE 각각에서 CONT와 DISC 값이 미세하게 다릅니다. 특히 데이터 수가 짝수인 지역에서는 중앙값 차이가 두드러집니다. 리포트 요구사항이 "실제 발생한 거래 금액을 기준으로 한다"면 DISC를, "이론적 중앙 지점을 추정한다"면 CONT를 채택합니다.
실전 예제 2단계: 윈도우 형태 + 이상치 태깅 + 오류 처리
실무에서는 개별 주문 행에 대해 "이 주문이 소속 지역의 상위 10% 안에 드는가"를 태깅해야 하는 경우가 많습니다. 이때 윈도우 형태를 사용합니다. 또한 NULL 처리, 통화 필터, 로깅용 뷰 구성까지 함께 고려합니다.
-- 지역별 P90을 각 행에 붙여 상위 그룹을 태깅
WITH FILTERED AS (
SELECT
ORDER_ID, REGION_CODE, CATEGORY_CODE, NET_AMOUNT
FROM SALES_ORDER_FACT
WHERE CURRENCY_CODE = 'USD'
AND NET_AMOUNT IS NOT NULL
AND ORDER_DATE BETWEEN ADD_MONTHS(CURRENT_DATE, -12) AND CURRENT_DATE
)
SELECT
ORDER_ID,
REGION_CODE,
NET_AMOUNT,
PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY NET_AMOUNT)
OVER (PARTITION BY REGION_CODE) AS REGION_P90_CONT,
PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY NET_AMOUNT)
OVER (PARTITION BY REGION_CODE) AS REGION_P90_DISC,
CASE
WHEN NET_AMOUNT >= PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY NET_AMOUNT)
OVER (PARTITION BY REGION_CODE)
THEN 'TOP_TIER'
ELSE 'STANDARD'
END AS TIER_FLAG
FROM FILTERED;
WITHIN GROUP 절 안의 ORDER BY NET_AMOUNT는 NULL을 자동으로 제외합니다. 만약 통화가 섞인 데이터를 그대로 넣으면 분위수 자체가 왜곡되므로 위 예처럼 사전에 통화·기간 필터를 적용하는 것이 안전합니다. 오류가 자주 나는 지점은 WITHIN GROUP을 빠뜨리고 그냥 OVER (PARTITION BY ...)만 쓰는 경우인데, HANA는 이때 문법 오류(SQL 257 또는 259 계열)를 반환합니다.
실전 예제 3단계: 프로덕션 등급 성능 최적화와 검증
대량 데이터에서 분위수를 자주 조회한다면 계산 결과를 스냅숏 테이블로 물질화(materialize)하거나 계산 뷰의 스크립트 노드로 감싸는 방식을 권장합니다. 아래는 일 단위 프리 컴퓨트 뷰의 예입니다.
-- 일자·지역별 분위수 스냅숏 (BI 대시보드용)
CREATE VIEW V_SALES_PERCENTILE_DAILY AS
SELECT
ORDER_DATE,
REGION_CODE,
COUNT(*) AS ORDER_COUNT,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY NET_AMOUNT) AS MEDIAN_CONT,
PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY NET_AMOUNT) AS MEDIAN_DISC,
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY NET_AMOUNT) AS P95_CONT,
PERCENTILE_DISC(0.95) WITHIN GROUP (ORDER BY NET_AMOUNT) AS P95_DISC
FROM SALES_ORDER_FACT
WHERE NET_AMOUNT IS NOT NULL
GROUP BY ORDER_DATE, REGION_CODE;
-- 실행 계획 확인 (컬럼 스캔·정렬 비용 점검)
EXPLAIN PLAN FOR
SELECT * FROM V_SALES_PERCENTILE_DAILY
WHERE ORDER_DATE = CURRENT_DATE - 1;
성능 관점에서는 정렬 비용이 지배적입니다. WITHIN GROUP은 그룹 내부 정렬을 수반하므로 파티션 수와 그룹당 카디널리티에 성능이 크게 좌우됩니다. 파티션 수가 많고 각 파티션이 작으면 병렬 처리 이점이 살아나지만, 반대로 대형 파티션 하나에 몰리면 임시 메모리와 CPU를 크게 씁니다. 실무에서는 ORDER_DATE, REGION_CODE에 파티셔닝(HASH/RANGE)을 적용하거나 NET_AMOUNT에 컬럼 스토어 딕셔너리 압축을 활용하는 방향이 일반적으로 권장됩니다.
테스트 관점에서는 두 함수의 결과가 규모가 커질수록 근접해지는지 확인해 보면 좋습니다. 데이터가 충분히 많고 연속형에 가까울수록 CONT ≈ DISC로 수렴합니다. 반대로 소규모 그룹에서만 두 값이 유의미하게 벌어지므로, 그룹 최소 크기 임계값을 정해두는 방어 로직도 유용합니다.
6. 자주 마주치는 실수와 트러블슈팅 FAQ
Q1. WITHIN GROUP 없이 쓰면 왜 오류가 나나요? PERCENTILE_CONT, PERCENTILE_DISC는 표준 SQL상 순서 지정 집계(ordered-set aggregate)로 분류됩니다. 어떤 컬럼을 기준으로 정렬할지 반드시 지정해야 하기 때문에 WITHIN GROUP (ORDER BY col)이 필수입니다. HANA는 이를 생략하면 SQL 문법 오류를 냅니다.
Q2. 짝수 개 데이터에서 중앙값이 이상해요. {10, 20}의 중앙값이 CONT는 15, DISC는 10입니다. 리포트 담당자와 "짝수 그룹의 P50 정의"를 먼저 합의하지 않으면 회계 감사 대응 시 재계산 요청이 반복될 수 있습니다. 일반적으로는 재무 지표에는 DISC, 통계 분석에는 CONT를 채택하는 사례가 많습니다.
Q3. 큰 팩트 테이블에서 쿼리가 너무 느립니다. 우선 EXPLAIN PLAN으로 정렬 노드의 비용을 확인하고, WHERE 절에서 필요한 기간·통화 범위를 최대한 좁혔는지 점검합니다. 반복 조회라면 앞서 소개한 스냅숏 뷰로 야간 배치에서 미리 집계해 두는 편이 성능과 일관성 모두에 유리합니다.
Q4. NULL이 결과에 어떻게 반영되나요? ORDER BY 대상 컬럼의 NULL은 계산에서 제외됩니다. 다만 사전 필터에서 IS NOT NULL을 명시하면 실행 계획을 더 좁힐 수 있어 성능·가독성 모두에 좋습니다.
Q5. Calculation View나 Datasphere에서도 쓸 수 있나요? SQL 스크립트 노드에서는 그대로 사용 가능하지만, 그래픽 노드의 집계 함수 목록에는 별도의 커스텀 표현식으로 넣어야 하는 경우가 있습니다. 팀 내 모델링 가이드를 확인하기를 권장합니다.
7. 확장 학습 로드맵
분위수 함수에 익숙해졌다면 다음 주제로 넓혀가면 좋습니다. NTILE, CUME_DIST, PERCENT_RANK 같은 순위 기반 함수는 분위수 개념과 짝을 이루어 세그멘테이션 분석에 강점을 보입니다. 또한 SAP HANA의 Predictive Analysis Library(PAL)에서 제공하는 통계 함수와 결합하면 이상치 탐지·수요 예측까지 확장할 수 있습니다. Datasphere와 SAP Analytics Cloud에서는 계산 뷰의 분위수 계산 결과를 라이브 커넥션으로 시각화해 비즈니스 사용자에게 곧바로 전달할 수 있습니다.
8. 더 파고들 때 도움이 되는 링크
댓글 0
아직 댓글이 없습니다.