ABAP SQL 튜닝 ③ - 전송량을 줄여라

ABAP SQL 튜닝 3편 배너

ABAP SQL 튜닝 시리즈 세 번째 글입니다. ②편을 줄이는 이야기였다면, 이번은 입니다.

SELECT * 의 비용

SELECT * 는 편합니다. 필드명을 안 적어도 되고, 나중에 다른 필드가 필요해도 코드를 안 고쳐도 됩니다. 그래서 습관이 되기 쉽습니다.

문제는 SAP 표준 테이블 상당수가 필드가 수십에서 수백 개라는 점입니다. 3개만 필요한데 200개를 가져오면, 그 차이가 그대로 비용이 됩니다.

1" ✗ 200개 필드를 다 가져와서 3개만 쓴다
2SELECT * FROM zsales
3  WHERE werks = @lv_werks
4  INTO TABLE @DATA(lt_all).
5
6" ✓ 필요한 것만
7SELECT vbeln, posnr, netwr
8  FROM zsales
9  WHERE werks = @lv_werks
10  INTO TABLE @DATA(lt_need).

비용은 한 곳이 아니라 여러 지점에 동시에 발생합니다.

구간SELECT * 의 손해
DB 읽기안 쓸 열까지 읽는다
네트워크안 쓸 데이터를 전송한다
ABAP 메모리내부 테이블 한 행이 커진다 → 대량이면 메모리 문제로 직결

컬럼 스토어에서는 더 중요하다

S/4HANA 기준에서 이 원칙의 무게가 더 커집니다.

행 저장(Row Store)한 행이 통째로 붙어 있다

어차피 행 단위로 읽으므로, 열을 몇 개 쓰든 읽기 비용 차이가 상대적으로 작습니다.

열 저장(Column Store)열마다 따로 저장된다

필요한 열만 읽으면 되는 구조입니다. 즉 열을 줄이면 읽기 비용이 실제로 줄어듭니다. 반대로 SELECT *는 이 장점을 통째로 버립니다.

HANA는 열 단위로 데이터를 압축해 보관합니다. 3개 열만 요청하면 그 3개만 풀어서 처리하면 됩니다. SELECT * 는 이 구조에서 얻을 수 있는 이점을 스스로 포기하는 셈입니다.

구조체를 맞출 때 주의

필드 리스트를 쓰면 결과 구조와 받는 변수의 구조를 맞춰야 합니다. 신 문법을 쓰면 이 부분이 편해집니다.

1" 인라인 선언 - 결과에 맞는 구조가 자동 생성된다
2SELECT vbeln, posnr, netwr
3  FROM zsales
4  WHERE werks = @lv_werks
5  INTO TABLE @DATA(lt_need).
6
7" 별도 타입을 쓸 때는 순서와 타입을 맞춘다
8TYPES: BEGIN OF ty_need,
9         vbeln TYPE zsales-vbeln,
10         posnr TYPE zsales-posnr,
11         netwr TYPE zsales-netwr,
12       END OF ty_need.
13DATA lt_need2 TYPE STANDARD TABLE OF ty_need.
14
15SELECT vbeln, posnr, netwr
16  FROM zsales
17  WHERE werks = @lv_werks
18  INTO TABLE @lt_need2.
TIP

필드를 CORRESPONDING FIELDS OF로 받으면 순서 불일치 걱정은 줄지만, 매핑 비용이 생깁니다. 대량 조회에서는 구조를 정확히 맞춰 그대로 받는 편이 낫습니다.

집계는 DB에서

전송량을 줄이는 가장 극적인 방법은 아예 안 가져오는 것입니다. 합계나 건수만 필요하다면, 원본 데이터를 다 받을 이유가 없습니다.

!ABAP에서 계산100만 건을 받아 더한다

100만 행이 네트워크를 건너오고 메모리에 쌓입니다. 결과는 숫자 하나인데 말입니다.

DB에서 계산숫자 하나만 받는다

DB가 집계해서 결과만 돌려줍니다. 전송량도 메모리도 사실상 0입니다.

1" ✗ 다 가져와서 ABAP에서 합산
2SELECT netwr FROM zsales
3  WHERE werks = @lv_werks
4  INTO TABLE @DATA(lt_amt).
5LOOP AT lt_amt INTO DATA(ls_amt).
6  lv_total = lv_total + ls_amt-netwr.
7ENDLOOP.
8
9" ✓ DB에서 합산
10SELECT SUM( netwr ) FROM zsales
11  WHERE werks = @lv_werks
12  INTO @DATA(lv_total2).
13
14" ✓ 그룹별 집계도 마찬가지
15SELECT werks, SUM( netwr ) AS total, COUNT(*) AS cnt
16  FROM zsales
17  WHERE erdat >= @lv_from
18  GROUP BY werks
19  INTO TABLE @DATA(lt_sum).

주요 집계 함수는 COUNT, SUM, AVG, MAX, MIN 입니다. 조건이 필요하면 HAVING을 함께 씁니다.

INFO

집계 함수를 쓰면 테이블 버퍼를 우회합니다. 소량 버퍼링 테이블에 대해서는 오히려 손해일 수 있으니, 대량 테이블에 쓰세요. 버퍼링 이야기는 ④편에서 다룹니다.

DISTINCT는 신중하게

중복 제거가 필요할 때 DISTINCT를 쓰게 되는데, DB가 정렬·비교 작업을 추가로 해야 하므로 공짜가 아닙니다.

1SELECT DISTINCT werks FROM zsales
2  WHERE erdat >= @lv_from
3  INTO TABLE @DATA(lt_werks).

정말 중복이 생기는 구조인지 먼저 확인하세요. 키를 제대로 지정했다면 애초에 중복이 안 나오는 경우도 많습니다. JOIN 때문에 중복이 생긴 것이라면, 조인 조건 쪽을 손보는 게 근본적입니다.

정리

  • SELECT * 를 기본값으로 쓰지 마세요. 필요한 필드만 명시합니다.
  • 손해는 DB 읽기·네트워크·ABAP 메모리 세 곳에서 동시에 발생합니다.
  • 컬럼 스토어에서는 효과가 더 큽니다. HANA의 구조적 장점을 살리는 방법입니다.
  • 합계·건수는 DB에서 계산해서 결과만 받으세요.
  • DISTINCT는 필요한지 먼저 따져보세요.

다음 ④편에서는 세 번째 황금률, DB 접근 횟수 줄이기를 다룹니다. 실무에서 가장 자주 만나는 성능 문제가 여기 있습니다.

Disclaimer — 이 포스트는 AI(Claude)를 활용하여 작성된 초안을 바탕으로 검수 및 보완하여 작성되었습니다. 내용 중 오류나 오타가 있다면 댓글로 알려주시면 감사하겠습니다.