ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • AMDP SQLScript — CURSOR & ::ROWCOUNT 정리
    SAP 2026. 6. 8. 19:23
    AMDP SQLScript — CURSOR & ::ROWCOUNT 정리

    AMDP SQLScript — CURSOR & ::ROWCOUNT 완전 정리

    SAP HANA SQLScript · AMDP Procedure · BEGIN~END 내부 제어 흐름

    1. AMDP Procedure — BEGIN ~ END 구문

    AMDP Procedure는 SQLScript 로직을 BEGIN ~ END 블록 안에 작성합니다. Table Function의 RETURN SELECT ...; 방식과 다릅니다.

    구분Table FunctionProcedure
    ABAP 선언BY DATABASE FUNCTIONBY DATABASE PROCEDURE
    로직 시작RETURN SELECT ...;BEGIN ... END;
    DML 허용❌ 불가✅ 가능
    EXECUTE IMMEDIATE❌ 불가✅ 가능
    반환 방식RETURN 값EXPORTING 파라미터

    기본 구조

    METHOD my_procedure
      BY DATABASE PROCEDURE
      FOR HDB
      LANGUAGE SQLSCRIPT
      USING ztable_a ztable_b.
    
      DECLARE lv_count  INT;
      DECLARE lv_msg    NVARCHAR(200);
    
      BEGIN
    
        SELECT COUNT(*) INTO lv_count
          FROM ztable_a
          WHERE mandt    = SESSION_CONTEXT('CLIENT')
            AND bukrs    = :iv_bukrs;
    
        IF lv_count > 0 THEN
          UPDATE ztable_a
            SET status = 'X'
            WHERE mandt    = SESSION_CONTEXT('CLIENT')
              AND bukrs    = :iv_bukrs;
          ev_result := 'X';
        ELSE
          ev_result := ' ';
        END IF;
    
      END;  -- 세미콜론 필수!
    
    ENDMETHOD.
    ⚠️ END 뒤 세미콜론 필수 — 빠지면 활성화 오류 발생.
    중첩 BEGIN~END 에서도 동일 규칙 적용.

    2. CURSOR 사용법

    2-1. FOR … AS … DO (권장 ✅)

    OPEN / FETCH / CLOSE 없이 간결하게 사용. 실무에서 가장 많이 쓰는 패턴입니다.

    BEGIN
    
      DECLARE CURSOR lc_orders FOR
        SELECT vbeln, kunnr, netwr
          FROM vbak
          WHERE mandt = SESSION_CONTEXT('CLIENT')
            AND auart = :iv_auart;
    
      -- OPEN/CLOSE 불필요 — FOR 루프가 자동 처리
      FOR ls_row AS lc_orders DO
    
        UPDATE vbap
          SET zz_flag = 'X'
          WHERE mandt = SESSION_CONTEXT('CLIENT')
            AND vbeln = :ls_row.vbeln;   -- ls_row.컬럼명 으로 접근
    
      END FOR;
    
    END;

    2-2. OPEN / FETCH / CLOSE (탈출 조건이 필요한 경우)

    BEGIN
    
      DECLARE lv_key    NVARCHAR(20);
      DECLARE lv_amount DECIMAL(15,2);
    
      DECLARE CURSOR lc_main FOR
        SELECT key_field, amount
          FROM ztable_a
          WHERE mandt = SESSION_CONTEXT('CLIENT')
            AND bukrs = :iv_bukrs;
    
      OPEN lc_main;
    
      LOOP
        FETCH lc_main INTO lv_key, lv_amount;
    
        IF ::CURSOR_ROWCOUNT = 0 THEN   -- 0 = EOF
          BREAK;
        END IF;
    
        UPDATE ztable_b
          SET processed_amt = :lv_amount
          WHERE mandt     = SESSION_CONTEXT('CLIENT')
            AND key_field = :lv_key;
    
      END LOOP;
    
      CLOSE lc_main;
    
    END;

    2-3. CURSOR + 행별 에러 격리

    내부 BEGIN~EXCEPTION~END를 감싸면 한 행 오류 시 나머지 행을 계속 처리합니다.

    BEGIN
    
      DECLARE lv_err NVARCHAR(500);
    
      DECLARE CURSOR lc_queue FOR
        SELECT key_field, sql_text
          FROM ztask_queue
          WHERE mandt  = SESSION_CONTEXT('CLIENT')
            AND status = 'P';
    
      FOR ls_task AS lc_queue DO
    
        BEGIN  -- 행별 에러 격리 블록
          EXECUTE IMMEDIATE :ls_task.sql_text;
    
          UPDATE ztask_queue
            SET status = 'S'
            WHERE mandt     = SESSION_CONTEXT('CLIENT')
              AND key_field = :ls_task.key_field;
    
        EXCEPTION
          WHEN OTHERS THEN
            lv_err := ::SQL_ERROR_MESSAGE;
    
            UPDATE ztask_queue
              SET status  = 'E',
                  err_msg = :lv_err
              WHERE mandt     = SESSION_CONTEXT('CLIENT')
                AND key_field = :ls_task.key_field;
        END;  -- 에러가 나도 다음 행 계속 진행
    
      END FOR;
    
    END;

    2-4. 중첩 CURSOR

    BEGIN
    
      DECLARE CURSOR lc_header FOR
        SELECT vbeln FROM vbak
          WHERE mandt = SESSION_CONTEXT('CLIENT')
            AND vkorg = :iv_vkorg;
    
      FOR ls_hd AS lc_header DO
    
        DECLARE CURSOR lc_item FOR
          SELECT posnr, matnr, kwmeng
            FROM vbap
            WHERE mandt = SESSION_CONTEXT('CLIENT')
              AND vbeln = :ls_hd.vbeln;
    
        FOR ls_it AS lc_item DO
    
          UPDATE zresult
            SET qty = :ls_it.kwmeng
            WHERE vbeln = :ls_hd.vbeln
              AND posnr = :ls_it.posnr;
    
        END FOR;
    
      END FOR;
    
    END;

    3. 시스템 변수 — ::ROWCOUNT vs ::CURSOR_ROWCOUNT

    변수용도사용 위치
    ::ROWCOUNT 직전 DML(INSERT/UPDATE/DELETE)의 영향 행 수 DML 문 직후
    ::CURSOR_ROWCOUNT 직전 FETCH로 읽어온 행 수 (0 또는 1) FETCH 직후

    3-1. ::ROWCOUNT — UPDATE 처리 여부 확인

    BEGIN
    
      UPDATE ztable_a
        SET status = 'X'
        WHERE mandt     = SESSION_CONTEXT('CLIENT')
          AND key_field = :iv_key;
    
      IF ::ROWCOUNT = 0 THEN
        -- 해당 키 없음 → INSERT로 대체
        INSERT INTO ztable_a ( mandt, key_field, status )
          VALUES ( SESSION_CONTEXT('CLIENT'), :iv_key, 'X' );
      END IF;
    
    END;

    3-2. ::ROWCOUNT — DELETE 행 수 반환

    BEGIN
    
      DELETE FROM ztable_b
        WHERE mandt  = SESSION_CONTEXT('CLIENT')
          AND bukrs  = :iv_bukrs
          AND status = 'E';
    
      ev_deleted_count := ::ROWCOUNT;  -- 삭제된 행 수를 EXPORTING으로 반환
    
    END;

    3-3. ::ROWCOUNT — CURSOR 루프 안에서 즉시 캡처

    💡 ::ROWCOUNT는 DML 실행마다 즉시 갱신됩니다. 루프 안에서 사용할 때는 변수에 바로 저장해야 합니다.
    BEGIN
    
      DECLARE lv_affected INT;
    
      DECLARE CURSOR lc_items FOR
        SELECT key_field, new_value FROM ztask_list
          WHERE mandt = SESSION_CONTEXT('CLIENT');
    
      FOR ls_row AS lc_items DO
    
        UPDATE ztarget
          SET col1 = :ls_row.new_value
          WHERE mandt     = SESSION_CONTEXT('CLIENT')
            AND key_field = :ls_row.key_field;
    
        lv_affected := ::ROWCOUNT;  -- ← 즉시 캡처 (다음 DML 전에!)
    
        IF lv_affected = 0 THEN
          INSERT INTO zerror_log ( mandt, key_field, msg )
            VALUES ( SESSION_CONTEXT('CLIENT'), :ls_row.key_field, 'NOT FOUND' );
        END IF;
    
      END FOR;
    
    END;

    4. 패턴 선택 가이드

    상황권장 패턴
    일반적인 행별 처리FOR ... AS ... DO
    조건부 중간 탈출(BREAK) 필요OPEN / FETCH / CLOSE + LOOP
    행별 에러 격리FOR ... AS ... DO + 내부 BEGIN~EXCEPTION~END
    중첩 루프FOR ... AS ... DO 중첩
    DML 성공 여부 확인DML 직후 ::ROWCOUNT 체크
    EOF 감지 (FETCH 방식)::CURSOR_ROWCOUNT = 0 체크

    5. 주의사항 요약

    항목내용
    DECLARE CURSOR 위치BEGIN 블록 안에 선언 (블록 스코프)
    FOR 루프의 행 변수별도 DECLARE 불필요 — FOR ls_row AS ... 선언 시 자동 생성
    ::ROWCOUNT 덮어쓰기DML마다 즉시 갱신 → 변수에 바로 캡처 필요
    SELECT INTO와 ::ROWCOUNTSELECT INTO 후 ::ROWCOUNT는 의미 없음 (DML 전용)
    성능 주의대용량 데이터는 CURSOR보다 집합 연산(JOIN/UPDATE FROM SELECT) 권장
    #AMDP #SQLScript #CURSOR #ROWCOUNT #CURSOR_ROWCOUNT #ABAP_Procedure #SAP_HANA #BEGIN_END
Designed by Tistory.