ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Oracle SQL → SAP AMDP 전환 가이드 #1
    SAP 2026. 5. 10. 21:27

    oracle-to-amdp.html
    0.03MB

     

     

    Oracle → SAP AMDP 변환 완전 가이드

    Oracle → SAP AMDP 변환 완전 가이드

    1. Oracle ↔ HANA SQLScript 함수 대응표

    OracleHANA SQLScript비고
    NVL(a, b)COALESCE(a, b)NULL 대체
    NVL2(a, b, c)CASE WHEN a IS NOT NULL THEN b ELSE c END
    DECODE(col, v1,r1, v2,r2, def)CASE col WHEN v1 THEN r1 WHEN v2 THEN r2 ELSE def ENDCASE WHEN 방식
    DECODE(col, v1,r1, v2,r2, def)MAP(col, v1,r1, v2,r2, def)🆕 MAP 함수 (HANA 전용, 간결)
    ROWNUM <= nLIMIT n 또는 ROW_NUMBER() OVER(...)
    ROWNUM = 1LIMIT 1첫 행만
    SYSDATECURRENT_DATE / NOW()
    TRUNC(date)TO_DATE(date)날짜 절삭
    TRUNC(num, n)FLOOR(num * POWER(10,n)) / POWER(10,n)숫자 절삭
    TO_CHAR(date, 'YYYYMMDD')TO_VARCHAR(date, 'YYYYMMDD')
    SUBSTR(str, 1, 5)SUBSTRING(str, 1, 5)
    INSTR(str, 'x')LOCATE(str, 'x')인자 순서 동일
    MONTHS_BETWEEN(d1, d2)MONTHS_BETWEEN(d1, d2)동일
    ADD_MONTHS(d, n)ADD_MONTHS(d, n)동일
    LPAD(str, n, 'x')LPAD(str, n, 'x')동일
    CONNECT BY LEVELSERIES_GENERATE_INTEGER(1, 1, n)계층/순번
    MINUSEXCEPT차집합
    a (+) = ba LEFT OUTER JOIN b ON ...표준 문법으로
    DUALFROM 절 생략 또는 DUMMYSELECT 1 FROM DUMMY
    LISTAGG(col, ',')STRING_AGG(col, ',')
    WMSYS.WM_CONCATSTRING_AGG(col, ',')
    REGEXP_LIKE(col, pat)col LIKE_REGEXPR pat

    💡 DECODE → MAP: HANA의 MAP() 함수는 Oracle DECODE와 인자 순서가 동일하여 마이그레이션이 직관적입니다. 단, HANA 전용 함수이므로 표준 SQL 호환이 필요한 경우 CASE WHEN을 사용하세요.

    2. SELECT / JOIN / 서브쿼리 변환

    2-1. Oracle Outer Join (+) → 표준 LEFT JOIN

    Oracle
    SELECT a.vbeln, b.matnr
      FROM vbak a, vbap b
     WHERE a.vbeln = b.vbeln(+)
       AND a.mandt = '100';
    AMDP SQLScript
    RETURN
      SELECT a.vbeln, b.matnr
        FROM vbak AS a
        LEFT OUTER JOIN vbap AS b
          ON  a.mandt = b.mandt
          AND a.vbeln = b.vbeln
       WHERE a.mandt = SESSION_CONTEXT('CLIENT');

    2-2. ROWNUM → LIMIT / TOP

    Oracle
    SELECT * FROM vbak
     WHERE ROWNUM <= 10
     ORDER BY erdat DESC;
    AMDP SQLScript
    RETURN
      SELECT TOP 10 *
        FROM vbak
       WHERE mandt = SESSION_CONTEXT('CLIENT')
       ORDER BY erdat DESC;

    2-3. 인라인 뷰(서브쿼리) → CTE (WITH 절) 권장

    Oracle
    SELECT x.vbeln, x.total_qty
      FROM (
        SELECT vbeln, SUM(kwmeng) AS total_qty
          FROM vbap
         GROUP BY vbeln
      ) x
     WHERE x.total_qty > 100;
    AMDP SQLScript
    RETURN
      WITH order_qty AS (
        SELECT vbeln, SUM(kwmeng) AS total_qty
          FROM vbap
         WHERE mandt = SESSION_CONTEXT('CLIENT')
         GROUP BY vbeln
      )
      SELECT vbeln, total_qty
        FROM order_qty
       WHERE total_qty > 100;

    2-4. DECODE → CASE WHEN / MAP

    Oracle
    SELECT vbeln,
           DECODE(auart,
             'TA', '표준',
             'KE', '반품',
             '기타') AS order_type
      FROM vbak;
    AMDP — CASE WHEN 방식
    RETURN
      SELECT vbeln,
             CASE auart
               WHEN 'TA' THEN '표준'
               WHEN 'KE' THEN '반품'
               ELSE '기타'
             END AS order_type
        FROM vbak
       WHERE mandt = SESSION_CONTEXT('CLIENT');
    AMDP — MAP 함수 방식 (HANA 전용, 더 간결)
    RETURN
      SELECT vbeln,
             MAP(auart, 'TA', '표준', 'KE', '반품', '기타') AS order_type
        FROM vbak
       WHERE mandt = SESSION_CONTEXT('CLIENT');

    💡 MAP vs CASE WHEN 선택 기준

    단순 등치 비교MAP(컬럼, 값1, 결과1, 값2, 결과2, ..., 기본값) — Oracle DECODE와 인자 순서 동일, 간결

    범위 조건 / 복합 조건 (BETWEEN, >, <) → CASE WHEN 사용

    표준 SQL 호환 필요CASE WHEN 권장 (MAP은 HANA 전용)

    3. 집계 함수 변환 (GROUP BY / HAVING)

    3-1. NVL + 집계 조합

    Oracle
    SELECT kunnr,
           SUM(NVL(netwr, 0))  AS total_sales,
           COUNT(*)             AS order_cnt
      FROM vbak
     WHERE mandt = '100'
       AND erdat BETWEEN '20240101' AND '20241231'
     GROUP BY kunnr
    HAVING SUM(NVL(netwr, 0)) > 1000000;
    AMDP SQLScript
    RETURN
      SELECT kunnr,
             SUM(COALESCE(netwr, 0)) AS total_sales,
             COUNT(*)                AS order_cnt
        FROM vbak
       WHERE mandt = SESSION_CONTEXT('CLIENT')
         AND erdat BETWEEN :iv_date_from AND :iv_date_to
       GROUP BY kunnr
      HAVING SUM(COALESCE(netwr, 0)) > 1000000;

    3-2. LISTAGG → STRING_AGG

    Oracle
    SELECT vbeln,
           LISTAGG(matnr, ', ')
             WITHIN GROUP (ORDER BY posnr)
             AS mat_list
      FROM vbap
     GROUP BY vbeln;
    AMDP SQLScript
    RETURN
      SELECT vbeln,
             STRING_AGG(matnr, ', ' ORDER BY posnr)
               AS mat_list
        FROM vbap
       WHERE mandt = SESSION_CONTEXT('CLIENT')
       GROUP BY vbeln;

    4. Window 함수 변환

    💡 HANA SQLScript는 표준 Window 함수를 대부분 지원하며 Oracle과 문법이 거의 동일합니다.
    TABLE FUNCTION에서 Window 함수 사용 가능 / PROCEDURE에서는 테이블 변수에 담아 처리합니다.

    4-1. RANK / DENSE_RANK — 문법 동일

    Oracle
    SELECT kunnr, netwr,
           RANK() OVER (
             PARTITION BY vkorg
             ORDER BY netwr DESC) AS rnk
      FROM vbak;
    AMDP SQLScript
    RETURN
      SELECT kunnr, netwr,
             RANK() OVER (
               PARTITION BY vkorg
               ORDER BY netwr DESC) AS rnk
        FROM vbak
       WHERE mandt = SESSION_CONTEXT('CLIENT');

    4-2. ROW_NUMBER — 중복 제거 / 최신 1건 추출

    Oracle
    SELECT * FROM (
      SELECT matnr, werks, labst,
             ROW_NUMBER() OVER (
               PARTITION BY matnr
               ORDER BY budat DESC) AS rn
        FROM mard
    ) WHERE rn = 1;
    AMDP — CTE로 변환
    RETURN
      WITH ranked AS (
        SELECT matnr, werks, labst,
               ROW_NUMBER() OVER (
                 PARTITION BY matnr
                 ORDER BY budat DESC) AS rn
          FROM mard
         WHERE mandt = SESSION_CONTEXT('CLIENT')
      )
      SELECT matnr, werks, labst
        FROM ranked
       WHERE rn = 1;

    4-3. LAG / LEAD — 이전/다음 행 참조 (문법 동일)

    RETURN
      SELECT budat, dmbtr,
             LAG(dmbtr,  1, 0) OVER (ORDER BY budat) AS prev_amt,
             LEAD(dmbtr, 1, 0) OVER (ORDER BY budat) AS next_amt
        FROM bkpf
       WHERE mandt = SESSION_CONTEXT('CLIENT');

    4-4. 누계 (Cumulative SUM) — 문법 동일

    RETURN
      SELECT budat, dmbtr,
             SUM(dmbtr) OVER (ORDER BY budat ROWS UNBOUNDED PRECEDING) AS cum_sum
        FROM bkpf
       WHERE mandt = SESSION_CONTEXT('CLIENT');

    5. Query Provider 패턴 (@ObjectModel.query.implementedBy)

    5-1. 개념 구조

    CDS View └── @ObjectModel.query.implementedBy: 'ABAP:ZCL_##_QUERY_PROVIDER' ↓ (데이터 조회 위임) ZCL_##_QUERY_PROVIDER (implements IF_RAP_QUERY_PROVIDER) └── method SELECT └── AMDP 호출 또는 직접 SQLScript 실행

    ⚠️ @ObjectModel.query.implementedBy를 선언하면 CDS 자체 DB 조회가 비활성화됩니다.
    지정 클래스의 SELECT 메서드가 모든 데이터 조회를 담당하므로, 필터·페이징·정렬을 직접 구현해야 합니다.

    5-2. CDS View 선언

    @ObjectModel.query.implementedBy: 'ABAP:ZCL_ZSALES_QUERY_PROVIDER'
    define root view entity ZC_SALES_ORDER
      as projection on ZI_SALES_ORDER
    {
      key SalesOrder,
          Customer,
          NetAmount,
          CreatedDate
    }

    5-3. Query Provider 클래스 구현 (AMDP 연동)

    CLASS zcl_zsales_query_provider DEFINITION
      PUBLIC FINAL CREATE PUBLIC.
      PUBLIC SECTION.
        INTERFACES if_rap_query_provider.
    ENDCLASS.
    
    CLASS zcl_zsales_query_provider IMPLEMENTATION.
    
      METHOD if_rap_query_provider~select.
    
        " 1) 필터 파라미터 추출
        DATA(lt_filter)   = io_request->get_filter( )->get_as_ranges( ).
        DATA lv_date_from TYPE vbak-erdat.
        DATA lv_date_to   TYPE vbak-erdat.
    
        LOOP AT lt_filter INTO DATA(ls_filter) WHERE name = 'CREATEDDATE'.
          lv_date_from = ls_filter-range[ 1 ]-low.
          lv_date_to   = ls_filter-range[ 1 ]-high.
        ENDLOOP.
    
        " 2) AMDP 호출
        DATA lt_result TYPE TABLE OF zst_sales_result.
        zcl_amdp_sales=>get_sales_data(
          EXPORTING iv_date_from = lv_date_from
                    iv_date_to   = lv_date_to
          IMPORTING et_result    = lt_result ).
    
        " 3) 페이징 처리 (필수 구현)
        DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
        DATA(lv_limit)  = io_request->get_paging( )->get_page_size( ).
        IF lv_limit <> if_rap_query_paging=>page_size_unlimited.
          lt_result = lt_result[ lv_offset + 1 ..
                        MIN( lv_offset + lv_limit, lines( lt_result ) ) ].
        ENDIF.
    
        " 4) 응답 설정
        io_response->set_total_number_of_records( lines( lt_result ) ).
        io_response->set_data( lt_result ).
    
      ENDMETHOD.
    ENDCLASS.

    5-4. Query Provider 사용 가능 기능 및 제약

    항목가능비고
    io_request->get_filter()필터 조건 추출
    io_request->get_paging()오프셋/페이지 크기
    io_request->get_sort_elements()정렬 기준
    io_request->get_requested_elements()요청된 필드 목록
    io_request->get_aggregation()✅ (7.57+)집계 요청 처리
    AMDP 직접 호출권장 방식
    ABAP SELECT 사용AMDP 대비 성능 낮음
    io_response->set_data()결과 반환 필수
    io_response->set_total_number_of_records()전체 건수 반환 필수
    CDS 자체 필터 자동 적용직접 구현 필요
    CDS Association 자동 JOIN직접 JOIN 구현

    6. 변환 체크리스트

    Oracle → AMDP 변환 시 아래 항목을 반드시 확인하세요.

    • NVLCOALESCE 치환
    • DECODEMAP(col, v1, r1, ..., default) 또는 CASE WHEN 치환
    • ROWNUMLIMIT 또는 ROW_NUMBER() OVER(...) 치환
    • (+) outer join → LEFT/RIGHT OUTER JOIN 표준 문법으로
    • TO_CHARTO_VARCHAR 치환
    • LISTAGGSTRING_AGG 치환
    • MINUSEXCEPT 치환
    • SYSDATECURRENT_DATE 또는 NOW() 치환
    • FROM DUAL 제거 또는 FROM DUMMY
    • 모든 테이블에 WHERE mandt = SESSION_CONTEXT('CLIENT') 추가
    • 사용하는 모든 테이블을 USING 절에 선언
    • TABLE FUNCTION이면 OPTIONS READ-ONLY 선언
    • PROCEDURE에서 EXECUTE IMMEDIATE 사용 시 SQL Injection 검토

    7. WITH 구문 + TABLE FUNCTION / EXECUTE IMMEDIATE 패턴

    7-1. TABLE FUNCTION 내부에서 WITH(CTE) 구문 — 다단계 집계

    METHOD get_sales_summary
      BY DATABASE FUNCTION
      FOR HDB
      LANGUAGE SQLSCRIPT
      OPTIONS READ-ONLY
      USING vbak vbap.
    
      RETURN
        WITH
        " 1단계: 기간 내 주문 헤더
        base_orders AS (
          SELECT vbeln, kunnr, vkorg, erdat
            FROM vbak
           WHERE mandt = SESSION_CONTEXT('CLIENT')
             AND erdat BETWEEN :iv_date_from AND :iv_date_to
        ),
        " 2단계: 아이템 집계
        item_summary AS (
          SELECT vbeln,
                 SUM(kwmeng) AS total_qty,
                 SUM(netwr)  AS total_netwr,
                 COUNT(*)    AS line_count
            FROM vbap
           WHERE mandt = SESSION_CONTEXT('CLIENT')
           GROUP BY vbeln
        ),
        " 3단계: 고객별 매출 + 순위
        ranked AS (
          SELECT o.kunnr, o.vkorg,
                 SUM(i.total_netwr) AS sales_amt,
                 RANK() OVER (
                   PARTITION BY o.vkorg
                   ORDER BY SUM(i.total_netwr) DESC
                 ) AS sales_rank
            FROM base_orders AS o
            JOIN item_summary AS i ON o.vbeln = i.vbeln
           GROUP BY o.kunnr, o.vkorg
        )
        " 4단계: 상위 N건 반환
        SELECT kunnr, vkorg, sales_amt, sales_rank
          FROM ranked
         WHERE sales_rank <= :iv_top_n;
    
    ENDMETHOD.

    7-2. PROCEDURE에서 TABLE FUNCTION 결과를 변수에 직접 받기 (권장)

    METHOD execute_with_table_func
      BY DATABASE PROCEDURE
      FOR HDB
      LANGUAGE SQLSCRIPT
      USING ZCL_AMDP_SALES=>GET_SALES_SUMMARY.  " USING 절에 반드시 선언
    
      -- TABLE FUNCTION 결과를 테이블 변수에 직접 할당
      lt_result = SELECT kunnr, vkorg, sales_amt, sales_rank
                    FROM ZCL_AMDP_SALES=>GET_SALES_SUMMARY(
                      iv_date_from => :iv_date_from,
                      iv_date_to   => :iv_date_to,
                      iv_top_n     => :iv_top_n
                    );
    
      -- 추가 가공
      et_result = SELECT * FROM :lt_result WHERE sales_rank = 1;
    
    ENDMETHOD.

    7-3. PROCEDURE에서 EXECUTE IMMEDIATE — 동적 호출

    METHOD execute_dynamic_call
      BY DATABASE PROCEDURE
      FOR HDB
      LANGUAGE SQLSCRIPT
      USING ZCL_AMDP_SALES=>GET_SALES_SUMMARY.
    
      DECLARE lv_sql   NVARCHAR(2000);
      DECLARE lv_top_n INT;
    
      lv_top_n := :iv_top_n;
    
      lv_sql :=
        'SELECT kunnr, vkorg, sales_amt, sales_rank' ||
        '  FROM ZCL_AMDP_SALES=>GET_SALES_SUMMARY(' ||
        '    iv_date_from => ''' || :iv_date_from || ''',' ||
        '    iv_date_to   => ''' || :iv_date_to   || ''',' ||
        '    iv_top_n     => '   || lv_top_n      || ')';
    
      EXECUTE IMMEDIATE lv_sql INTO et_result;
    
    ENDMETHOD.

    ⚠️ EXECUTE IMMEDIATE 주의사항

    • TABLE FUNCTION이 아닌 PROCEDURE 안에서만 사용 가능

    • 동적 호출 시에도 USING 절에 해당 TABLE FUNCTION을 반드시 선언해야 활성화됨

    • 문자열 연결로 파라미터 조립 시 SQL Injection 위험 → 가능하면 바인딩 파라미터(:param) 방식 사용

    7-4. ABAP에서 TABLE FUNCTION 결과 호출 (Open SQL SELECT INTO)

    " ABAP 레이어에서 AMDP Table Function 직접 호출
    SELECT kunnr, vkorg, sales_amt, sales_rank
      INTO TABLE @DATA(lt_top_sales)
      FROM ( SELECT kunnr, vkorg, sales_amt, sales_rank
               FROM ZCL_AMDP_SALES=>GET_SALES_SUMMARY(
                 iv_date_from = @lv_date_from
                 iv_date_to   = @lv_date_to
                 iv_top_n     = @lv_top_n ) ) AS result
     WHERE sales_rank = 1.
    #SAP_ABAP #AMDP #SQLScript #HANA #Oracle_to_HANA #CDS_View #RAP #QueryProvider #TABLE_FUNCTION #EXECUTE_IMMEDIATE #MAP함수 #DECODE
Designed by Tistory.