본문 바로가기
ABAP/개발Tip

[개발Tip] Internal Table 순차탐색과 Secondary Key 성능 비교

by name_text 2024. 11. 7.

Internal Table 순차탐색과 Secondary Key 성능 비교

standard Table with non-unique sorted key

sorted table with non-unique key

 

작년에 Internal Teble의 유형별로 성능 비교를 다룬적이 있었는데,

ABAP 개발시 여러 이유로 인해 Loop내에서 Internal Table을 순차탐색을 하거나 standard table을 secondary key 설정 없이 Loop where을 사용하여 경우가 종종 있는데, 이럴 경우 데이터 증가에 따라 성능이 기하급수적으로 나빠지므로 주의해야 합니다.

특히 이부분은 ABAP Performance Tuning시 기본적으로 조치해야 하는 부분입니다.

2023.05.16 - [ABAP/개발Tip] - [개발Tip] Internal Table 성능(Select into, Read, Loop where) 비교

 

[개발Tip] Internal Table 성능(Select into, Read, Loop where) 비교

Internal Table 성능(Select into, Read, Loop where) 비교 Standard Table, Sorted Table, Hashed Table SAP Internal Table에 대한 설명과 각 유형별 성능 비교 자료는 구글링을 하면 무수히 많이 나오며, Easy ABAP에도 상세시

playabap.tistory.com

 

그래서, 이번에는 실제 실무에서 많이 사용하는 Loop 내의 Read Table 과 Loop where에 대해

NON-UNIQUE KEY와 NON-UNIQUE SORTED KEY를 기준으로 성능 비교를 해봤습니다.

 

 

#1. 순차탐색, Sorted key, Secondary key, Hashed 성능 비교 (1만건 데이터 기준)

아래 결과는 절대적인 성능 비교표는 아닙니다, '순차탐색과 Sorted Key탐색의 차이가 이정도로 많이 나는구나~' 정도로만 이해하면 좋습니다.

 

#2. 결론

Internal Table의 키필드 값의 변경이 없다면 Non-Unique Sorted table

Append도 하고 Modify도 하고, ALV에도 사용해야 한다면 Standard table with Sorted Secondary key

 

Sorted table의 Non-Unique primary key는 선언한 키 Components를 다 사용하지 않아도 대부분의 상황에서 가장 준수한 성능을 제공합니다, 단 Insert 마다 Key필드를 기준으로 정렬이 되므로 속도가 느리며 Key 필드의 경우 값을 변경 할 수 없습니다.

Standard table의 경우에도 Secondary key를 잘 선언하여 사용하면 Sorted table에 근접한 성능을 내면서 Standard table의 장점인 빠른 Append와 Modify가 가능합니다.

Hashed Table의 경우 Unique key의 Components를 정확하게 사용하지 않으면 Table Key 탐색이 아닌 Free key 탐색이 되어 Standard Table의 순차탐색과 같은 성능을 내게 되어 주의해야 합니다.

 

#3. 테스트 소스코드

REPORT ydemo_sort_skey.

TYPES: BEGIN OF ts_result,
         step       TYPE i,
         category   TYPE string,
         text       TYPE string,
         rtime      TYPE i,
         rtime_unit TYPE string,
       END OF ts_result,
       tt_result TYPE STANDARD TABLE OF ts_result WITH EMPTY KEY.

DATA: lt_temp TYPE STANDARD TABLE OF dd03l,
      ls_temp LIKE LINE OF lt_temp.

DATA: lt_itab_standard_1 LIKE STANDARD TABLE OF ls_temp WITH NON-UNIQUE SORTED KEY skey1 COMPONENTS tabname rollname
                                                        WITH NON-UNIQUE SORTED KEY skey2 COMPONENTS tabname rollname fieldname,
      lt_itab_sorted_1   LIKE SORTED TABLE OF ls_temp WITH UNIQUE KEY tabname rollname fieldname,
      lt_itab_sorted_2   LIKE SORTED TABLE OF ls_temp WITH NON-UNIQUE KEY tabname rollname,
      lt_itab_sorted_3   LIKE SORTED TABLE OF ls_temp WITH NON-UNIQUE KEY tabname rollname fieldname,
      lt_itab_sorted_4   LIKE SORTED TABLE OF ls_temp WITH UNIQUE KEY tabname rollname fieldname
                                                      WITH NON-UNIQUE SORTED KEY skey1 COMPONENTS tabname rollname
                                                      WITH NON-UNIQUE SORTED KEY skey2 COMPONENTS tabname rollname fieldname,
      lt_itab_hashed_1   LIKE HASHED TABLE OF ls_temp WITH UNIQUE KEY tabname rollname fieldname.

DATA: lv_rtime  TYPE i,
      lt_result TYPE tt_result.

PARAMETERS p_count TYPE i OBLIGATORY DEFAULT 10000.

START-OF-SELECTION.
  FREE: lt_result.

  SELECT DISTINCT
    tabname,
    rollname,
    fieldname
  FROM dd03l
  WHERE as4local EQ 'A'
  INTO CORRESPONDING FIELDS OF TABLE @lt_temp
    UP TO @p_count ROWS.

  lt_itab_standard_1 = CORRESPONDING #( lt_temp ).
  lt_itab_sorted_1 = CORRESPONDING #( lt_temp ).
  lt_itab_sorted_2 = CORRESPONDING #( lt_temp ).
  lt_itab_sorted_3 = CORRESPONDING #( lt_temp ).
  lt_itab_sorted_4 = CORRESPONDING #( lt_temp ).
  lt_itab_hashed_1 = CORRESPONDING #( lt_temp ).

*--------------------------------------------------------------------*
*Standard Table
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_standard_1[ tabname = ls_temp-tabname
                                        rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Standard Table - 순차탐색'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_standard_1[ KEY skey1
                                        tabname = ls_temp-tabname
                                        rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Standard Table - Secondary key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_standard_1[ KEY skey2
                                        tabname = ls_temp-tabname
                                        rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Standard Table - Secondary key 일부 Components 사용'
                                lv_rtime.

*--------------------------------------------------------------------*
*Sorted Table
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_sorted_1[ tabname = ls_temp-tabname
                                      rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Sorted Table - Unique Key 일부 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_sorted_2[ tabname = ls_temp-tabname
                                      rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Sorted Table - Non-Unique Key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_sorted_3[ tabname = ls_temp-tabname
                                      rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Sorted Table - Non-Unique Key 일부 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_sorted_4[ KEY skey1
                                        tabname = ls_temp-tabname
                                        rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Sorted Table - Secondary key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_sorted_4[ KEY skey2
                                        tabname = ls_temp-tabname
                                        rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Sorted Table - Secondary key 일부 Components 사용'
                                lv_rtime.

*--------------------------------------------------------------------*
*Hashed Tabl
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_hashed_1[ tabname = ls_temp-tabname
                                      rollname = ls_temp-rollname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table' 'Hashed Table - Unique Key 일부 Components 사용'
                                lv_rtime.

*--------------------------------------------------------------------*
*unique key값으로 비교시
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_sorted_1[ tabname = ls_temp-tabname
                                      rollname = ls_temp-rollname
                                      fieldname = ls_temp-fieldname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table-유일값비교' 'Sorted Table - Unique Key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    IF line_exists( lt_itab_hashed_1[ tabname = ls_temp-tabname
                                      rollname = ls_temp-rollname
                                      fieldname = ls_temp-fieldname ] ).

    ENDIF.
  ENDLOOP.
  PERFORM determine_write USING 'Read Table-유일값비교' 'Hashed Table - Unique Key 전체 Components 사용'
                                lv_rtime.

**********************************************************************

*--------------------------------------------------------------------*
*Standard Table
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_standard_1 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                        AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Standard Table - 순차탐색'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_standard_1 TRANSPORTING NO FIELDS USING KEY skey1 WHERE tabname = ls_temp-tabname
                                                                        AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Standard Table - Secondary key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_standard_1 TRANSPORTING NO FIELDS USING KEY skey2 WHERE tabname = ls_temp-tabname
                                                                        AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Standard Table - Secondary key 일부 Components 사용'
                                lv_rtime.

*--------------------------------------------------------------------*
*Sorted Table
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_sorted_1 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                      AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Sorted Table - Unique Key 일부 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_sorted_2 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                      AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Sorted Table - Non-Unique Key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_sorted_3 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                      AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Sorted Table - Non-Unique Key 일부 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_sorted_4 TRANSPORTING NO FIELDS USING KEY skey1 WHERE tabname = ls_temp-tabname
                                                                      AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Sorted Table - Secondary key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_sorted_4 TRANSPORTING NO FIELDS USING KEY skey2 WHERE tabname = ls_temp-tabname
                                                                      AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Sorted Table - Secondary key 일부 Components 사용'
                                lv_rtime.

*--------------------------------------------------------------------*
*Hashed Tabl
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_hashed_1 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                      AND rollname = ls_temp-rollname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where' 'Hashed Table - Unique Key 일부 Components 사용'
                                lv_rtime.

*--------------------------------------------------------------------*
*unique key값으로 비교시
*--------------------------------------------------------------------*
  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_sorted_1 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                      AND rollname = ls_temp-rollname
                                                      AND fieldname = ls_temp-fieldname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where-유일값비교' 'Sorted Table - Unique Key 전체 Components 사용'
                                lv_rtime.

  GET RUN TIME FIELD lv_rtime.
  LOOP AT lt_temp INTO ls_temp.
    LOOP AT lt_itab_hashed_1 TRANSPORTING NO FIELDS WHERE tabname = ls_temp-tabname
                                                      AND rollname = ls_temp-rollname
                                                      AND fieldname = ls_temp-fieldname.
    ENDLOOP.
  ENDLOOP.
  PERFORM determine_write USING 'Loop where-유일값비교' 'Hashed Table - Unique Key 전체 Components 사용'
                                lv_rtime.

  "Result
  zcl_salv_output=>display( lt_result ).

*&---------------------------------------------------------------------*
*& Form DETERMINE_WRITE
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*&      --> P_
*&      --> LV_SDATE
*&      --> LV_STIME
*&---------------------------------------------------------------------*
FORM determine_write  USING    pv_category
                               pv_text
                               pv_rtime.
  DATA: lv_rtime TYPE i,
        lv_ftime TYPE i.
  GET RUN TIME FIELD lv_rtime.
  lv_ftime = lv_rtime - pv_rtime.

  APPEND VALUE #( step       = lines( lt_result ) + 1
                  category   = pv_category
                  text       = pv_text
                  rtime      = lv_ftime
                  rtime_unit = `MicroSecond` ) TO lt_result.
ENDFORM.

 

댓글