From Zero to SAP
[실습] ABAP 인터널 테이블 실습 - 선언부터 집계, 조건 처리, 출력까지 실전 연습 본문
피트니스 센터에는 다음 두 개의 DB 테이블이 있다.
회원 테이블: ZMEMBER
필드: MBR_ID(CHAR10), MBR_NAME(CHAR30), GRADE(CHAR10), JOIN_DATE(DATS)
출석 테이블: ZATTEND
필드: MBR_ID(CHAR10), ATTEND_DATE(DATS), LESSON_FEE(INT4)`
[테이블 생성]
@EndUserText.label : '피트니스 회원 마스터'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zmember {
key client : abap.clnt not null;
key mbr_id : abap.char(10) not null;
mbr_name : abap.char(30);
grade : abap.char(10);
join_date : abap.dats;
}
@EndUserText.label : '피트니스 출석 이력'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zattend {
key client : abap.clnt not null;
key mbr_id : abap.char(10) not null;
key attend_date : abap.dats not null;
lesson_fee : abap.int4;
}
[CDS VIEW 생성]
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'member CDS view'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_MEMBER as select from zmember
{
key mbr_id,
mbr_name,
grade,
join_date
}
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'attend cds view'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_ATTEND as select from zattend
association [1..1] to ZI_MEMBER as _Member -- ZI_ATTEND에서 ZI_MEMBER로 가는 길을 _Member라는 이름으로 뚫어놓은
on $projection.mbr_id = _Member.mbr_id -- JOIN 조건 (이 뷰의 mbr_id랑 ZI_MEMBER의 mbr_id가 같을 때 연결해)
{
key mbr_id,
key attend_date,
lesson_fee,
_Member -- ← association 노출 (ZI_ATTEND._Member.grade 이렇게 접근 가능하도록)
}
ZI_ATTEND에서 ZI_MEMBER로의 Association을 선언해서, mbr_id를 기준으로 회원 정보를 탐색할 수 있는 네비게이션 경로를 구성했다.
[1단계] 데이터 셋업
인터널 테이블 선언부터 데이터 입력까지
- gt_member: ZMEMBER 구조 기반, MBR_ID로 해시 탐색이 가능한 인터널 테이블로 선언
- gt_attend: ZATTEND 구조 기반, MBR_ID + ATTEND_DATE로 정렬 상태를 유지하는 인터널 테이블로 선언
gt_member는 아래와 같이 5명의 데이터를 VALUE #( ) 방식으로 한 번에 초기화했다.
| MBR_ID | MBR_NAME | GRADE | JOIN_DATE |
| M001 | 김철수 | GOLD | 20230101 |
| M002 | 이영희 | SILVER | 20230315 |
| M003 | 박민준 | GOLD | 20230601 |
| M004 | 최수진 | BRONZE | 20230901 |
| M005 | 정태양 | SILVER | 20240101 |

gt_attend는 아래와 같이 출석 데이터를 추가했다.
이 때, VALUE #( ) 방식이 아닌 INSERT 방식을 고려했다.
이유는 아래와 같다.
1. gt_attend는 Sorted Table로 선언되었다.
SORTED TABLE은 항상 정렬 상태를 유지해야 하기 때문에, = 로 통째 할당하면 정렬 키 중복 체크 등 테이블 타입의 제약을 우회해버림.
그래서 ABAP에서 아예 런타임 에러 또는 컴파일 경고로 막아버린다.
2. 실무적인 관점에서 보면, 출석 데이터는 기존 데이터에서 추가하는 경우가 많기 때문이다.
INSERT는 인터널 테이블 타입에 따라서 동작이 디테일이 다르다.
(참고로, SORTED TABLE에서 APPEND로 데이터를 추가하면 정렬이 깨진다. HASHED TABLE에서는 APPEND를 많이 사용한다.
'이 테이블은 순서 없는 해시구나' 라는 의도가 코드에서 보이기 때문에)
| STANDARD | APPEND랑 똑같이 맨 뒤에 추가 |
| SORTED | 정렬 키 기준으로 위치 찾아서 삽입 |
| HASHED | 해시 키로 버킷 계산해서 삽입 |
| MBR_ID | ATTEND_DATE | LESSON_FEE |
| M001 | 20240101 | 50000 |
| M001 | 20240115 | 50000 |
| M001 | 20240201 | 60000 |
| M002 | 20240101 | 30000 |
| M002 | 20240201 | 30000 |
| M003 | 20240101 | 50000 |
| M003 | 20240115 | 70000 |
| M005 | 20240201 | 40000 |

[2단계] 집계
- gt_fee_sum: MBR_ID별 LESSON_FEE 합계를 담는 인터널 테이블 만들기

- gt_attend를 LOOP 돌면서 COLLECT로 MBR_ID별 총 수강료를 집계하기

처음에 COLLECT ls_attend INTO gt_fee_sum 을 시도했으나 실패했다.
그 이유는 ls_attend는 gt_attend 테이블의 한 행을 가져오는데 gt_fee_sum와 타입이 다르기 때문이다.
따라서, 필요한 필드만 골라서 ty_fee_sum 타입으로 만든 다음 gt_fee_sum에 넣었다.
[3단계] 데이터 보강 및 조건 처리
- gt_fee_sum을 LOOP 돌면서, 각 MBR_ID에 해당하는 회원 이름을 gt_member에서 읽어와서 채우기
이 때 BINARY SEARCH는 쓰면 안 되고, 대신 gt_member의 테이블 타입 특성을 활용해 O(1)로 읽기
gt_member는 HASHED TABLE이다. 해시 테이블에서 KEY로 READ하면 자동으로 O(1)이다.


첫 번째 LOOP에서는 출석 테이블을 돌면서 인터널 테이블 gt_fee_sum에 멤버ID를 기준으로 레슨 비용 합산을 저장한다.
두 번째 LOOP에서는 위에서 계산한 테이블을 한 행씩 돌면서 멤버 ID를 기준으로 gt_member 테이블에서 해당 멤버의 정보를 가져와서 ls_member에 저장한다. 마지막으로, 출력 결과에서 보여줄 필드를 APPEND한다.
COLLECT는 자동으로 문자 타입 필드는 키로 판단하고, 숫자 타입 필드는 합산 대상으로 판단하여 계산한다.
- 총 수강료가 100,000 이상인 회원은 GRADE를 'VIP'로 업데이트하기 (gt_member 기준)
MODIFY 시 GRADE 필드만 수정되도록 작성

TRANSPORTING 키워드를 사용하여 grade 필드만 수정되도록 하였다.
[4단계] 출력
- gt_member를 JOIN_DATE 오름차순으로 정렬한 뒤 출력하기
출력 형식: [순번] MBR_ID | MBR_NAME | GRADE | JOIN_DATE - M004 (최수진)처럼 출석 기록이 없는 회원도 정상 출력되도록 하기


'ABAP > 실습' 카테고리의 다른 글
| [실습] ABAP 인터널 테이블 실습 - COLLECT 한계, MODIFY 최적화 (0) | 2026.04.13 |
|---|---|
| [실습] ABAP 인터널 테이블 실습 — LOOP, FILTER, SORT, FIELD-SYMBOL (0) | 2026.04.11 |
| [ABAP 실습] Open SQL JOIN & CRUD 연습 문제 풀이 (0) | 2026.04.09 |
| [실습] Open SQL과 CDS View 활용 (0) | 2026.04.06 |
| [실습] DDIC 테이블 감각 익히기 (Open SQL CRUD) (0) | 2026.04.05 |