ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Oracle Hierarchy → SAP HANA SQLScript 변환 가이드
    SAP 2026. 5. 26. 21:39
    Oracle Hierarchy → SAP HANA SQLScript 변환 가이드

    Oracle Hierarchy → SAP HANA SQLScript 변환 가이드

    1. Oracle ↔ HANA 계층 키워드 대응표

    Oracle 키워드HANA SQLScript 대응비고
    START WITHSTART WHEREWHERE 키워드 필수
    CONNECT BY PRIORCONNECT BY PRIOR문법 동일
    LEVELHIERARCHY_LEVEL자동 생성 컬럼
    SYS_CONNECT_BY_PATHHIERARCHY_PATH자동 생성 컬럼
    CONNECT_BY_ROOTHIERARCHY_ROOT_RANK + JOINCTE 조합 필요
    CONNECT_BY_ISLEAFIS_LEAF자동 생성 컬럼
    CONNECT_BY_ISCYCLEIS_CYCLE자동 생성 컬럼
    CONNECT BY LEVEL <= nSERIES_GENERATE_INTEGER순번 생성
    CONNECT BY LEVEL (날짜)SERIES_GENERATE_DATE날짜 시퀀스

    2. 패턴별 변환 예제

    ① 기본 Top-Down 탐색

    Oracle
    -- 최상위 관리자부터 하위로 전개
    SELECT LEVEL,
           empno,
           ename,
           mgr,
           LPAD(' ', (LEVEL-1)*4) || ename AS tree_view
      FROM emp
     START WITH mgr IS NULL
    CONNECT BY PRIOR empno = mgr;
    HANA SQLScript
    RETURN
      SELECT hierarchy_level AS "LEVEL",
             empno, ename, mgr,
             LPAD(' ', (hierarchy_level-1)*4)
               || ename AS tree_view
        FROM HIERARCHY (
          SOURCE
            SELECT empno, ename, mgr
              FROM emp
             WHERE mandt = SESSION_CONTEXT('CLIENT')
          START WHERE mgr IS NULL
          CONNECT BY PRIOR empno = mgr
        );

    ② Bottom-Up 역방향 탐색

    Oracle
    -- 특정 사원 → 최상위 관리자까지 역추적
    SELECT LEVEL, empno, ename, mgr
      FROM emp
     START WITH empno = 7902
    CONNECT BY PRIOR mgr = empno;
    HANA SQLScript
    RETURN
      SELECT hierarchy_level AS "LEVEL",
             empno, ename, mgr
        FROM HIERARCHY (
          SOURCE
            SELECT empno, ename, mgr
              FROM emp
             WHERE mandt = SESSION_CONTEXT('CLIENT')
          START WHERE empno = :iv_empno
          CONNECT BY PRIOR mgr = empno  -- 역방향 동일
        );

    ③ SYS_CONNECT_BY_PATH (경로 표시)

    Oracle
    SELECT LEVEL, ename,
           SYS_CONNECT_BY_PATH(ename, '/') AS full_path
      FROM emp
     START WITH mgr IS NULL
    CONNECT BY PRIOR empno = mgr;
    -- 결과: /KING/JONES/FORD/SMITH
    HANA SQLScript
    RETURN
      SELECT hierarchy_level AS "LEVEL",
             ename,
             hierarchy_path  AS full_path  -- 자동 생성
        FROM HIERARCHY (
          SOURCE
            SELECT empno, ename, mgr
              FROM emp
             WHERE mandt = SESSION_CONTEXT('CLIENT')
          START WHERE mgr IS NULL
          CONNECT BY PRIOR empno = mgr
        );
    ⚠️ 주의 HIERARCHY_PATH는 노드 key 기준 경로입니다. ename 값 기반 경로가 필요하면 CTE + STRING_AGG 조합으로 별도 구성해야 합니다.

    ④ CONNECT_BY_ROOT (루트 노드 값 참조)

    Oracle
    SELECT empno, ename,
           CONNECT_BY_ROOT ename AS root_name,
           LEVEL
      FROM emp
     START WITH mgr IS NULL
    CONNECT BY PRIOR empno = mgr;
    HANA SQLScript (CTE 방식)
    RETURN
      WITH hier AS (
        SELECT hierarchy_level    AS lv,
               empno, ename,
               hierarchy_rank     AS h_rank,
               hierarchy_root_rank AS root_rank
          FROM HIERARCHY (
            SOURCE SELECT empno, ename, mgr
                     FROM emp
                    WHERE mandt = SESSION_CONTEXT('CLIENT')
            START WHERE mgr IS NULL
            CONNECT BY PRIOR empno = mgr
          )
      ),
      roots AS (
        SELECT h_rank, ename AS root_name
          FROM hier WHERE lv = 1
      )
      SELECT h.lv AS "LEVEL",
             h.empno, h.ename,
             r.root_name
        FROM hier AS h
        JOIN roots AS r
          ON h.root_rank = r.h_rank;

    ⑤ CONNECT_BY_ISLEAF (리프 노드 여부)

    Oracle
    SELECT ename,
           CONNECT_BY_ISLEAF AS is_leaf,
           LEVEL
      FROM emp
     START WITH mgr IS NULL
    CONNECT BY PRIOR empno = mgr;
    HANA SQLScript
    RETURN
      SELECT ename,
             is_leaf,          -- 자동 생성 컬럼 (1=리프)
             hierarchy_level AS "LEVEL"
        FROM HIERARCHY (
          SOURCE SELECT empno, ename, mgr
                   FROM emp
                  WHERE mandt = SESSION_CONTEXT('CLIENT')
          START WHERE mgr IS NULL
          CONNECT BY PRIOR empno = mgr
        )
       WHERE is_leaf = 1;   -- 리프만 필터링 예시

    ⑥ CONNECT BY LEVEL (순번 · 날짜 시퀀스 생성)

    Oracle
    -- 1~12 순번
    SELECT LEVEL AS seq FROM DUAL
    CONNECT BY LEVEL <= 12;
    
    -- 이번 달 1일~말일 날짜
    SELECT TRUNC(SYSDATE,'MM') + LEVEL - 1 AS dt
      FROM DUAL
    CONNECT BY LEVEL <=
      TO_NUMBER(TO_CHAR(LAST_DAY(SYSDATE),'DD'));
    HANA SQLScript
    -- 1~12 순번
    RETURN
      SELECT GENERATED_PERIOD_START AS seq
        FROM SERIES_GENERATE_INTEGER(1, 1, 13);
        -- (간격, 시작, 끝+1)
    
    -- 이번 달 1일~말일 날짜
    RETURN
      SELECT GENERATED_PERIOD_START AS dt
        FROM SERIES_GENERATE_DATE(
          'DAY',
          ADD_DAYS(CURRENT_DATE,
            -(DAYOFMONTH(CURRENT_DATE)-1)),  -- 월초
          LAST_DAY(CURRENT_DATE),            -- 월말
          1
        );

    3. 자기 부서 포함 + 상위 전체 탐색 실무 핵심 패턴

    Oracle에서는 START WITH deptno = :v 만으로 자기 자신이 LEVEL=1에 자동 포함되지만, HANA에서는 역방향 탐색과 자기 포함 여부를 명시적으로 제어해야 합니다.

    Oracle 기존 코드

    -- 자기 부서(DEPTNO=30) + 상위 부서 전체
    SELECT LEVEL,
           deptno,
           dname,
           parent_deptno
      FROM dept
     START WITH deptno = 30               -- ← 자기 자신이 LEVEL=1로 자동 포함
    CONNECT BY PRIOR parent_deptno = deptno;  -- ← 역방향
    
    -- 결과:
    -- LEVEL=1  DEPTNO=30  (자기 자신)
    -- LEVEL=2  DEPTNO=20  (직속 상위)
    -- LEVEL=3  DEPTNO=10  (최상위)

    방법 1 ✅ 권장 — Recursive CTE

    자기 포함 + 역방향 탐색을 가장 명확하게 제어. HANA 모든 버전에서 안정적으로 동작합니다.

    METHOD get_ancestors
      BY DATABASE FUNCTION
      FOR HDB
      LANGUAGE SQLSCRIPT
      OPTIONS READ-ONLY
      USING zdept.
    
      RETURN
        WITH RECURSIVE ancestors AS (
    
          -- ① Base: 자기 자신 (depth = 0)
          SELECT deptno,
                 dname,
                 parent_deptno,
                 0 AS depth
            FROM zdept
           WHERE mandt  = SESSION_CONTEXT('CLIENT')
             AND deptno = :iv_deptno        -- ← 입력 부서코드
    
          UNION ALL
    
          -- ② Recursive: 상위 부서를 한 단계씩 올라감
          SELECT d.deptno,
                 d.dname,
                 d.parent_deptno,
                 a.depth + 1 AS depth
            FROM zdept     AS d
            JOIN ancestors AS a
              ON d.deptno          = a.parent_deptno
           WHERE d.mandt            = SESSION_CONTEXT('CLIENT')
             AND d.parent_deptno IS NOT NULL   -- 최상위 도달 시 종료
        )
        SELECT deptno,
               dname,
               parent_deptno,
               depth
          FROM ancestors
         ORDER BY depth ASC;   -- depth=0(자기)부터 최상위 순
    
    ENDMETHOD.
    ▶ 결과 예시 (iv_deptno = '30')
    depth=0 │ deptno=30 │ dname=영업팀     ← 자기 자신
    depth=1 │ deptno=20 │ dname=영업본부   ← 직속 상위
    depth=2 │ deptno=10 │ dname=전사       ← 최상위

    방법 2 — HIERARCHY() 역방향

    RETURN
      SELECT hierarchy_level AS depth,
             deptno,
             dname,
             parent_deptno
        FROM HIERARCHY (
          SOURCE
            SELECT deptno, dname, parent_deptno
              FROM zdept
             WHERE mandt = SESSION_CONTEXT('CLIENT')
          START WHERE deptno = :iv_deptno     -- 자기 자신이 level=1로 포함
          CONNECT BY PRIOR parent_deptno = deptno  -- ← 역방향
        )
       ORDER BY hierarchy_level;
    ⚠️ 주의 HIERARCHY() 역방향은 HANA 버전에 따라 동작이 다를 수 있습니다. 검증된 환경이 아니라면 Recursive CTE 방식을 권장합니다.

    방법 3 — HIERARCHY_ANCESTORS() SPS09+

    RETURN
      SELECT dist          AS depth,   -- 자기=0, 직속상위=1, ...
             deptno,
             dname,
             parent_deptno
        FROM HIERARCHY_ANCESTORS(
          HIERARCHY (
            SOURCE
              SELECT deptno, dname, parent_deptno
                FROM zdept
               WHERE mandt = SESSION_CONTEXT('CLIENT')
            CONNECT BY PRIOR deptno = parent_deptno
          )
          MATCH ( SOURCE SELECT deptno FROM zdept
                          WHERE mandt  = SESSION_CONTEXT('CLIENT')
                            AND deptno = :iv_deptno )
          WITH ANCESTOR CONDITION dist >= 0   -- 0이면 자기 자신도 포함
        )
       ORDER BY dist;
    💡 dist 옵션 dist >= 0 → 자기 자신 포함  |  dist >= 1 → 자기 제외, 상위만

    순환 참조(Cycle) 방어 — Recursive CTE

    실무 데이터에 상위부서가 서로 참조하는 잘못된 데이터가 있으면 무한루프가 발생합니다. 아래 패턴으로 반드시 방어하세요.

    WITH RECURSIVE ancestors AS (
    
      SELECT deptno,
             dname,
             parent_deptno,
             0                    AS depth,
             TO_NVARCHAR(deptno)  AS visited_path  -- 방문 경로 추적
        FROM zdept
       WHERE mandt  = SESSION_CONTEXT('CLIENT')
         AND deptno = :iv_deptno
    
      UNION ALL
    
      SELECT d.deptno,
             d.dname,
             d.parent_deptno,
             a.depth + 1,
             a.visited_path || ',' || TO_NVARCHAR(d.deptno)  -- 경로 누적
        FROM zdept     AS d
        JOIN ancestors AS a
          ON d.deptno = a.parent_deptno
       WHERE d.mandt             = SESSION_CONTEXT('CLIENT')
         AND d.parent_deptno  IS NOT NULL
         AND LOCATE( a.visited_path,
                     TO_NVARCHAR(d.deptno) ) = 0   -- ✅ 이미 방문한 노드 차단
         AND a.depth < 20                          -- ✅ 최대 깊이 제한 (안전장치)
    )
    SELECT deptno, dname, parent_deptno, depth
      FROM ancestors
     ORDER BY depth;

    방법 비교 요약

    Recursive CTE HIERARCHY() 역방향 HIERARCHY_ANCESTORS()
    자기 포함 ✅ Base절에서 명시적 포함 ✅ START WHERE ✅ dist >= 0
    HANA 버전 모든 버전 SPS07+ SPS09+
    순환 방어 ✅ 직접 제어 가능 ⚠️ IS_CYCLE 자동 ✅ 자동
    가독성 ✅ 높음 보통 보통
    권장도 ⭐⭐⭐ ⭐⭐ ⭐⭐

    4. AMDP 완성 코드 예제 — 조직도 조회

    CLASS zcl_amdp_org DEFINITION
      PUBLIC FINAL CREATE PUBLIC.
    
      PUBLIC SECTION.
        INTERFACES if_amdp_marker_hdb.
    
        TYPES: BEGIN OF ty_org,
                 level       TYPE i,
                 empno       TYPE n LENGTH 4,
                 ename       TYPE c LENGTH 20,
                 mgr         TYPE n LENGTH 4,
                 full_path   TYPE string,
                 is_leaf     TYPE abap_bool,
               END OF ty_org.
        TYPES tt_org TYPE TABLE OF ty_org.
    
        CLASS-METHODS get_org_tree
          IMPORTING VALUE(iv_start_empno) TYPE n
          EXPORTING VALUE(et_result)      TYPE tt_org.
    
    ENDCLASS.
    
    CLASS zcl_amdp_org IMPLEMENTATION.
    
      METHOD get_org_tree
        BY DATABASE FUNCTION   " ← TABLE FUNCTION: RETURN 문 필수
        FOR HDB
        LANGUAGE SQLSCRIPT
        OPTIONS READ-ONLY
        USING emp.
    
        RETURN
          SELECT
            hierarchy_level              AS level,
            TO_NVARCHAR(empno)           AS empno,
            ename,
            TO_NVARCHAR(mgr)             AS mgr,
            hierarchy_path               AS full_path,
            is_leaf
          FROM HIERARCHY (
            SOURCE
              SELECT empno, ename, mgr
                FROM emp
               WHERE mandt = SESSION_CONTEXT('CLIENT')
            START WHERE
              CASE
                WHEN :iv_start_empno = 0
                  THEN ( mgr IS NULL )            -- 전체 루트
                ELSE  ( empno = :iv_start_empno )
              END
            CONNECT BY PRIOR empno = mgr
          )
          ORDER BY hierarchy_rank;    -- 계층 출력 순서 보장
    
      ENDMETHOD.
    
    ENDCLASS.

    5. HIERARCHY() 자동 제공 컬럼 정리

    컬럼명타입설명Oracle 대응
    HIERARCHY_LEVEL INT 현재 깊이 (루트=1) LEVEL
    HIERARCHY_RANK INT 전위순회(pre-order) 순번
    HIERARCHY_PARENT_RANK INT 부모 노드의 RANK
    HIERARCHY_ROOT_RANK INT 루트 노드의 RANK CONNECT_BY_ROOT
    HIERARCHY_PATH NVARCHAR 루트→현재 경로 문자열 SYS_CONNECT_BY_PATH
    IS_LEAF TINYINT (0/1) 자식 없으면 1 CONNECT_BY_ISLEAF
    IS_CYCLE TINYINT (0/1) 순환 감지 시 1 CONNECT_BY_ISCYCLE

    6. 변환 체크리스트

    • BY DATABASE FUNCTION 으로 선언해야 RETURN 절 사용 가능 (TABLE FUNCTION)
    • 사용하는 테이블은 반드시 USING 절에 선언
    • START WITHSTART WHERE 키워드 변경 (WHERE 필수)
    • 모든 테이블에 WHERE mandt = SESSION_CONTEXT('CLIENT') 추가
    • LEVELHIERARCHY_LEVEL 컬럼으로 참조
    • CONNECT_BY_ISLEAFIS_LEAF 컬럼으로 참조
    • SYS_CONNECT_BY_PATHHIERARCHY_PATH 컬럼 (단, node key 기반)
    • CONNECT_BY_ROOTHIERARCHY_ROOT_RANK + CTE JOIN으로 처리
    • 순번 생성(CONNECT BY LEVEL) → SERIES_GENERATE_INTEGER
    • 날짜 시퀀스 → SERIES_GENERATE_DATE
    • 역방향 + 자기 포함 패턴 → Recursive CTE 권장 (가장 안정적)
    • Recursive CTE 사용 시 순환 참조 방어 (visited_path 추적 + depth < 20 안전장치)
    #SAP_ABAP #AMDP #SQLScript #HANA #Oracle_마이그레이션 #Hierarchy #CONNECT_BY #RecursiveCTE #계층쿼리
Designed by Tistory.