ABAP SQL 신기능 완전 가이드 — Window Functions, CTE, 계층 쿼리까지



ABAP SQL 신기능 완전 가이드 — Window Functions, CTE, 계층 쿼리까지
1. 개요 및 학습 목표
ABAP SQL은 7.51 릴리스 이후 대폭 강화되어, 기존에 애플리케이션 레이어에서 루프로 처리하던 복잡한 집계·순위·계층 연산을 데이터베이스 레벨에서 선언적으로 수행할 수 있게 되었습니다. 이 가이드에서는 CTE(Common Table Expressions), Window Functions, 그리고 계층 쿼리(WITH HIERARCHY)를 실무 코드와 함께 단계별로 학습합니다.
학습 완료 후 체크리스트:
- WITH 절로 다단계 CTE를 작성하고 JOIN할 수 있다
- ROW_NUMBER, RANK, LEAD/LAG 등 Window Function을 PARTITION BY와 결합할 수 있다
- NTILE, FIRST_VALUE, LAST_VALUE를 활용한 분석 쿼리를 작성할 수 있다
- CTE와 Window Function을 결합하여 프로덕션 수준 쿼리를 구성할 수 있다
- WITH HIERARCHY를 이용한 계층 쿼리의 기본 구조를 이해한다
- 성능 최적화 포인트와 흔한 실수를 식별할 수 있다
2. 선수 지식
이 튜토리얼을 효과적으로 따라가려면 다음 사항에 대한 기본 이해가 필요합니다.
- ABAP Open SQL의 SELECT, INSERT, UPDATE 기본 구문
- INNER JOIN / LEFT OUTER JOIN 사용 경험
- GROUP BY, HAVING 등 집계 구문에 대한 이해
- 인라인 선언(
DATA(),@DATA()) 사용법
3. 환경 / 버전 / 준비물
ABAP SQL의 고급 기능은 릴리스마다 점진적으로 추가되었습니다. 아래 표를 참고하여 사용 중인 시스템의 지원 범위를 확인하세요.
| 기능 | 최소 요구 버전 | 비고 |
|---|---|---|
| CTE (WITH 절) | ABAP 7.51 | 기본 CTE, 다단계 체이닝 |
| ROW_NUMBER, RANK, DENSE_RANK | ABAP 7.51 | Ranking Functions |
| LEAD, LAG | ABAP 7.54 | Value Functions, 디폴트 값 지정 가능 |
| FIRST_VALUE, LAST_VALUE | ABAP 7.54 | 프레임 경계 지정 권장 |
| NTILE | ABAP 7.54 | 균등 분할 버켓팅 |
| WITH HIERARCHY | ABAP 7.54 | 계층 쿼리, 부모-자식 모델 |
준비물:
- SAP NetWeaver AS ABAP 7.51 이상 또는 SAP BTP ABAP Environment
- ADT(ABAP Development Tools) — Eclipse 기반 개발 환경 권장
- SQL Monitor(트랜잭션 SQLM) 접근 권한 — 성능 분석에 활용
4. 핵심 개념
CTE — 임시 이름표를 붙인 서브쿼리
CTE는 WITH 키워드로 시작하는 이름 있는 임시 뷰입니다. 비유하자면, 긴 보고서를 작성할 때 중간 결과를 별도의 메모 카드에 적어두고, 최종 보고서에서 해당 카드를 참조하는 방식과 유사합니다. 각 CTE는 데이터베이스 접근 기간 동안만 존재하며, 후속 쿼리에서 테이블처럼 참조할 수 있습니다.
CDS View vs CTE: CTE는 한 번의 SQL 실행 내에서만 유효한 임시 뷰입니다. 여러 프로그램에서 재사용해야 한다면 CDS View가 더 적합합니다. 단일 리포트 내 복잡한 집계에는 CTE가 코드 가독성 면에서 유리합니다.
Window Functions — 행을 유지한 채로 분석하기
일반 집계(GROUP BY)는 행을 그룹으로 축소합니다. 반면 Window Function은 원본 행을 그대로 유지하면서 각 행에 분석 결과를 덧붙입니다. 비유하자면, 교실에서 학생들이 줄 서 있을 때 GROUP BY는 "반별 평균 키"만 남기지만, Window Function은 "각 학생 옆에 반 평균 키 스티커를 붙여주는 것"입니다.
Window Function은 세 가지 범주로 나뉩니다.
- 집계 윈도우: SUM, AVG, MAX, MIN, COUNT — 기존 집계 함수를 OVER 절과 함께 사용
- 순위 함수: ROW_NUMBER, RANK, DENSE_RANK, NTILE — 파티션 내 순위/버켓 부여
- 값 함수: LEAD, LAG, FIRST_VALUE, LAST_VALUE — 현재 행 기준 전후/경계 값 참조
핵심 구문 요소는 다음과 같습니다.
- OVER( ) — Window Function임을 선언
- PARTITION BY — 논리적 그룹(파티션) 경계를 정의
- ORDER BY — 파티션 내 행의 정렬 순서를 지정
- ROWS BETWEEN — 현재 행 기준으로 연산 범위(프레임)를 한정
계층 쿼리 — 트리 구조를 SQL로 펼치기
조직도, BOM(자재 명세서), 계정 코드 체계처럼 부모-자식 관계를 갖는 데이터를 다룰 때, 기존에는 ABAP 루프로 재귀 탐색해야 했습니다. ABAP 7.54부터 WITH HIERARCHY 구문을 사용하면 선언적으로 계층 구조를 탐색할 수 있습니다. 참고로 SAP HANA의 WITH 절은 재귀 쿼리를 지원하지 않으므로, 계층 데이터에는 이 전용 구문을 사용하는 것이 일반적입니다.
5. 실전 코드 3단계
1단계: CTE 기본 — WITH, 다단계 집계, JOIN
판매 오더 헤더(VBAK)와 아이템(VBAP)을 CTE로 분리 집계한 뒤 JOIN하는 패턴입니다. 복잡한 서브쿼리를 중첩하는 대신, 각 단계를 명명된 CTE로 분리하여 가독성을 높입니다.
" ABAP 7.51+: CTE 기본 — 판매 오더 분석
DATA lv_from_date TYPE dats VALUE '20260101'.
WITH
" 1단계 CTE: 기간 내 판매 오더 헤더 필터링
+cte_sales AS (
SELECT FROM vbak
FIELDS vbeln, kunnr, erdat, auart
WHERE erdat >= @lv_from_date
),
" 2단계 CTE: 아이템별 금액·수량 집계
+cte_items AS (
SELECT FROM vbap
FIELDS vbeln,
SUM( netwr ) AS total_amount,
COUNT( * ) AS item_count
GROUP BY vbeln
),
" 3단계 CTE: 고객별 주문 건수 집계
+cte_cust_summary AS (
SELECT FROM +cte_sales AS s
INNER JOIN +cte_items AS i ON i~vbeln = s~vbeln
FIELDS s~kunnr,
COUNT( * ) AS order_count,
SUM( i~total_amount ) AS cust_total
GROUP BY s~kunnr
)
" 최종 SELECT: CTE 결과를 조합
SELECT FROM +cte_sales AS s
INNER JOIN +cte_items AS i ON i~vbeln = s~vbeln
LEFT OUTER JOIN +cte_cust_summary AS c ON c~kunnr = s~kunnr
FIELDS s~vbeln,
s~kunnr,
s~erdat,
i~total_amount,
i~item_count,
c~order_count,
c~cust_total
INTO TABLE @DATA(lt_result).
cl_demo_output=>display( lt_result ).
포인트: CTE 이름 앞에 + 접두사를 붙이는 것이 ABAP SQL 구문 규칙입니다. 호스트 변수(@lv_from_date)를 활용하여 동적으로 필터링 조건을 전달합니다. 각 CTE는 이전 CTE를 참조할 수 있어 단계별 집계가 자연스럽게 이어집니다.
2단계: Window Functions — ROW_NUMBER, RANK, LEAD/LAG, 집계 윈도우
직원 테이블에서 부서별 급여 순위를 매기고, 전후 행 값을 비교하는 예제입니다.
" ABAP 7.54+: Window Functions 종합 예제
SELECT FROM zhr_employees
FIELDS
empid,
department,
name,
salary,
" 부서별 급여 내림차순 일련번호 (동일 값이어도 고유 번호)
ROW_NUMBER( ) OVER(
PARTITION BY department
ORDER BY salary DESCENDING
) AS row_num,
" 부서별 급여 순위 (동일 값이면 같은 순위, 다음 순위 건너뜀)
RANK( ) OVER(
PARTITION BY department
ORDER BY salary DESCENDING
) AS salary_rank,
" 부서별 급여 순위 (동일 값이면 같은 순위, 다음 순위 연속)
DENSE_RANK( ) OVER(
PARTITION BY department
ORDER BY salary DESCENDING
) AS dense_rnk,
" 같은 파티션 내 다음 행의 급여 (없으면 0)
LEAD( salary, 1, 0 ) OVER(
PARTITION BY department
ORDER BY salary DESCENDING
) AS next_salary,
" 같은 파티션 내 이전 행의 급여 (없으면 0)
LAG( salary, 1, 0 ) OVER(
PARTITION BY department
ORDER BY salary DESCENDING
) AS prev_salary,
" 부서별 평균 급여 (행 축소 없이 모든 행에 표시)
AVG( salary ) OVER(
PARTITION BY department
) AS dept_avg_salary,
" 부서별 급여 합계
SUM( salary ) OVER(
PARTITION BY department
) AS dept_total_salary
INTO TABLE @DATA(lt_emp_analysis).
" 결과: 각 직원 행에 순위, 전후 급여, 부서 평균이 함께 표시됨
cl_demo_output=>display( lt_emp_analysis ).
포인트: LEAD와 LAG의 두 번째 인자는 오프셋(기본 1), 세 번째 인자는 범위 밖일 때의 디폴트 값입니다. 집계 윈도우(AVG, SUM)에 PARTITION BY만 지정하고 ORDER BY를 생략하면 파티션 전체를 대상으로 연산합니다.
3단계: 프로덕션 — CTE + Window 결합, NTILE, FIRST_VALUE/LAST_VALUE, 계층 쿼리
CTE로 데이터를 준비한 뒤 Window Function으로 분석하는 결합 패턴과, 계층 쿼리 기본 구조를 보여줍니다.
" ABAP 7.54+: CTE + Window Function 결합 — 고객 매출 분석
WITH
+cte_monthly AS (
SELECT FROM vbrk AS h
INNER JOIN vbrp AS i ON i~vbeln = h~vbeln
FIELDS h~kunag AS customer,
LEFT( h~fkdat, 6 ) AS yearmonth,
SUM( i~netwr ) AS monthly_revenue
WHERE h~fkdat >= '20260101'
GROUP BY h~kunag, LEFT( h~fkdat, 6 )
)
SELECT FROM +cte_monthly
FIELDS
customer,
yearmonth,
monthly_revenue,
" 고객별 매출 5분위 그룹 (1=최저, 5=최고)
NTILE( 5 ) OVER(
PARTITION BY customer
ORDER BY monthly_revenue
) AS revenue_quintile,
" 고객별 첫 달(최소 매출 월) 매출액
FIRST_VALUE( monthly_revenue ) OVER(
PARTITION BY customer
ORDER BY yearmonth
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS first_month_rev,
" 고객별 최근 달(최대 매출 월) 매출액
LAST_VALUE( monthly_revenue ) OVER(
PARTITION BY customer
ORDER BY yearmonth
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS latest_month_rev,
" 전월 대비 매출 변동 (현재 - 이전 월)
monthly_revenue - LAG( monthly_revenue, 1, 0 ) OVER(
PARTITION BY customer
ORDER BY yearmonth
) AS mom_change
INTO TABLE @DATA(lt_analysis).
cl_demo_output=>display( lt_analysis ).
LAST_VALUE 주의: LAST_VALUE를 사용할 때 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING을 명시하지 않으면, 기본 프레임이 현재 행까지만 적용되어 기대와 다른 결과가 나올 수 있습니다.
" ABAP 7.54+: WITH HIERARCHY 기본 구조 — 조직 계층 탐색
WITH
+org_source AS (
SELECT FROM zorg_unit
FIELDS org_id, parent_id, org_name, manager
),
+org_hierarchy AS (
HIERARCHY(
SOURCE +org_source
CHILD TO PARENT ASSOCIATION _relat
START WHERE parent_id IS INITIAL
SIBLINGS ORDER BY org_name
)
)
SELECT FROM +org_hierarchy
FIELDS org_id,
parent_id,
org_name,
manager,
hierarchy_rank,
hierarchy_tree_size,
hierarchy_level
INTO TABLE @DATA(lt_org_tree).
cl_demo_output=>display( lt_org_tree ).
성능 최적화 팁:
- CTE 체이닝은 일반적으로 8개 이내로 유지하세요. 과도한 체이닝은 옵티마이저 부담을 키울 수 있습니다.
- Window Function의 PARTITION BY 컬럼에 인덱스가 있으면 성능에 유리합니다.
- SQL Monitor(트랜잭션 SQLM)로 실행 계획과 CTE 체인 효율성을 확인하는 습관을 들이세요.
- 대량 데이터셋에서 불필요한 ROWS BETWEEN 확장은 피하고, 필요한 프레임만 지정하세요.
6. 흔한 실수 / 트러블슈팅
FAQ 1: "CTE를 사용했는데 구문 오류가 발생합니다."
시스템이 ABAP 7.51 미만일 가능성이 높습니다. sy-saprl 값을 확인하세요. 또한 CTE 이름에 + 접두사를 빠뜨리거나, 마지막 CTE 뒤에 쉼표를 넣은 경우에도 구문 오류가 발생합니다. WITH 블록의 마지막 CTE 정의와 최종 SELECT 사이에는 쉼표 없이 바로 연결되어야 합니다.
FAQ 2: "LAST_VALUE가 항상 현재 행의 값을 반환합니다."
Window Function의 기본 프레임은 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW입니다. 따라서 LAST_VALUE는 현재 행까지만 보고 "마지막 값"을 반환하게 됩니다. 파티션 전체의 마지막 값을 얻으려면 반드시 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING을 명시해야 합니다.
FAQ 3: "LEAD/LAG를 사용했더니 덤프가 발생합니다."
LEAD와 LAG는 ABAP 7.54 이상에서만 지원됩니다. 7.51~7.53 시스템에서는 사용할 수 없으며, ROW_NUMBER 기반의 셀프 조인으로 우회해야 합니다. 또한 LEAD/LAG의 디폴트 값 타입이 대상 컬럼과 일치하는지 확인하세요.
FAQ 4: "CTE를 10개 이상 체이닝하니 응답이 매우 느립니다."
CTE 체인이 길어지면 데이터베이스 옵티마이저가 최적 실행 계획을 수립하기 어려워질 수 있습니다. 일반적으로 8~10개 이내로 유지하고, 재사용성이 높은 중간 결과는 CDS View로 분리하는 것을 권장합니다. SQL Monitor로 병목 지점을 식별한 뒤 분할하세요.
FAQ 5: "SAP HANA에서 재귀 CTE를 쓰고 싶습니다."
SAP HANA의 WITH 절은 재귀 쿼리를 지원하지 않습니다. 계층적 데이터 탐색이 필요한 경우, ABAP SQL의 WITH HIERARCHY 구문을 사용하거나 HANA Calculation View의 계층 노드를 활용하는 것이 일반적인 대안입니다.
7. 다음 단계 / 관련 주제
이 가이드에서 다룬 CTE, Window Function, 계층 쿼리를 숙지했다면 다음 주제로 확장해 보세요.
- CDS View Entity: CTE로 프로토타이핑한 쿼리를 재사용 가능한 CDS View로 전환하는 방법
- AMDP(ABAP Managed Database Procedures): ABAP SQL로 표현하기 어려운 복잡한 로직을 SQLScript로 구현
- RAP(RESTful Application Programming): CDS View 기반 Fiori 앱에서 Window Function 활용
- ABAP SQL 성능 최적화: SQL Monitor, EXPLAIN PLAN, 인덱스 전략 심화
- MERGE 문: ABAP SQL의 MERGE를 활용한 대량 데이터 동기화 패턴
8. 참고 자료
- SAP Help — ABAP SQL SELECT 문 레퍼런스
- SAP Help — ABAP SQL 성능 가이드
- SAP Help — WITH (CTE) 구문 레퍼런스
- SAP-samples GitHub — ABAP SQL Cheat Sheet
- BMCG — ABAP CTE Quick Playbook
- SAP ABAP Central — Window Functions in ABAP SQL
- SAP Community — New Window Functions in ABAP SQL 상세
📌 본 게시물은 AI(Claude)가 공개된 자료를 기반으로 자동 생성한 콘텐츠입니다. 기술 내용의 정확성은 SAP 공식 문서 와 교차 확인하시기 바랍니다.
™ SAP, S/4HANA, ABAP, Fiori, SAP BTP 등은 SAP SE 또는 그 계열사의 등록 상표입니다. 본 사이트는 SAP SE 와 공식적인 관련이 없는 비공식 학습 자료 입니다.
📧 저작권 침해 / 오류 / 콘텐츠 신고: btpstacks.com 의 "문의" 메뉴를 이용해주세요.