ABAP

IF 분기 vs SQL CASE — 뭐가 빠를까 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 학습 포인트

ABAP Open SQL의 CASE 표현식은 SELECT 문 내부에서 조건에 따라 값을 분기시킬 수 있는 강력한 기능입니다. 기존에는 데이터를 가져온 후 ABAP 레벨에서 IF/CASE 문으로 가공했다면, CASE 표현식을 사용하면 DB 서버에서 직접 분기 처리하여 네트워크 전송량을 줄이고 성능을 개선할 수 있습니다. 이 글에서는 NetWeaver 7.5 이상에서 강화된 Open SQL CASE 표현식의 문법과 실전 활용법을 단계별로 살펴봅니다.

  • Simple CASE와 Searched CASE의 차이 이해
  • SELECT 컬럼 리스트에서 CASE 표현식 작성
  • 수주 상태, 재고 등급 같은 실무 시나리오 적용
  • WHERE/ORDER BY 절에서의 CASE 활용
  • NULL 처리와 데이터 타입 일치 규칙

먼저 알고 있어야 할 내용

이 글은 ABAP Open SQL의 기본 SELECT 문법(SELECT, FROM, WHERE, INTO TABLE)을 이미 사용해 본 개발자를 대상으로 합니다. 또한 내부 테이블(internal table)과 작업 영역(work area)의 개념, 그리고 ABAP의 기본 데이터 타입(CHAR, NUMC, DEC 등)에 대한 이해가 있으면 좋습니다. ABAP CDS View나 AMDP를 사용해 본 경험이 있다면 더욱 수월하게 진행할 수 있습니다.

환경 및 준비 사항

CASE 표현식은 SAP NetWeaver 7.40 SP05부터 Open SQL에서 본격적으로 지원되기 시작했으며, 7.50 이후부터는 더욱 풍부한 표현식 기능을 제공합니다. 따라서 이 글의 예제는 일반적으로 NetWeaver 7.50 이상 또는 S/4HANA 1809 이후 환경을 권장합니다.

  • SAP NetWeaver AS ABAP 7.50 이상 (S/4HANA 1909 이상 권장)
  • ABAP Development Tools (ADT, Eclipse 기반) 또는 SE80
  • SE11에서 테스트용 커스텀 테이블(ZSALES_ORDER, ZINVENTORY 등) 생성 권한
  • SE16N 또는 ADT의 Data Preview로 결과 확인

예제 시나리오를 위해 ZSALES_ORDER(수주번호, 고객, 금액, 상태코드)와 ZINVENTORY(자재번호, 재고수량) 테이블이 있다고 가정합니다. 실제 운영 환경에서는 VBAK, MARD 같은 표준 테이블로 대체할 수 있습니다.

CASE 표현식의 핵심 개념

Open SQL의 CASE 표현식은 일반 프로그래밍 언어의 switch/case와 비슷하지만, 결정적인 차이는 값을 반환하는 표현식(expression)이라는 점입니다. 즉, 분기 로직 자체가 하나의 컬럼 값처럼 동작합니다. 비유하자면 자판기와 같습니다. 동전을 넣으면(조건) 해당 음료가 나오듯이(값), CASE는 조건에 따라 정해진 값을 즉시 반환합니다.

CASE 표현식에는 두 가지 형태가 있습니다.

  • Simple CASE: 하나의 비교 대상을 여러 값과 동등 비교(=)합니다. CASE column WHEN value1 THEN ... WHEN value2 THEN ... END 형태로, 코드 매핑처럼 단순한 변환에 적합합니다.
  • Searched CASE: 각 WHEN 절마다 독립적인 조건식을 작성합니다. CASE WHEN condition1 THEN ... WHEN condition2 THEN ... END 형태로, 범위 비교(BETWEEN, >, <), 다중 컬럼 조건, AND/OR 조합 같은 복잡한 로직에 사용합니다.

두 형태 모두 ELSE 절은 선택 사항이며, ELSE를 생략하고 어떤 WHEN도 매칭되지 않으면 NULL 값(ABAP에서는 초기값)이 반환됩니다. 또한 모든 THEN/ELSE의 결과 데이터 타입은 호환 가능해야 하며, 그렇지 않으면 구문 오류가 발생합니다.

왜 굳이 SQL 레벨에서 분기를 처리할까요? 첫째, 네트워크 부하 감소입니다. DB에서 미리 가공된 결과를 보내면 App 서버로 전송할 데이터량이 줄어듭니다. 둘째, HANA의 코드 푸시다운(Code Pushdown) 원칙에 부합합니다. SAP는 S/4HANA 시대에 들어서면서 로직을 가능한 한 DB 레이어로 내리는 것을 일반적으로 권장합니다. 셋째, 코드 가독성입니다. SELECT 후 LOOP를 돌며 분기 처리하는 것보다 SELECT 한 문장 안에 의도가 명확히 드러납니다.

1단계: 기본 Simple CASE 예제

가장 간단한 형태부터 시작합니다. 수주 상태 코드(A, B, C, X)를 사용자가 읽기 쉬운 한글 라벨로 변환하는 예제입니다.

REPORT z_demo_case_basic.

DATA: BEGIN OF ls_result,
        order_id     TYPE zsales_order-order_id,
        customer_id  TYPE zsales_order-customer_id,
        status_code  TYPE zsales_order-status,
        status_label TYPE string,
      END OF ls_result,
      lt_result LIKE TABLE OF ls_result.

SELECT order_id,
       customer_id,
       status,
       CASE status
         WHEN 'A' THEN '접수'
         WHEN 'B' THEN '처리중'
         WHEN 'C' THEN '완료'
         WHEN 'X' THEN '취소'
         ELSE '알수없음'
       END AS status_label
  FROM zsales_order
  INTO TABLE @lt_result
  UP TO 100 ROWS.

LOOP AT lt_result INTO ls_result.
  WRITE: / ls_result-order_id, ls_result-status_label.
ENDLOOP.

여기서 핵심은 CASE status WHEN A THEN ... 형태로, status 컬럼 값이 무엇과 같은지 한 번만 지정하면 됩니다. 또한 AS status_label로 별칭을 부여하여 INTO TABLE의 구조 필드명과 맞췄습니다. ABAP 7.40 SP08 이후 호스트 변수 앞에 @ 표시를 붙이는 것이 권장되는 New Open SQL 문법입니다.

2단계: Searched CASE로 재고 등급 분류와 에러 처리

실무에서는 단순 코드 매핑보다 범위 조건이 더 자주 등장합니다. 재고 수량에 따라 품절, 부족, 정상, 과잉 등급을 매기는 시나리오를 살펴봅니다.

REPORT z_demo_case_searched.

DATA: lt_inventory TYPE TABLE OF zinventory_report,
      lv_msg       TYPE string.

TRY.
    SELECT material_id,
           plant,
           stock_qty,
           CASE
             WHEN stock_qty = 0
                  THEN 'OUT_OF_STOCK'
             WHEN stock_qty > 0 AND stock_qty <= 10
                  THEN 'LOW'
             WHEN stock_qty > 10 AND stock_qty <= 100
                  THEN 'NORMAL'
             WHEN stock_qty > 100 AND stock_qty <= 500
                  THEN 'HIGH'
             ELSE 'OVERSTOCK'
           END AS stock_grade,
           CASE
             WHEN stock_qty < safety_stock
                  THEN abap_true
             ELSE abap_false
           END AS reorder_flag
      FROM zinventory
      INTO TABLE @lt_inventory.

    IF sy-subrc <> 0.
      MESSAGE 'No inventory data found' TYPE 'I'.
    ENDIF.

  CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
    lv_msg = lx_sql->get_text( ).
    MESSAGE lv_msg TYPE 'E'.
ENDTRY.

두 번째 CASE에서는 abap_true/abap_false 상수를 직접 SELECT 결과에 매핑했습니다. 이 패턴은 화면 ALV에서 체크박스 컬럼을 표시할 때 매우 유용합니다. 또한 cx_sy_open_sql_db 예외를 잡아 DB 레벨 에러를 로깅하는 것은 운영 환경에서 권장되는 방식입니다.

3단계: 프로덕션 — 집계 및 ORDER BY 활용

실제 운영 리포트에서는 CASE를 집계 함수 안에 넣어 조건부 카운트/합계를 구하는 패턴이 자주 등장합니다. 또한 ORDER BY에서도 CASE를 사용해 커스텀 정렬 순서를 만들 수 있습니다.

REPORT z_demo_case_production.

TYPES: BEGIN OF ty_summary,
         customer_id     TYPE zsales_order-customer_id,
         total_orders    TYPE i,
         completed_cnt   TYPE i,
         cancelled_cnt   TYPE i,
         pending_amount  TYPE p LENGTH 15 DECIMALS 2,
         priority_label  TYPE string,
       END OF ty_summary.

DATA: lt_summary TYPE TABLE OF ty_summary.

SELECT customer_id,
       COUNT(*) AS total_orders,
       SUM( CASE WHEN status = 'C' THEN 1 ELSE 0 END ) AS completed_cnt,
       SUM( CASE WHEN status = 'X' THEN 1 ELSE 0 END ) AS cancelled_cnt,
       SUM( CASE WHEN status IN ( 'A', 'B' )
                 THEN net_amount
                 ELSE 0
            END ) AS pending_amount,
       CASE
         WHEN SUM( net_amount ) >= 1000000 THEN 'VIP'
         WHEN SUM( net_amount ) >= 100000  THEN 'GOLD'
         WHEN SUM( net_amount ) >= 10000   THEN 'SILVER'
         ELSE 'STANDARD'
       END AS priority_label
  FROM zsales_order
  GROUP BY customer_id
  HAVING COUNT(*) > 5
  ORDER BY CASE
             WHEN SUM( net_amount ) >= 1000000 THEN 1
             WHEN SUM( net_amount ) >= 100000  THEN 2
             ELSE 3
           END,
           customer_id
  INTO TABLE @lt_summary.

cl_demo_output=>display( lt_summary ).

이 코드의 핵심 포인트는 다음과 같습니다.

  • 조건부 집계: SUM( CASE WHEN ... THEN 1 ELSE 0 END ) 패턴으로 특정 상태의 건수만 카운트합니다.
  • 조건부 합계: A 또는 B 상태의 미결 금액만 합산하여 미수금 추적이 가능합니다.
  • GROUP BY와 CASE 조합: 집계 결과에 따라 고객 등급을 동적으로 부여합니다.
  • ORDER BY 내 CASE: 단순 사전식 정렬이 아닌 비즈니스 우선순위로 정렬합니다.

주의할 점은 ORDER BY 절의 CASE 식은 SELECT 절의 별칭(priority_label)을 재사용할 수 없는 경우가 많으므로 표현식을 다시 작성해야 합니다. 또한 모든 THEN/ELSE 결과 타입이 호환되어야 하는데, 위 예제에서는 모두 정수 또는 모두 문자열이라 안전합니다.

흔한 실수와 트러블슈팅

Q1. "Type conflict between WHEN clauses" 같은 구문 오류가 발생합니다.
모든 THEN 절과 ELSE 절의 반환 데이터 타입은 호환되어야 합니다. 예를 들어 한 WHEN에서는 숫자(1)를, 다른 WHEN에서는 문자(A)를 반환하면 오류가 납니다. 의도적으로 문자열로 통일하려면 명시적으로 문자 리터럴을 사용하거나, 숫자/문자 변환을 명확히 해주세요.

Q2. ELSE를 생략했더니 결과 컬럼에 NULL이 들어와 ABAP에서 처리가 이상합니다.
Open SQL은 NULL을 ABAP의 초기값(빈 문자열, 0)으로 변환하지만, 이로 인해 값이 없음과 값이 0/공백임을 구분하기 어려워집니다. 가능하면 ELSE 절을 명시적으로 작성하여 기본값을 정해주는 것이 디버깅 시간을 줄이는 일반적인 권장 사항입니다.

Q3. WHERE 절에 CASE를 썼는데 인덱스를 안 타는 것 같습니다.
WHERE 절 내부의 CASE 식은 컬럼을 함수처럼 감싸기 때문에 일반적으로 인덱스 활용이 어렵습니다. 가능하다면 WHERE에서는 일반 조건(AND, OR)을 사용하고, CASE는 SELECT 컬럼 리스트나 ORDER BY에서만 사용하는 편이 성능상 유리합니다. HANA에서는 옵티마이저가 일부 케이스를 잘 다루지만, AnyDB(Oracle, DB2 등) 환경까지 고려한다면 보수적으로 작성하세요.

Q4. 7.40 이전 시스템에서 동작하지 않습니다.
풍부한 CASE 표현식은 NetWeaver 7.40 SP05 이후에 안정화되었습니다. 그 이전 버전이라면 Native SQL이나 ABAP 레벨 분기로 대체해야 합니다. 운영 시스템의 정확한 SP 레벨은 시스템 상태(System Status)에서 확인할 수 있습니다.

관련하여 더 살펴보면 좋은 주제

CASE 표현식에 익숙해졌다면 다음 주제로 확장해보세요. 첫째, COALESCE 함수는 NULL 처리 전용 단축 표현식으로 CASE와 함께 자주 쓰입니다. 둘째, ABAP CDS View의 CASE는 Open SQL과 거의 동일한 문법으로 동작하며, 재사용 가능한 가상 뷰를 만들 수 있습니다. 셋째, AMDP(ABAP Managed Database Procedure)에서 HANA SQLScript의 CASE를 활용하면 더 복잡한 분기와 임시 변수를 다룰 수 있습니다. 넷째, Open SQL의 산술 표현식, 문자열 함수(CONCAT, SUBSTRING)와 CASE를 조합하면 한 줄 SQL로 강력한 데이터 변환이 가능합니다.

댓글 0

아직 댓글이 없습니다.