728x90

 

SQL문 분석 with 옵티마이저

SQL과 실행 계획

- SQL은 처리 방법(절차)를 기술하지 않는다.(이렇게 저렇게 해라라는 방법에 대한 기술이 없다)

- 대신 옵티마이저(파서)라고 불리는 기능이 실행 계획(plan)이라는 처리 방법을 생성한다. 이 작업은 서버 프로세스SQL문 분석에 해당하는 작업이다.

- 실행 계획은 규칙 기반(rule base)과 비용 기반(cost base)라는 알고리즘을 가지고 생성한다. 하지만 규칙 기반은 더 이상 쓰이지 않아 비용 기반만 고려한다.

- 비용 기반이란 '처리 시간이나 I/O 횟수가 가장 적을 것으로 예상되는 처리 방법이 최상'이라는 알고리즘이다. 

- 이 비용을 계산하기 위해서 여러 통계 정보를 사용한다.

옵티마이저 동작 원리

- 비용 계산을 위해 데이터 딕셔너리 뷰의 USER_TAB_STATISTICS와 같은 통계 정보를 이용한다.

비용 계산에 필요한 정보들

실행 계획 수립의 한계와 공유 풀(Shared Pool)

 어떤 처리 방법이 가장 좋은지(비용이 적은지)를 판단하기 위해서는 모든 처리 방법의 비용을 비교해야 한다. 모든 처리 방법을 비교한다는 것은 수 많은 경우의 수에 대한 예상치를 계산해야하기 때문에 그 자체로 비용(자원)이 많이 든다. 즉, 분석에 드는 CPU 자원이 아까워지는 현상이 발생할 것이다. 그럼 이 실행 계획을 공유해서 자원 소비를 줄이는 방법을 자연스럽게 생각하게 된다. Ch3에서 이미 캐시와 공유 메모리에 대해 알아보았다. 이 실행계획도 서버 프로세스들이 서로 공유한다면 실행 계획을 수립하는 데 사용되는 자원을 줄일 수 있다. (*또한, 선정된 실행 계획이 무조건 가장 좋은 계획이 아닐 수 있다. 어디까지나 예상이기 때문이다. SQL 튜닝(인덱스 등을 활용한)을 통해 더 나은 실행 계획을 세우도록 유도할 수 있다.)

 공유 풀(Shared Pool)이라는 공간이 공유 메모리 영역(SGA)에 존재한다. 공유 메모리는 대부분 버퍼 캐시로 사용되고 남은 일부가 공유 풀로 사용되어 그 안에 통계 정보나 실행 계획 등의 캐시 데이터가 저장된다.

 실행 계획 등은 라이브러리 캐시(Libary Cache) 공간에 캐싱된다.

공유 풀의 구조

 

같은 SQL은 같은 실행 계획을 사용한다. 그러면 오라클은 어떻게 같은 SQL을 판단할까?

바인드 변수의 사용

SELECT ID, CUST_NAME, TEL_NO
  FROM CUST_INFO
 WHERE ID = '001'

SELECT ID, CUST_NAME, TEL_NO
  FROM CUST_INFO
 WHERE ID = '002'

-- 위 두 가지 SQL은 오라클이 다른 SQL로 취급한다. 오라클은 SQL문을 하나의 문자열로 간주하기 떄문이다.

SELECT ID, CUST_NAME, TEL_NO
  FROM CUST_INFO
 WHERE ID = :P1

SELECT ID, CUST_NAME, TEL_NO
  FROM CUST_INFO
 WHERE ID = :P1

-- 바인드 변수를 사용하여 SQL을 실행하면 오라클은 'P1'에 어떤 값이 담기든 같은 SQL로 간주한다.
-- 같은 SQL이 실행된 것으로 판단하여 이전에 캐시에 저장해둔 실행 계획을 가져와 SQL을 처리한다.

- Hard Parse : 공유 풀에 실행 계획이 없어 실행 계획을 새로 생성. 위 경우에 해당한다. 사용자(Client)는 같은 SQL이라고 생각해도 오라클은 그렇게 판단하지 않는다.

- Soft Parse : 공유 풀에 있는 실행 계획을 재사용. 아래 경우에 해당한다.

 

 이처럼 공유 풀과 바인드 변수를 사용하여 소프트 파스를 유도하여 실행 계획 수립에 대한 비용을 낮추는 방법이 사용된다.

 

공유 풀 정보 with statspack report

- Statspack은 오라클의 분석용 도구이다.

예시 정보
실제 예시

- 위 통계를 보고 parse를 위한 CPU사용량 등이 적절한 지에 대한 판단을 할 수 있을 것

정리

- SQL문은 처리 방법에 대한 기술이 없어 분석(parse)을 통해 처리 방법(실행 계획)을 수립한다.

- 실행 계획에도 좋고 나쁨이 있다. 

- 실행 계획을 생성하는 데 사용되는 비용을 줄이기 위해 공유 풀(라이브러리 캐시)에 실행 계획을 캐시해서 재활용.

728x90
728x90

캐시(Cache)

- 오라클 시스템 구조에서의 캐시를 살펴보기 이전에 컴퓨터 공학에서 사용되는 캐시라는 용어에 대한 일반적인 개념부터 살펴보는 게 좋다

컴퓨터 구조에서의 캐시 개념

https://www.geeksforgeeks.org/cache-memory-in-computer-organization/

Cache Memory is a special very high-speed memory. The cache is a smaller and faster memory that stores copies of the data from frequently used main memory locations. There are various different independent caches in a CPU, which store instructions and data. The most important use of cache memory is that it is used to reduce the average time to access data from the main memory. 

- 캐시는 캐시 메모리라고 불리며 빠른 속도의 메모리이다. 캐시 메모리는 메인 메모리에서 자주 사용되는 데이터를 더 빠르게 접근하기 위해 따로 저장해두는 메모리를 뜻한다.

CPU와 메모리 사이에 위치한 캐시 메모리

- 위는 컴퓨터 구조에서의 캐시 개념을 설명한 것이다. 일반적으로 캐시는 '자주 쓰이는 데이터를 더 빠르게 접근하도록 임시보관하는 메모리 공간'이라고 생각하면 된다. 

- 오라클 시스템에서의 캐시를 살펴보기 이전에 일반적인 캐시 개념을 이용해 유추해보자면 시간이 상당히 걸리는 디스크 접근 횟수를 줄이기 위해 자주 쓰이는 데이터를 캐시라는 메모리 공간에 두고 사용하지 않을까라는 추측을 할 수 있다.

 

오라클의 캐시(데이터 캐시 혹은 버퍼 캐시)

- 오라클의 캐시는 data cache 혹은 buffer cache라고 불린다. 서버 프로세스(클라이언트의 요청을 처리하는 오라클 프로세스)가 원하는 데이터가 캐시에 존재하면 그 데이터에 접근하여 DISK I/O를 줄인다. 즉, SQL의 처리 속도를 높인다.

 

오라클 캐시의 데이터 관리 단위 : 블록

- 오라클은 'block'이라는 단위로 데이터를 관리한다. 디스크 I/O 및 버퍼 캐시 모두 블록 단위로 관리한다.(OS에도 블록이라는 개념이 있지만 이것과는 다르다)

- 데이터를 한 개만 꺼내오려고 해도 필요한 데이터를 포함한 블록 자체가 캐시에 보관된다. 2KB, 4KB, 8KB, 16KB, 32KB 등 블록 사이즈를 고를 수 있다. 

Multi Level Index 구조

- 그림과 같이 인덱스가 단계적으로 되어 있을 때 인덱스를 이용해 데이터를 구하기 위해서는 Outer인덱스의 블록에서 데이터를 가져와(DISK IO 1회) Inner 인덱스 블록을 찾으러 다시 디스크에 접근한다(DISK IO 2회). 그리고 나서 데이터의 주소를 가져와 데이터 블록에 다녀온다.(DISK IO 3회). 위와 같은 구조에서는 디스크에 총 3번을 접근해야 원하는 데이터에 접근할 수 있다.

- 이 때, 원하는 데이터가 데이터 캐시(버퍼 캐시)에 존재한다면, DISK에 접근을 한 번도 하지 않고 디스크 접근이라는 물리적인 움직임 없이 전기적인 신호만으로 클라이언트의 요청에 대한 응답이 가능하다. 이것이 캐시를 이용하는 이유이다.

 

 공유 메모리(SGA;System Global Area)

- 프로세스는 캐시를 공유한다. 기본적으로 다른 프로세스의 메모리를 보는 것을 불가능하다. 데이터에 손상을 입히지 않도록 OS가 제한을 두기 때문이다. DBMS는 이런 불편한 점을 해결하고 다른 프로세스와 데이터를 공유하기 위해 OS의 기능 중 특수한 메모리 기능인 '공유 메모리'를 사용한다. 즉 자신의 캐시를 다른 프로세스도 볼 수 있게 되고 자신 또한 다른 프로세스의 캐시를 볼 수 있게 된다. 이런 공유 메모리 영역을 오라클에서 SGA(System Global Area)라고 불린다. 공유되지 않는 메모리는 PGA(Program Global Area, 이 명칭은 Process가 Program이 실행된 형태를 의미하는 것을 안다면 쉽게 와닿는다)

- 메모리의 데이터를 공유하기 떄문에 데이터에 손상을 가하지 않도록 락을 걸어 배타 제어(exclusive control)을 DBMS가 내부적으로 수행한다. 예를 들어, 동시에 같은 데이터를 변경하려고 하는 등의 시도를 발생하지 않도록 방지하는 것이다.

오라클 시스템 구조
책 참고 이미지

버퍼 캐시를 정리하는 LRU 알고리즘

- 정보처리기사에서 페이징 처리 알고리즘에서 배웠던 LRU 알고리즘이 버퍼 캐시를 정리하기 위해 쓰인다. 간단하게 최근에 사용하지 않은 데이터부터 캐시 아웃(버리기)하는 것이다.

데이터 변경도 캐시에서 이루어진다

- 데이터를 변경(update)하는 것과 같은 요청 또한 캐시에서 선제적으로 이루어진다. 설명하자면 클라이언트의 데이터 변경(UPDATE)요청 시 서버 프로세스는 디스크에 직접 변경된 데이터를 전달하여 기록하지 않고 버퍼 캐시에 읽어온 데이터를 변경하여 놔둔다. 이러면 디스크 IO없이 UPDATE SQL요청을 빠르게 처리하고 클라이언트에게 작업완료 응답을 보낼 수 있다. 

- 서버 프로세스가 캐시에 변경된 데이터를 두면 백그라운드 프로세스인 DBWR가 캐시에서 변경된 데이터가 버려지기 전에 디스크에 기록한다. 디스크에 부하를 주지 않기위해 정기적으로 수행한다.

 

모든 것을 버퍼 캐시에 두지는 않는다

- 오라클은 큰 테이블이라고 판단하면 풀 스캔한 것을 버퍼 캐시에 오랜 시간 보관하지 않도록 하고 있다. 일반적으로 풀 스캔 시의 데이터는 버퍼 캐시에 적재되지 않는다고 생각하면 된다.

- 큰 테이블, 작은 테이블 등의 기준은 '_small_table_threshold' 과 같은 설정값을 바꾸어 조정할 수 있다. 

 

스토리지 캐시와 OS의 가상메모리

스토리지라는 저장장치의 캐시를 이용하면 IO 시간을 줄일 수 있다

스토리지 캐시 사용 시 구조

- 스토리지의 캐시에만 데이터 CRUD를 하면 OS입장에서는 스토리지가 캐시 데이터를 알아서 디스크에 기록할 테니 작업이 끝난 것으로 간주할 수 있다. 즉, DBMS입장에서도 디스크 IO가 실제로 이루어지지 않고 스토리지의 캐시에만 기록을 적재한 것이지만 디스크 IO가 끝난 것으로 간주하여 응답을 빠르게 할 수 있다.

- 이렇듯 서버 프로세스가 버퍼 캐시를 사용하는 것만이 데이터 처리의 효율성을 증가시키는 것이 아니라 스토리지 레벨에서도 효율성을 증가시키는 방법이 있다는 것을 알 수 있다.

 

 

가상 메모리 때문에 버퍼 캐시가 항상 속도를 증가시키는 것이라고 단언할 수 없다.

- 가상 메모리란 메모리에서 사용 빈도가 낮은 데이터를 물리적인 디스크에 보관하고 메모리에 있는 것처럼 간주하는 기술이다. 이는 OS 레벨에서의 기능이기 때문에 DBMS는 이를 고려하지 않을 것이다.

 

- 그렇다면 버퍼 캐시가 사용 가능한 물리 메모리의 용량보다 크게 설정되어 있다면 어떻게 될까?

- 그렇게 되면 OS는 가상메모리 기술을 사용해 버퍼 캐시가 사용할 초과분의 용량을 디스크에 마련해 이를 메모리처럼 사용하게 할 것이다.

- 그리고 이 가상 메모리 공간에는 버퍼 캐시에서 잘 사용되지 않는 데이터를 보관하게 하는 것이 가장 효율적일 것이다. 그러다 서버 프로세스가 가상 메모리에 있는 데이터를 요청하면 어떻게 될까?

- 가상 메모리는 디스크에 있기 때문에 디스크 IO가 일어날 것이다. 그러면 서버 프로세스 입장에서는 캐시에만 접근해서 데이터를 가져왔는데도 불구하고 디스크 IO가 일어난 것과 같은 속도로 처리되어 캐시를 사용하는 의미가 없어지게 된다. 즉 아무런 효과도 없는 현상이 발생한다. 가상 메모리 개념과 버퍼 캐시의 개념이 서로 상충하기 때문이다.

- 최근에는 메모리 값이 저렴해지고 용량이 커졌기 때문에 가상 메모리를 사용하지 않는 것을 권고한다. 이렇게 OS레벨에서의 기술과 DBMS레벨에서의 기술이 서로 상충되면 문제를 해결하기 어렵기 때문이 아닐까 싶다

 

 

*OS의 버퍼 캐시도 고려해야 한다. 

- OS에도 파일 캐시나 페이지 캐시라고 불리는 버퍼 캐시가 있다. 오라클의 버퍼 캐시와 같은 역할을 수행한다. 그렇기 때문에 오라클을 재기동하고 오라클의 버퍼 캐시를 비워도 OS가 재기동되지 않았다면 OS 버퍼 캐시에 데이터가 계속 남아 성능 측정에 오류가 생길 수 있다.(OS의 버퍼 캐시에서 데이터를 가져왔는데 디스크IO한 것이라고 판단할 수가 있다.)

 

 

728x90
728x90

인터널 테이블 명령어

MOVE

TYPES : BEGIN OF T_LINE,
  COL1 TYPE I,
  COL2 TYPE I,
  END OF T_LINE.

DATA : GT_ITAB1 TYPE STANDARD TABLE OF T_LINE WITH HEADER LINE.
DATA : GT_ITAB2 TYPE STANDARD TABLE OF T_LINE.
DATA : GS_WA LIKE LINE OF GT_ITAB2.

DO 5 TIMES.
  GT_ITAB1-COL1 = SY-INDEX.
  GT_ITAB1-COL2 = SY-INDEX * 2.
  INSERT TABLE GT_ITAB1.
ENDDO.

MOVE GT_ITAB1[] TO GT_ITAB2.

LOOP AT GT_ITAB2 INTO GS_WA.
  WRITE : / GS_WA-COL1, GS_WA-COL2.
ENDLOOP.

 

CLEAR, REFRESH, FREE

- 헤더 라인이 없으면 3가지 모두 인터널 테이블의 BODY를 삭제한다.

- 헤더 라인이 있으면 CLEAR는 헤더 라인을, REFRESH와 FREE는 바디를 삭제한다.

 

- CLEAR : 데이터를 지우고 메모리 공간을 반환(RELEASSE)한다.

- REFRESH : 테이블의 내용만 삭제하고 메모리는 가지고 있는다.

- FREE : 메모리 공간을 반환한다.

- 대부분 CLEAR를 쓰고 헤더가 있는 경우 바디를 삭제할 때는 ITAB[]를 사용하여 바디를 명시하면 바디의 데이터를 삭제한다. 

 

SORT

DATA : BEGIN OF GS_LINE,
  COL1 TYPE C,
  COL2 TYPE I,
  END OF GS_LINE.


DATA : GT_ITAB LIKE STANDARD TABLE OF GS_LINE WITH NON-UNIQUE KEY COL1.

GS_LINE-COL1 = 'B'.
GS_LINE-COL2 = 3.
APPEND GS_LINE TO GT_ITAB.

GS_LINE-COL1 = 'C'.
GS_LINE-COL2 = 4.
APPEND GS_LINE TO GT_ITAB.

GS_LINE-COL1 = 'A'.
GS_LINE-COL2 = 2.
APPEND GS_LINE TO GT_ITAB.

GS_LINE-COL1 = 'A'.
GS_LINE-COL2 = 1.
APPEND GS_LINE TO GT_ITAB.

GS_LINE-COL1 = 'B'.
GS_LINE-COL2 = 1.
APPEND GS_LINE TO GT_ITAB.

SORT GT_ITAB.
PERFORM WRITE_DATA.

SORT GT_ITAB BY COL1 COL2.
PERFORM WRITE_DATA.

SORT GT_ITAB BY COL1 DESCENDING COL2 ASCENDING.
PERFORM WRITE_DATA.

SORT GT_ITAB BY COL2 COL1.
PERFORM WRITE_DATA.

FORM WRITE_DATA.
  LOOP AT GT_ITAB INTO GS_LINE.
    WRITE : / GS_LINE-COL1, GS_LINE-COL2.
  ENDLOOP.
  ULINE.
ENDFORM.

- SQL의 ORDER BY와 크게 다르지 않다.

- 그냥 SORT 명령을 실행하면 KEY 컬럼인 COL1을 기준으로 정렬을 수행한다.

 

DESCRIBE : LINES(현재 라인 수), OCCURS(초기 라인수), KIND(종류)

 

 

인터널 테이블 데이터 추가

한 라인 씩 추가, 여러 라인 추가

DATA : BEGIN OF GS_LINE,
  COL1 TYPE C,
  COL2 TYPE I,
  END OF GS_LINE.


DATA : GT_ITAB1 LIKE STANDARD TABLE OF GS_LINE WITH NON-UNIQUE KEY COL1.
DATA : GT_ITAB2 LIKE SORTED TABLE OF GS_LINE WITH NON-UNIQUE KEY COL1.

GS_LINE-COL1 = 'B'.
GS_LINE-COL2 = 1.
INSERT GS_LINE INTO TABLE GT_ITAB1.

GS_LINE-COL1 = 'A'.
GS_LINE-COL2 = 2.
INSERT GS_LINE INTO TABLE GT_ITAB1.

GS_LINE-COL1 = 'C'.
GS_LINE-COL2 = 3.
INSERT GS_LINE INTO TABLE GT_ITAB1. " 한 라인 추가

INSERT LINES OF GT_ITAB1 INTO TABLE GT_ITAB2. " 여러 라인 추가

LOOP AT GT_ITAB2 INTO GS_LINE.
  WRITE : / GS_LINE-COL1, GS_LINE-COL2.
ENDLOOP.

 

- SORTED TABLE에 INSERT하면 자동으로 정렬을 한다.

- STANDARD TABLE은 APPEND 구문과 같이 테이블 마지막에 삽입된다.

- APPEND는 STANDARD TABLE에만 쓸 수 있다. SORTED에는 순서대로 삽입해준다면 사용 가능하다.

 

*INDEX 구문을 이용하면 특정 위치에 라인을 삽입할 수도 있다. 잘 쓰이진 않을 듯 하다.

 

APPEND INITIAL LINE TO ITAB : 빈 공간 미리 생성하여 라인 추가

SORTED BY : 정렬을 수행하고 추가함. SIZE에 한계가 있어도 정렬 후 추가하기 때문에 SIZE가 꽉 찬 후 추가하더라도 순서상 앞에 와야 한다면 기존 데이터를 밀어내고 자리를 차지한다.

 

COLLECT 구문

- 인터널 테이블의 숫자 타입 칼럼을 합산하는 기능 수행

- KEY 값을 제외한 칼럼들은 Numeric Type(f, i, p)으로 선언되어야 한다. 같은 key 값이 있을 때는 숫자 타입 컬럼을 합산하고 없을 때에는 APPEND한다.

- Key 값이 없는 테이블에 COLLECT를 수행하면 CHAR 타입 컬럼들을 기준으로 같은 작업을 수행한다.

- COLLECT WA INTO ITAB.

 

DATA : BEGIN OF GS_LINE,

  COL1(3) TYPE C,
  COL2(2) TYPE N,
  COL3 TYPE I,
  END OF GS_LINE.


DATA : GT_ITAB LIKE STANDARD TABLE OF GS_LINE WITH NON-UNIQUE KEY COL1 COL2.

GS_LINE-COL1 = 'AA'.
GS_LINE-COL2 = '17'.
GS_LINE-COL3 = 660.

COLLECT GS_LINE INTO GT_ITAB.

GS_LINE-COL1 = 'AL'.
GS_LINE-COL2 = '34'.
GS_LINE-COL3 = 220.

COLLECT GS_LINE INTO GT_ITAB.

GS_LINE-COL1 = 'AA'.
GS_LINE-COL2 = '17'.
GS_LINE-COL3 = 280.

COLLECT GS_LINE INTO GT_ITAB.

LOOP AT GT_ITAB INTO GS_LINE.

  WRITE : / GS_LINE-COL1, GS_LINE-COL2, GS_LINE-COL3.

ENDLOOP.

- COL1, COL2 를 기준으로 값이 합산된 것을 확인할 수 있다.

 

 

DATA : BEGIN OF GS_LINE,
  CARRID TYPE SFLIGHT-CARRID,
  CONNID TYPE SFLIGHT-CONNID,
  PAYMENTSUM TYPE SFLIGHT-PAYMENTSUM,

  END OF GS_LINE.

DATA : GT_ITAB LIKE TABLE OF GS_LINE WITH NON-UNIQUE KEY CARRID CONNID WITH HEADER LINE.

DATA : GT_SUM LIKE TABLE OF GS_LINE WITH NON-UNIQUE KEY CARRID CONNID WITH HEADER LINE.

SELECT CARRID CONNID PAYMENTSUM
  INTO CORRESPONDING FIELDS OF TABLE GT_ITAB
  FROM SFLIGHT.


LOOP AT GT_ITAB.
  COLLECT GT_ITAB INTO GT_SUM.
ENDLOOP.

LOOP AT GT_SUM.
  WRITE : / GT_SUM-CARRID, GT_SUM-CONNID, GT_SUM-PAYMENTSUM.

ENDLOOP.

결과

인터널 테이블 데이터 변경

단건 변경

 
DATA : BEGIN OF GS_LINE,
  COL1(2) TYPE C,
  COL2 TYPE I,
  COL3 TYPE SY-DATUM,
  END OF GS_LINE.

DATA : GT_ITAB LIKE STANDARD TABLE OF GS_LINE WITH NON-UNIQUE KEY COL1 COL2.

GS_LINE-COL1 = 'AA'.
GS_LINE-COL2 = 50.
INSERT GS_LINE INTO TABLE GT_ITAB.

GS_LINE-COL1 = 'AA'.
GS_LINE-COL2 = 26.
INSERT GS_LINE INTO TABLE GT_ITAB.

GS_LINE-COL1 = 'AA'.
GS_LINE-COL2 = 50.
GS_LINE-COL3 = SY-DATUM.
MODIFY TABLE GT_ITAB FROM GS_LINE.

LOOP AT GT_ITAB INTO GS_LINE.
  WRITE : / GS_LINE-COL1, GS_LINE-COL2, GS_LINE-COL3.

ENDLOOP.

* AA         50  2020.10.29
* AA         26  0000.00.00

 

여러 건 변경

DATA : BEGIN OF GS_LINE,
  CARRID TYPE SFLIGHT-CARRID,
  CARRNAME TYPE SCARR-CARRNAME,
  FLDATA  TYPE SFLIGHT-FLDATE,

  END OF GS_LINE.

DATA : GT_ITAB LIKE TABLE OF GS_LINE.

SELECT CARRID CONNID
  INTO CORRESPONDING FIELDS OF TABLE GT_ITAB
  FROM SFLIGHT.

LOOP AT GT_ITAB INTO GS_LINE.

  AT NEW CARRID. " 새로운 GT_LINE-CARRID 일 때
    SELECT SINGLE CARRNAME INTO GS_LINE-CARRNAME
      FROM SCARR WHERE CARRID = GS_LINE-CARRID. " CARRID로 CARRNAME 가져오기

    MODIFY GT_ITAB FROM GS_LINE TRANSPORTING CARRNAME
" TRANSPORTING은 SQL의 SET과 같음 GT_ITAB-CARRNAME = GS_LINE-CARRNAME
    WHERE CARRID = GS_LINE-CARRID. " WHERE 조건으로 GT_ITAB의 여러 건 필터 후 변경

  ENDAT.

  WRITE : / GS_LINE-CARRID, GS_LINE-CARRNAME.

ENDLOOP.

 

AT 구문 정리

- AT FIRST : 인터널 테이블의 첫 번째 값이 실행될 때 수행

- AT NEW F1 : 컬럼 F1에 새로운 값이 들어올 때 수행

- AT END OF F1 : 컬럼 F1의 값이 마지막일 때 수행

- AT LAST  : 인터널 테이블의 마지막 값이 실행될 때 수행

 

 

 

INDEX를 이용해 한 라인 변경

DATA : BEGIN OF GS_LINE,
  CARRID TYPE SFLIGHT-CARRID,
  CARRNAME TYPE SCARR-CARRNAME,
  FLDATE TYPE SFLIGHT-FLDATE,
  END OF GS_LINE.

DATA : GT_ITAB LIKE TABLE OF GS_LINE.

SELECT CARRID FLDATE
  INTO CORRESPONDING FIELDS OF TABLE GT_ITAB
  FROM SFLIGHT.

LOOP AT GT_ITAB INTO GS_LINE.

  SELECT SINGLE CARRNAME
    INTO GS_LINE-CARRNAME
    FROM SCARR
   WHERE CARRID = GS_LINE-CARRID.

  MODIFY GT_ITAB FROM GS_LINE INDEX SY-TABIX.

  WRITE : / GS_LINE-CARRID, GS_LINE-CARRNAME.

ENDLOOP.

 

-  위 굵은 표시 구문에서 INDEX SY-TABIX를 지워도 결과는 같다. 즉, 해당 문구가 LOOP 안의 MODIFY에는 항상 생략되어 있다. 

 

인터널 테이블 데이터 삭제

TABLE KEY를 이용한 한 라인 삭제

- DELETE TABLE ITAB WITH TABLE KEY K1 =  F1 .... KN = FN.

 

WHERE 조건을 이용한 여러 라인 삭제

- DELETE ITAB WHERE F1 = V1.

 

INDEX를 이용한 삭제

- DELETE ITAB INDEX N.

 

 

ADJACENT DUPLICATE 구문을 이용한 중복 라인 삭제

DATA : BEGIN OF GS_LINE,
  CARRID TYPE SFLIGHT-CARRID,
  CONNID TYPE SFLIGHT-CONNID,

  END OF GS_LINE.

DATA GT_ITAB LIKE TABLE OF GS_LINE WITH HEADER LINE.

SELECT CARRID CONNID INTO CORRESPONDING FIELDS OF TABLE GT_ITAB
  FROM SFLIGHT.

DELETE ADJACENT DUPLICATES FROM GT_ITAB.

LOOP AT GT_ITAB.

  WRITE : / GT_ITAB-CARRID, GT_ITAB-CONNID.

ENDLOOP.

중복 제거(왼쪽), 중복 제거 x(오른쪽)

 

 

인터널 테이블 읽기

- 인터널 테이블을 읽을 때는 READ 구문을 사용한다

 

KEY 값을 이용한 READ

DATA : BEGIN OF GS_LINE,
  CARRID TYPE SCARR-CARRID,
  CARRNAME TYPE SCARR-CARRNAME,
  END OF GS_LINE.

DATA : GT_ITAB LIKE TABLE OF GS_LINE WITH NON-UNIQUE KEY CARRID.

SELECT CARRID CARRNAME
  INTO CORRESPONDING FIELDS OF TABLE GT_ITAB
  FROM SCARR.

GS_LINE-CARRID = 'AA'.
READ TABLE GT_ITAB FROM GS_LINE INTO GS_LINE.

WRITE : / GS_LINE-CARRID, GS_LINE-CARRNAME.

CLEAR : GS_LINE.

READ TABLE GT_ITAB WITH TABLE KEY CARRID = 'AB' INTO GS_LINE.

WRITE : / GS_LINE-CARRID, GS_LINE-CARRNAME.

 

WORK AREA 할당

1. READ 구문의 COMPARING 옵션

DATA : BEGIN OF GS_LINE,
  COL1 TYPE C,
  COL2 TYPE I,

  END OF GS_LINE.

DATA : GT_ITAB LIKE SORTED TABLE OF GS_LINE WITH UNIQUE KEY COL1.

GS_LINE-COL1 = 'A'.
GS_LINE-COL2 = 1.
INSERT GS_LINE INTO TABLE GT_ITAB.

CLEAR GS_LINE.

GS_LINE-COL1 = 'B'.
GS_LINE-COL2 = 2.
INSERT GS_LINE INTO TABLE GT_ITAB.

CLEAR GS_LINE.

GS_LINE-COL1 = 'A'.

WRITE : / 'BEFORE READ : ', GS_LINE-COL1, GS_LINE-COL2.

READ TABLE GT_ITAB FROM GS_LINE INTO GS_LINE COMPARING COL2.

WRITE : / 'SY-SUBRC : ', SY-SUBRC. " 값이 달라서 2 반환

WRITE : / 'RESULT : ', GS_LINE-COL1, GS_LINE-COL2.

 

 

2. READ 구문의 TRASNPORTING 옵션

- TRANSPORTING은 READ한 결과를 해당 컬럼만 TARGET에 저장하는 기능을 수행한다.

DATA : BEGIN OF GS_LINE,
  COL1 TYPE C,
  COL2(7) TYPE C,

  END OF GS_LINE.

DATA : GT_ITAB LIKE SORTED TABLE OF GS_LINE WITH UNIQUE KEY COL1.
DATA : GS_WA LIKE LINE OF GT_ITAB.

GS_LINE-COL1 = 'A'.
GS_LINE-COL2 = 'FIRST'.ㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇ
INSERT GS_LINE INTO TABLE GT_ITAB.
CLEAR GS_LINE.

GS_LINE-COL1 = 'B'.
GS_LINE-COL2 = 'SECOND'.
INSERT GS_LINE INTO TABLE GT_ITAB.
CLEAR GS_LINE.

GS_LINE-COL1 = 'A'.

READ TABLE GT_ITAB WITH TABLE KEY COL1 = 'A' INTO GS_WA TRANSPORTING COL2.

WRITE : / 'COL1 : ', GS_WA-COL1, ' COL2 : ', GS_WA-COL2.

 

- COL2에만 값이 삽입된다. GS_LINE을 CLEAR하고 COL2만 TRANSPORTING하는 듯

 

인덱스 활용 READ구문

READ TABLE GT_ITAB INDEX 1.

 

READ BINARY SEARCH

- STANDARD TYPE의 인터널 테이블을 READ할 때 이용 가능. 대상 컬럼을 기준으로 SORT한 후 사용하며 READ 속도가 일반 READ 속도보다 따르다.

DATA : BEGIN OF GS_LINE,
  CARRID TYPE SFLIGHT-CARRID,
  CARRNAME TYPE SCARR-CARRNAME,

  END OF GS_LINE.

DATA : GT_ITAB LIKE TABLE OF GS_LINE.
DATA : GT_SCARR LIKE TABLE OF SCARR WITH HEADER LINE.

SELECT CARRID CONNID
  INTO CORRESPONDING FIELDS OF TABLE GT_ITAB
  FROM SFLIGHT.

SELECT CARRID CARRNAME
  INTO CORRESPONDING FIELDS OF TABLE GT_SCARR
  FROM SCARR.

LOOP AT GT_ITAB INTO GS_LINE.
  READ TABLE GT_SCARR WITH KEY CARRID = GS_LINE-CARRID BINARY SEARCH.

  GS_LINE-CARRNAME = GT_SCARR-CARRNAME.
  MODIFY GT_ITAB FROM GS_LINE.

  WRITE : / GS_LINE-CARRID, GS_LINE-CARRNAME.
ENDLOOP.

- 매번 테이블에 접근하지 않고 테이블의 모든 데이터를 인터널 테이블에 저장한 후에 BINARY SEARCH를 이용해 항공사 이름을 할당하여 속도가 더 빠르다. 그 이유는 데이터베이스에 여러번 다녀오지 않고 APPLICATION SERVER에서 인터널 테이블을 활용하기 때문이다.

728x90
728x90

Internal Table이란 C언어의 구조체 배열이나 자바의 객체 배열를 생각하면 된다. (같은 용도, 같은 개념으로 쓰인다)

 

생성 방법

1. Local Table Tyep을 이용한 인터널 테이블 생성

2. Global ABAP Dictionary Type을 이용한 인터널 테이블 생성

 

1번 방법

TYPES : BEGIN OF S_TYPE,

  NO(6) TYPE C,
  NAME(10) TYPE C,
  PART(16) TYPE C,
  END OF S_TYPE.

DATA GT_ITAB TYPE STANDARD TABLE OF S_TYPE WITH NON-UNIQUE KEY NO WITH HEADER LINE.

GT_ITAB-NO = '0001'.
GT_ITAB-NAME = 'EASY ABAP'.
GT_ITAB-PART = 'SAP TEAM'.
APPEND GT_ITAB.

GT_ITAB-NO = '0002'.
GT_ITAB-NAME = 'EASY ABAP2'.
GT_ITAB-PART = 'SAP TEAM2'.
APPEND GT_ITAB.

LOOP AT GT_ITAB.
  WRITE : / GT_ITAB-NO, GT_ITAB-NAME, GT_ITAB-PART.
ENDLOOP.
 
 
2번 방법
DATA : GT_ITAB TYPE SORTED TABLE OF SCARR WITH UNIQUE KEY CARRID.
DATA : GS_STR LIKE LINE OF GT_ITAB.

SELECT *
  INTO TABLE GT_ITAB
  FROM SCARR.

LOOP AT GT_ITAB INTO GS_STR.
  WRITE : / GS_STR-CARRID, GS_STR-CARRNAME.
ENDLOOP.

 

인터널 테이블과 헤더 라인

- 헤더라인 = WORK AREA, 인터널 테이블 선언 시 WITH HEADER LINE 구문 추가하면 됨

- 헤더라인이 있으면 Internal Table 이름이 그대로 헤더 라인을 뜻하게 된다.

 

- 헤더라인이 있을 때의 MODIFY

MODIFY TABLE ITAB. = MODIFY TABLE ITAB FROM ITAB. " 이 때 뒤의 ITAB은 ITAB의 헤더라인을 뜻한다.

 

헤더라인 있을 때

TYPES : BEGIN OF T_STR,
  COL1 TYPE I,
  COL2 TYPE I,
  END OF T_STR.


DATA : GT_ITAB TYPE TABLE OF T_STR WITH HEADER LINE.

DO 3 TIMES.
  GT_ITAB-COL1 = SY-INDEX.
  GT_ITAB-COL2 = SY-INDEX ** 2.

  APPEND GT_ITAB. " = APPEND GT_ITAB TO GT_TAB.
ENDDO.

LOOP AT GT_ITAB.
  WRITE : / GT_ITAB-COL1, GT_ITAB-COL2.
ENDLOOP.

 

 

헤더 라인 없을 때

TYPES : BEGIN OF T_STR,
  COL1 TYPE I,
  COL2 TYPE I,
  END OF T_STR.


DATA : GT_ITAB TYPE TABLE OF T_STR WITH HEADER LINE.

DO 3 TIMES.
  GT_ITAB-COL1 = SY-INDEX.
  GT_ITAB-COL2 = SY-INDEX ** 2.

  APPEND GT_ITAB. " APPEND GT_ITAB TO GT_TAB.
ENDDO.

LOOP AT GT_ITAB.
  WRITE : / GT_ITAB-COL1, GT_ITAB-COL2.
ENDLOOP.

 

*OCCURS를 이용한 선언

DATA : GT_ITAB TYPE T_STR OCCURS 0.

DATA : GT_ITAB2 TYPE TABLE OF T_STR.
 
- 위 둘 다 헤더라인이 없는 인터널 테이블 선언이다.
- 차이점은 TABLE OF 라는 명령어가 없이 'OCCURS n'을 넣어 메모리를 얼마나 할당하느냐는 구문으로 TABLE임을 암시하며 INTERNAL TABLE을 생성한다는 점이다.
- 헤더 라인 없이 프로그래밍을 하는 것이 권장되는 방식이다. 즉, TYPE TABLE OF T_TYPE 으로 인터널 테이블을 사용하는 것이 권장된다. (실무적으로는 OCCURS 0을 많이 쓰는 듯)
 
 
 

인터널 테이블 종류

 
- STANDARD TABLE, SORTED TABLE, HASHED TABLE 3가지로 나뉜다.

STANDARD TABLE

- 순차적인 INDEX를 가지는 테이블, TREE 구조

- INDEX를 이용하여 ENTRY를 찾을 때 바람직한 구조. READ, MODIFY, DELETE 구문을 사용할 때도 INDEX를 사용하므로 자주 사용할 듯(여기서 INDEX는 데이터베이스 오브젝트인 INDEX가 아니라 인터널 테이블에서 데이터가 위치하는 ROW의 번호를 뜻할 뿐이다.)

 

" 1. STRUCTURE TYPE 선언

TYPES : BEGIN OF T_LINE,
  FIELD1 TYPE C LENGTH 5,
  FIELD2 TYPE C LENGTH 4,
  FIELD3 TYPE I,

END OF T_LINE.

" 2. STANDARD TABLE 타입 선언
TYPES : T_TAB TYPE STANDARD TABLE OF T_LINE WITH NON-UNIQUE DEFAULT KEY.

" 3. INTERNAL TABLE 선언
DATA : GT_ITAB TYPE T_TAB WITH HEADER LINE.

GT_ITAB-FIELD1 = 'ENJOY'.
GT_ITAB-FIELD2 = 'ABAP'.
GT_ITAB-FIELD3 = 1.

APPEND GT_ITAB.

GT_ITAB-FIELD1 = 'ENJOY'.
GT_ITAB-FIELD2 = 'JAVA'.
GT_ITAB-FIELD3 = 2.

APPEND GT_ITAB.

READ TABLE GT_ITAB INDEX 1.

WRITE : / GT_ITAB-FIELD1, GT_ITAB-FIELD2, GT_ITAB-FIELD3.

READ TABLE GT_ITAB WITH TABLE KEY FIELD1 = 'ENJOY' FIELD2 = 'JAVA'.

WRITE : / GT_ITAB-FIELD1, GT_ITAB-FIELD2, GT_ITAB-FIELD3.

READ TABLE GT_ITAB WITH KEY FIELD3 = 1.

WRITE : / GT_ITAB-FIELD1, GT_ITAB-FIELD2, GT_ITAB-FIELD3.

 

- DEFAULT KEY. 구문은 CHAR 타입으로 선언된 모든 컬럼들을 키 컬럼으로 정의하는 것. 선언하지 않아도 선언된 것으로 간주한다.

- READ TABLE은 개별 LINE에 접근하는 문장

- INDEX, WITH TABLE KEY, WITH KEY 등 을 사용하여 인터널 테이블의 ROW를 찾아간다.

- DEFAULT KEY. 를 적었으므로 FIELD1, FIELD2가 모두 키 값이므로 WITH TABLE KEY로 데이터를 찾을 때는 두 변수 모두 적어주어야 한다.

- 키 값 이외의 값으로 찾을 때는 WITH KEY 를 사용한다.

 

 

SORTED TABLE

- STANDARD 테이블과 SOTRED TABLE은 INDEX 테이블이다. SORTED TABLE은 KEY값으로 항상 정렬된 인터널 테이블이다. STANDARD 와의 차이점은 UNIQUENESS인데, SORTED TABLE 사용 시 WITH UNIQUE를 사용할 수 있지만 STANDARD TABLE은 항상 NON-UNIQUE만 사용할 수 있다.

- SORTED TABLE은 내부적으로 BINARY SEARCH를 이용하기 때문에 TABLE ENTRY수와 탐색 속도는 정적 상관관계를 맺는다. UNIQUE 여부를 명시해야하며 이미 SORT되어 있기 때문에 SORT 명령어를 사용하면 오류가 발생한다.

 

 

" 1. STRUCTURE 선언
TYPES : BEGIN OF T_LINE,
  COL TYPE C,
  SEQ TYPE I,
  END OF T_LINE.
 
 
" 2. SORTED TABLE 타입 선언

TYPES : T_TAB TYPE SORTED TABLE OF T_LINE WITH UNIQUE KEY COL.

" 3. INTERNAL TABLE 선언

DATA : GT_ITAB TYPE T_TAB WITH HEADER LINE.

GT_ITAB-COL = 'B'.
GT_ITAB-SEQ = '1'.
INSERT TABLE GT_ITAB.

GT_ITAB-COL = 'A'.
GT_ITAB-SEQ = '2'.
INSERT TABLE GT_ITAB.

CLEAR GT_ITAB.

READ TABLE GT_ITAB INDEX 2.

WRITE : / GT_ITAB-COL, GT_ITAB-SEQ. " A -> B 로 정렬되어 있어 KEY가 B인 라인이 출력

 

- INSERT 대신 APPEND 구문을 사용하면 정렬에 문제가 생겼다는 DUMP ERROR가 발생한다.

 

 

HASHED TABLE

- 순차적인 INDEX를 가지고 있지 ㅇ낳음

- HASH 값으로 계산된 KEY로만 탐색할 수 있다.

- HASH 알고리즘을 사용하므로 ENTRY 수와 관계없이 응답속도는 항상 같다.

- 반드시 UNIQUE로 선언되어야 한다.

 

 

인터널 테이블 종류별 속도 비교

- 인터널 테이블의 종류가 3가지나 있는 것은 상황에 따라 바람직한 방식이 다르기 때문이다.

- 대부분 STANDARD 형태를 쓰지만 상황에 맞는 종류의 인터널 테이블을 사용해 데이터 탐색 시간을 줄여 성능을 높이는 것이 좋다.

 

인터널 테이블 비교 표

- 빠르게 교재를 훑고 있으므로 깊게 다루지 않고 정리한 표만 보고 넘어감

ACCESS TIME 비교(READ 비교)
INSERT 비교

- 기본적으로 INSERT가 많으면 STANDARD, READ가 많으면 SORTED가 바람직하다.

- 하지만 성능을 고려해야 한다면 귀찮더라도 UNIQUE한 KEY값을 인터널 테이블로 선언하여 프로그래밍하여 가장 read성능이 좋은 HASHED TABLE을 쓰는 것이 바람직하다. 

728x90

'OLD' 카테고리의 다른 글

SAP SD [기초] 7 - Enquiry(문의), Quotation(견적)  (1) 2024.04.17
EASY ABAP - 8-2. Internal Table(2)  (1) 2024.04.09
EASY ABAP - 7. Function  (0) 2024.04.08
EASY ABAP - 6. Subroutine  (0) 2024.04.08
EASY ABAP - 5. SQL  (3) 2024.04.05
728x90

Function과 Subroutine의 차이

- Subroutine이 Local Modularization이라고 한다면 Function은 Global Mudularization이라고 할 수 있다.

- 차이점 3가지

1. Function Module은 Function Group이라고 불리는 POOL에 속해있어야 한다.

2. Function Module은 예외 처리 기능을 제공하여 에러가 발생하면 예외 상황을 호출한 프로그램에 전달할 수 있다.

3. Function Module은 호출 프로그램에 관계없이 Stand-alone 모드에서 테스트할 수 있다.

 

Function Module

- Function Module은 중앙 라이브러리(R/3 Repository)에 저장되는 특별한 Global 서브루틴이다. R/3 시스템에는 기본적으로 많은 Function이 미리 만들어져 제공되고 있다.

- Function Module 인터페이스 요소

  • Import Parameters : Function Module에전달하는 값이며 선택 사항이다.
  • Export Parameters : Function Module로부터 ABAP 프로그램에 전달받는 값이며 선택 사항이다.
  • Changing Parameters : Function Module에 값을 넘기고, 그 값을 바꿀 수 있다.(객체 자체를 넘긴다)
  • Tables : 인터널 테이블을 Function Module에 전달하고 받을 수 있다.
  • Exceptions : 에러에 대한 정보를 제공한다.

 

Function Group

- 여러 Function Module을 모아 놓은 Container.

- Function 호출 시, 시스템은 호출한 프로그램의 Internal 세션 안으로 Function Group 전체를 Load한다. 즉, Function Group 내에서 데이터를 공유하고 스크린을 생성해서 호출하고 PERFORM 서브루틴 등을 공유할 수 있도록 한다. 주의해야 할 점은 Function 하나에 에러가 발생하면 동일 Group 내의 모든 Function이 실행되지 않는다는 것이다.

 

Function Group 생성

SE37
SE80에서 확인

- 생성하면 L[이름]TOP, L[이름]UXX 2개의 Include를 포함하는 Main프로그램이 자동으로 생성된다.

Function Group Main Program

Function Module

Attributes

Function Module의 상세 정보

- Processsing Type

1. Normal Function Module : 일반적으로 사용되는 Function Module 형태이다.

2. Remote-enbled module : 동일 시스템에서 비동기적으로 호출되거나 다른 시스템(레거시 등)으로부터 호출될 때 사용. 이 설정으로 된 함수는 RFC라고 불린다.

3. 일괄적으로 데이터베이스를 변경하기 위하여 추가적인 Function을 포함하는 형태이다. SAP Bundling 기술 중 하나.

 

- General Data

1. Edit Lock : 다른 개발자의 변경을 막기 위함

2. Global : 체크 시 Function Module 내의 변수를 Function Group의 Global 데이터로 선언하게 된다.

 

 

Import

Import 파라미터

1. Type : 파라미터 타입 유형 지정, TYPE, LIKE, TYPE REF TO

2. Associated Type : 데이터 타입이나 참조할 특정 테이블의 필드를 지정

3. Default Value : 파라미터의 초깃값을 설정

4. Optional : 파라미터를 선택 사항으로 설정

5. Passing Value : 파라미터를 전달받을 때 새로운 메모리에 값을 복사하여 작업한다.

 

 

Export

- 호출한 프로그램으로 값을 전달하는 목적

 

Changing 

- 호출한 ABAP 프로그램과 Parameter를 동시에 주고받을 수 있다.

 

Tables 

- 인터널 테이블의 내용을 주고 받을 수 있다.

 

※위 파라미터들은 서브루틴과 같은 논리를 가지므로 자세히 설명 X

 

Exceptions

- Function Module이 실행되는 동안 발생하는 예외사항을 처리할 수 있다.

 

Function 모듈 작성과 사용

Function Module을 호출한 프로그램 소스코드

- 패턴 버턴을 클릭하여 소스코드 양식을 생성할 수 있다.

 

호출된 Function Module

- RAISE DIV_ZERO 구문과 EXIT 명령어로 예외처리를 하고 있다.

 

RFC(Remote Function Call)

- SAP 밖의 시스템(레거시 시스템)에서 SAP으로, 혹은 그 반대로 데이터를 주고 받아야 한다면 SAP RFC를 이용해서 통신하는 것도 한 방법이다. 다양한 언어로 개발된 레거시 시스템과 SAP를 인터페이스하려면 RFC만으로는 구현하기가 힘들어 WEBMETHODS를 이용한다.

 

- Webmethods는 인터페이스 중계 서버 같은 걸 생각하면 된다.

 

- 내가 일하는 회사 시스템은 자바로 백엔드를 구현한 웹사이트가 SAP ECC 6.0 버전의 시스템을 마치 데이터베이스처럼 사용하고 있다. WAS에서 JCO를 이용해 RFC를 호출한다. 단점이라고 하면 비즈니스 로직을 자바 백엔드에서도 구현할 수 있고 SAP에서도 구현할 수 있기 때문에 명확한 가이드라인이 없으면 개발자 본인이 익숙한 곳에서 비즈니스 로직을 만들어 버리는 경우가 있어 유지보수가 어려워진다는 점이다. 사내 다른 서버(메일 서버, 파일 서버 등)과 통신할 때는 SAP에서 구현하는 것보다 자바에서 구현하는 편하기 때문에 JAVA에서 타 서비스를 호출하는 경우가 잦다.

 

 

- SAP을 공부하다보면 Web Service, SOA, Webmothods, WSDL, Solman, ADS 등 보편적으로 쓰이는 용어를 SAP에 특화된 서비스를 칭하는 데 쓰거나 아예 SAP에서만 쓰이는 용어를 쓰는 경우가 종종 보인다. 특히 외부와 통신하는 다양한 개념들에 대해서 그런 경우가 많은데 인터넷에 자세한 문서가 없어 용어에 대한 개념이 잡히지 않아 공부하기가 어렵다. 그리고 이 개념들이 HANA 버전에서도 유효한 것인지 직접 보지 않고는 알 수 없을 것 같다. - 잠깐 푸념..

 

 

 

 

728x90

'OLD' 카테고리의 다른 글

EASY ABAP - 8-2. Internal Table(2)  (1) 2024.04.09
EASY ABAP - 8-1. Internal Table(1)  (2) 2024.04.08
EASY ABAP - 6. Subroutine  (0) 2024.04.08
EASY ABAP - 5. SQL  (3) 2024.04.05
EASY ABAP - 4-2. Data Type(2)  (1) 2024.04.04
728x90

 Subroutine은 Java의 메서드를 생각하면 된다. 형식은 FORM으로 시작해서 ENDFORM으로 끝난다.


PERFORM WRITE_DATA.

FORM WRITE_DATA.

  WRITE : / 'SUBROUTINE'.

ENDFORM.

 

Subroutine 파라미터

 서브루틴을 호출할 때 사용하는 파라미터를 Actual Parameter라 하고 서브루틴에서 사용되는 파라미터를 Formal Parameter라고 한다.

 파라미터를 3가지 유형으로 주고받을 수 있다.

- Call by Value : Actual Parameter와 Formal Parameter가 물리적으로 다른 메모리 영역을 갖는다.

- Call by Reference : 물리적으로 같은 메모리 영역을 공유하여 넘겨준다. 즉, 주소값을 넘겨주는 것

- Call by Value and Result : 변수의 값을 넘겨주고 받는 구문에서 작업을 성공적으로 수행하였을 경우 변경된 값을 돌려준다. 물리적으로는 다른 영역을 사용한다.

 

Call by Value

- USING 키워드 다음에 파라미터를 사용하고 VALUE 구문으로 완성한다.

 

DATA : GV_VAL TYPE C LENGTH 30 VALUE 'CALL BY VALUE CHECK'.

PERFORM CALL_BYVALUE USING GV_VAL. " CALL BY VALUE로 수행

WRITE : / GV_VAL. " CALL BY VALUE CHECK

FORM CALL_BYVALUE USING VALUE(P_VAL).

  CONCATENATE P_VAL '!!!!!' INTO P_VAL.

  WRITE : / P_VAL. " CALL BY VALUE CHECK!!!!!
 
ENDFORM.


- PERFORM 이후에도 서로 다른 메모리를 공유하기 때문에 파리미터 GV_VAL의 값이 변하지 않은 것을 확인할 수 있다.

- 즉, FORM 구문에서 USING과 VALUE 키워드를 같이 사용하면 새로운 메모리 영역에 값을 복사하여 값을 전달하는 것이다. P_VAL은 FORM내 에서만 쓰이는 지역 변수이다.

 

Call by Reference

- Java 프로그래밍에서 메서드를 호출할 때 변수가 아니라 참조변수를 파라미터로 주는 것과 같다고 볼 수 있다. 참조변수는 객체의 주소값을 가진 변수이기 때문에 파라미터로 참조변수를 던져주면 서브루틴 내에서 객체의 주소값을 가지고 해당 객체를 메모리에서 가져오기 때문에 Actual Parameter와 Formal Parameter가 같아진다.

 

DATA : GV_VAL TYPE C LENGTH 30 VALUE 'CALL BY REFERENCE CHECK'.

WRITE : / GV_VAL. " CALL BY REFERENCE CHECK

PERFORM CALL_BYREF CHANGING GV_VAL. " CALL BY REFERENCE 수행

WRITE : / GV_VAL. " VALUE HAS BEEN CHANGED

FORM CALL_BYREF CHANGING P_VAL.

  P_VAL = 'VALUE HAS BEEN CHANGED'.
 
ENDFORM.

 

 

- VALUE 구문을 쓰지 않으면 USING과 CHANGING 모두 Call by Reference를 이용한다. 하지만 가독성 차원에서 CHANGING을 쓰는 것이 좋을 것이다.

 

 

Call by Value and Result 

- CHANGING 구문과 VALUE 구문이 함께 사용되면 Subroutine이 정상 종료 시 Actual Parameter값이 변경된다. 

DATA : GV_VAL1 TYPE I VALUE 2.
DATA : GV_VAL2 TYPE I VALUE 3.
DATA : GV_SUM  TYPE I.

PERFORM SUM_DATA USING GV_VAL1 GV_VAL2
                                       CHANGING GV_SUM.

WRITE : / 'RESULT : ', GV_SUM.


FORM SUM_DATA USING VALUE(P_VAL1)
                                           VALUE(P_VAL2)
                                CHANGING VALUE(P_SUM).
 
  P_SUM = P_VAL1 + P_VAL2.
 
ENDFORM.

 

 

파라미터

파라미터 타입 정의

- FORM 구문 내의 Formal Parameter는 타입을 명시적으로 지정하지 않으면 Generic Type으로 정의되고 Actual Parameter의 기술적 속성을 상속받게 된다.

- FORM의 Formal Parameter를 I로 지정하고 호출 시 파라미터로 TYPE C를 던져주면 구문 에러가 발생한다.

- Formal Parameter 타입 지정 방식 

1. FORM SUBR CHANGING P_VAL. - Generic Type 사용

2. FORM SUBR CHANGING P_VAL TYPE D. - Actual Parameter와 같은 타입 사용

3. FORM SUBR CHANGING P_VAL LIKE GV_VAL. - Actual Parameter와 같은 타입의 변수 사용

 

 

구조체 파라미터

DATA : BEGIN OF GS_STR,
    COL1 VALUE 'A',
    COL2,
  END OF GS_STR.

GS_STR-COL2 = 'B'.

PERFORM WRITE_DATA USING GS_STR.


FORM WRITE_DATA USING PS_STR STRUCTURE GS_STR.
  WRITE : PS_STR-COL1, PS_STR-COL2.
 
ENDFORM.

 

- 구조체를 파라미터로 전달할 때 타입을 명시적으로 지정하지 않으면 컬럼이 없다는 에러가 발생한다.

- 즉, 구조체를 파라미터로 쓸 때에는 타입을 명시적으로 지정하거나 그렇지 않으면 필드 심볼을 이용해야 한다.

 
DATA : BEGIN OF GS_STR,
    COL1 VALUE 'A',
    COL2,
  END OF GS_STR.

GS_STR-COL2 = 'B'.

FIELD-SYMBOLS <FS>.

PERFORM WRITE_DATA USING GS_STR.


FORM WRITE_DATA USING PS_STR.
 
  ASSIGN COMPONENT 1 OF STRUCTURE PS_STR TO <FS>.
 
  WRITE <FS>.
 
  ASSIGN COMPONENT 2 OF STRUCTURE PS_STR TO <FS>.
 
  WRITE <FS>.
 
ENDFORM.

 

- 위와 같은 결과를 반환한다.

 

인터널 테이블 파라미터

TYPES : BEGIN OF T_STR,
  COL1 TYPE C,
  COL2 TYPE I,
  END OF T_STR.

TYPES : T_ITAB TYPE TABLE OF T_STR.

DATA : GS_STR TYPE T_STR.
DATA : GT_ITAB TYPE T_ITAB.

GS_STR-COL1 = 'A'.
GS_STR-COL2 = 1.
APPEND GS_STR TO GT_ITAB.

GS_STR-COL1 = 'B'.
GS_STR-COL2 = 2.
APPEND GS_STR TO GT_ITAB.

PERFORM TEST_ITAB USING GT_ITAB.

FORM TEST_ITAB USING PT_ITAB TYPE T_ITAB.

  READ TABLE PT_ITAB WITH KEY COL1 = 'A' INTO GS_STR.

  IF SY-SUBRC EQ 0.
    WRITE : GS_STR-COL1, GS_STR-COL2.
  ENDIF.

ENDFORM.
 
 
TABLES 구문
- USING과 CHANGING구문 대신 사용
- 예전 아밥에서 많이 사용되어 현재도 많이 사용되고 또 소스코드에 많이 남아있는 시스템이 많다.
- Call by Value로는 사용할 수 없다.
TYPES : BEGIN OF T_STR,
  COL1 TYPE C,
  COL2 TYPE I,
  END OF T_STR.

TYPES : T_ITAB TYPE TABLE OF T_STR.

DATA : GT_ITAB TYPE T_ITAB.


PERFORM TEST_ITAB TABLES GT_ITAB.

PERFORM WRITE_DATA TABLES GT_ITAB.

FORM TEST_ITAB TABLES PT_ITAB TYPE T_ITAB.

  DATA : LS_STR TYPE T_STR.

  LS_STR-COL1 = 'A'.
  LS_STR-COL2 = 1.
  APPEND LS_STR TO PT_ITAB.

  LS_STR-COL1 = 'B'.
  LS_STR-COL2 = 2.
  APPEND LS_STR TO PT_ITAB.

ENDFORM.

FORM WRITE_DATA TABLES PT_ITAB LIKE GT_ITAB.

  DATA : LS_STR TYPE T_STR.

  LOOP AT PT_ITAB INTO LS_STR.
    WRITE : / LS_STR-COL1, LS_STR-COL2.
  ENDLOOP.

ENDFORM.

 

 

Subroutine 호출

- 서브루틴 호출은 Internal, External 두 가지 방식이 있음

- Internal : 프로그램 내에서 서브루틴 호출

- External : 다른 아밥 프로그램 내의 서브루틴 호출

 

 

REPORT ZABAP_HYOWON_TEST1.

DATA : GV_CHAR(20) VALUE 'EXTERNAL CALL!'.

PERFORM TEST_EXRN_SUBR(ZABAP_HYOWON_4) USING GV_CHAR.

" ================================================

REPORT ZABAP_HYOWON_4.

FORM TEST_EXRN_SUBR USING P_CHAR.

  WRITE : / P_CHAR.

ENDFORM.

 

*ABAP 반복문 3가지 형태

- DO ~ ENDDO : 순환 횟수 지정, 횟수는 SY-INDEX에 저장

DO 3 TIMES.
  PERFORM TEST_EXRN_SUBR(ZABAP_HYOWON_4) USING GV_CHAR.
ENDDO.
 

- WHILE - ENDWHILE : 표현식이 참이면 반복순환, 횟수는 SY-INDEX에 저장

WHILE GV_INDEX < 10.

  PERFORM TEST_EXRN_SUBR(ZABAP_HYOWON_4) USING GV_CHAR.
 
  WRITE : GV_INDEX.
 
  GV_INDEX = GV_INDEX + 1.

ENDWHILE.

 

- LOOP ~ ENDLOOP : 인터널 테이블의 라인을 차례대로 WORK AREA나 HEADER LINE으로 이동하는 순환 구문 

- 현재 순환 횟수는 SY-TABIX에 저장, 인터널 테이블의 라인 번호

LOOP AT PT_ITAB INTO LS_STR.

  WRITE : / LS_STR-COL1, LS_STR-COL2.
 
ENDLOOP.

 

 

 

Subroutine 종료

- EXIT 구문을 만나면 서브루틴 빠져 나오고,

- CHECK 구문을 만나면 값을 비교하여 참이면 이후 구문 수행

- 개인적인 생각으로 IF문을 통해 EXIT 구문을 수행하느냐 마느냐를 정하는 게 소스코드는 길어도 소스코드에 일관성이 있지 않을까함. 다른 언어들도 Return문을 수행하느냐 마느냐로 분기 처리를 하는 경우가 많기 때문에..

 

PARAMETERS : P_VAL TYPE CHAR10.

PERFORM END_SUBR USING P_VAL.

FORM END_SUBR USING VALUE(P_VAL).

  CASE P_VAL.
    WHEN 'EXIT'.
      WRITE 'SUBROUTINE EXIT'.
      EXIT.

    WHEN 'CHECK'.
      WRITE : 'VALUE CHECK : ', P_VAL.
      CHECK P_VAL NE 'CHECK'.

    WHEN OTHERS.

  ENDCASE.

  WRITE 'SUBROUTINE HAS NOMALLY DONE'.

ENDFORM.
 
 
 
 

PERFORM ON COMMIT

- ON COMMIT : COMMIT WORK를 만나면 서브루틴 구문 호출

- ON ROLLBACK : ROLLACK WORK를 만나면 서브루틴 구문 호출

 

DATA : GS_SCARR LIKE SCARR,
      GV_FLG TYPE C.

SELECT SINGLE * FROM SCARR INTO GS_SCARR
  WHERE CARRID = 'AA'.

PERFORM DELETE_DATA USING GS_SCARR.

PERFORM INSERT_DATA ON COMMIT.

IF GV_FLG = 'X'.
  COMMIT WORK.
ENDIF.

FORM DELETE_DATA USING VALUE(PS_SCARR) TYPE SCARR.

  DELETE SCARR FROM PS_SCARR.
  IF SY-SUBRC EQ 0.
    GV_FLG = 'X'.
  ENDIF.

ENDFORM.

FORM INSERT_DATA.
  GS_SCARR-ZCOL1 = 'Z'.
  INSERT SCARR FROM GS_SCARR.
ENDFORM.

 

- COMMIT WORK가 수행될 때 PERFORM INSERT_DATA 구문이 수행되도록 함.

 

 

MACRO

- 같은 구문을 여러 번 사용해야 할 때 미리 구문 덩어리를 매크로라는 하나의 덩어리로 정의하고 계속해서 가져다 쓰는 것이다.

- PERFORM을 사용하는 것과 다르지 않지만 더 소스코드가 짧다는 장점이 있다. "MACRO_NAME 변수." 가 끝이기 때문에 단순하지만 이 구문이 어떤 명령어인지 어디서 왔는지를 알려주는 PERFORM과 같은 명령어가 없어 가독성이 떨어진다. 아마 예전에 SUBROUTINE이나 FUNCTION MODULE 개념이 없었을 때 만들어진 기능이 아닌가 싶다. 안 쓰는 게 좋을 듯.

 

 

 

728x90

'OLD' 카테고리의 다른 글

EASY ABAP - 8-1. Internal Table(1)  (2) 2024.04.08
EASY ABAP - 7. Function  (0) 2024.04.08
EASY ABAP - 5. SQL  (3) 2024.04.05
EASY ABAP - 4-2. Data Type(2)  (1) 2024.04.04
EASY ABAP - 4-1. Data Type(1)  (1) 2024.04.04
728x90

SAP ABAP 에서의 SQL은 OPEN SQL과 NATIVE SQL 2가지로 나뉜다.

- OPEN SQL은 ABAP에서 사용되는 개념이며 Database Interface를 통해 결국은 Native SQL로 번역된다.

- Native SQL은 일반적으로 데이터베이스에 사용되는 SQL을 의미한다.

2가지 SQL의 처리 흐름 비교

- 보통 OPEN SQL로 해결 되지 않는 복잡한 SQL은 NATIVE SQL을 이용한다. 각 DB벤더에서 제공하는 기본적인 함수 등을 사용할 수 있다. ( +기호로 조인 및 LPAD 등 )

- Local Buffer : 데이터베이스의 부하를 줄이는 기술. DB에도 buffer가 있지만 Database와 통신하기 전에도 해당 작업을 하는 단계가 또 있는듯함.

- OPEN SQL은 클라이언트 종속적이라 MANDT 필드를 조건에 넣지 않아도 되지만 Native SQL을 쓸 떄에는 MANDT 필드를 넣어주어야 한다.

- SQL 수행결과가 성공이면 SY-SUBRC = 0, SY-DBCNT는 ROW의 수를 반환한다.

- OPEN SQL의 MODIFY는 INSERT와 UPDATE의 기능을 수행한다. 키값을 기준으로 UPDATE할 지 INSERT할 지 정한다.

 

OPEN SQL : 데이터 읽기

SELECT

INTO

FROM

WHERE

GORUP BY

HAVING

ORDER BY

- 위와 같은 키워드가 있고 대부분 일반적인 SQL과 비슷함 

 

SELECT 

- SELECT SINGLE 옵션은 조회된 ROW중 임의의 ROW를 하나 반환한다. 따라서 WHERE 조건을 제대로 입력하여 하나의 ROW만 반환하도록 SQL을 잘 짜야 한다.

 

INTO 

- 조회한 결과를 변수에 저장하는 기능을 수행한다.

- CORRESPODING FIELDS OF 구문을 사용하면 동일 필드명에 값을 할당한다.

- SAP의 테이블들은 컬럼이 상당히 많기 때문에 SELECT 문에서 *을 삼가는 것이 좋다. fetch size를 줄여야 성능이 향상되기 때문이다.

- 구조체(Work Area) : 한 라인만 조회하고자 할 경우 Work Area(변수, 구조체)에 값을 할당한다.

- Internal Table : 여러 라인을 조회하고자 하는 경우 사용한다. APPENDING은 인터널 테이블에 추가로 INSERT하고 INTO는 인터널 테이블의 데이터를 삭제한 다음 INSERT한다.

 

FROM

- CLIENT SPECIFIED : 자동 클라이언트 설정 해제

- BYPASSING BUFFER : SAP Local Buffer에서 값을 읽지 않는다. 테이블이 Buffering이 설정되어 있어도 무시하고 데이터베이스에서 직접 데이터를 읽어 온다. 사용하는 이유는 가장 최신 정보를 가져오기 위해서 또는 자주 사용하지 않는 데이터를 local buffer에 남겨두지 않게 하여 메모리 사용을 줄여 효율성을 높이기 위함.

- UP TO n ROWS : SELECT 개수 조절. 

 

 

JOIN

- ANSI 표준은 INNER, OUTER JOIN ~ ON을 사용한다.

- ABAP에서는 조인보다는 조회 후 인터널 테이블에 각각 저장하여 기준이 되는 테이블을 LOOP를 돌려 인터널 테이블의 내용을 MODIFY하는 경우가 많다. 이런 방식의 코딩이 많아 하나의 기능을 하는 프로그램에 짧은 여러 개의 SQL이 수행된다. 일반적인 웹개발에서 긴 SQL문을 사용하는 것과 대조적이다.

 

WHERE 

operator Meaning
=, EQ True if the content of operand1 is equal to the content of operand2.
<>, NE True if the content of operand1 is not equal to the content of operand2.
<, LT True if the content of operand1 is less than the content of operand2.
>, GT True if the content of operand1 is greater than the content of operand2.
<=, LE True if the content of operand1 is less than or equal to the content of operand2.
>=, GE True if the content of operand1 is greater than or equal to the content of operand2.

 

FOR ALL ENTRIES : 인터널 테이블과 데이터베이스의 테이블을 JOIN하는 개념과 유사하다. LOOP를 수행하면서 SQL을 수행하기 때문에 데이터베이스에 반복적으로 접근하여 비효율적이지만 편리하다는 장점이 있다.

 

SELECT *
  FROM SFLIGHT
  INTO TABLE GT_SFLIGHT
   FOR ALL ENTRIES IN GT_SPFLI
 WHERE CARRID = GT_SPFLI-CARRID.

 

- WHERE 절의 조건이 한 줄이지만 native sql로 파싱되면 OR 구문을 써서 조회하는 것과 같은 방식이 된다.

 

GROUP BY 

- AVG, COUNT, MAX, MIN, STDDEV(표준편차), SUM, VARIANCE(분산) 등의 함수를 SELECT 문에 사용할 수 있게 된다.

- 위 함수 사용시 ALIAS를 지정해야 CORRESPONDING 명령어가 의도한 대로 수행된다.

 

 

ORDER BY

- 일반적인 SQL과 같음

Scalar Subquery도 일반적인 SQL문과 같은 방식으로 사용할 수 있다.

또한, EXISTS 또한 사용가능하다.

 

 

OPEN SQL : 데이터 변경(삽입,수정,삭제)

INSERT 

DATA : GS_SCARR TYPE SCARR.

GS_SCARR-CARRID = 'Z1'.
GS_SCARR-CARRNAME = 'AIR KOREA'.
GS_SCARR-URL = 'http://www.airkorea.com'.

INSERT INTO SCARR VALUES GS_SCARR.

 

- 이 때 같은 KEY값을 가져 데이터 무결성이 깨지면 DUMP ERROR를 발생시키는데 이를 방지하기 위해 ACCEPTING DUPLICATE KEYS 구문을 넣는다. INSERT 구문 실패 시 SY-SUBRC 값 4를 반환하게 한다.

 

DATA : GS_SCARR TYPE SCARR.
DATA : GT_SCARR TYPE TABLE OF SCARR.


GS_SCARR-CARRID = 'Z1'.
GS_SCARR-CARRNAME = 'AIR KOREA'.
GS_SCARR-URL = 'http://www.airkorea.com'.

APPEND GS_SCARR TO GT_SCARR.

INSERT SCARR FROM TABLE GT_SCARR ACCEPTING DUPLICATE KEYS.

WRITE : / SY-SUBRC. " 4
 
 
UPDATE
 
DATA : GS_SCARR TYPE SCARR.

GS_SCARR-CARRID = 'Z1'.
GS_SCARR-CARRNAME = 'AIR KOREA'.
GS_SCARR-URL = 'http://www.airkorea.com123'.

UPDATE SCARR FROM GS_SCARR.

WRITE : / SY-SUBRC. " 0 계속 해도 0임 즉 기존과 같은 값이어도 0
 

 

- 일반적인 SQL의 UPDATE TB_NAME SET COL1 = 'VALUE1' WHERE A=B 구조가 성능이 우수하다.

- 구조체나 테이블에 바꾸고 싶은 컬럼 이외의 값이 정의되어 있고 비어있다면 빈 값으로 UPDATE한다.

- EX) SCARR에 CURRCODE 컬럼에 이미 특정 값이 있는 데 위와 같이 UPDATE하면 CURRCODE 값은 빈 값으로 바뀌는 것이다

- 즉, 구조체나 테이블의 모든 컬럼을 비교 후에 UPDATE하기 떄문에 컬럼의 값을 직접 지정해서 바꾸는 방식이 더 성능이 좋은 것.

 

DELETE

 

DATA : GS_SCARR TYPE SCARR.
DATA : GT_SCARR TYPE TABLE OF SCARR.

GS_SCARR-CARRID = 'Z1'.
GS_SCARR-CARRNAME = 'AIR KOREA'.
GS_SCARR-URL = 'http://www.airkorea.com123'.


DELETE SCARR FROM GS_SCARR.

WRITE : / SY-SUBRC. " 삭제할 것이 없으면 4 반환

 

MODIFY = INSERT + UPDATE

- MODIFY는 INSERT와 UPDATE를 합친 기능 수행. ABAP 소스코드를 보다보면 INSERT나 UPDATE보다 MODIFY를 주로 사용하는 것을 볼 수 있다.

- 키값을 가지는 데이터가 테이블에 존재하면 UPDATE, 아니면 INSERT.

DATA : GS_SCARR TYPE SCARR.
DATA : GT_SCARR TYPE TABLE OF SCARR.

GS_SCARR-CARRID = 'Z2'.
GS_SCARR-CARRNAME = 'AIR KOREA'.
GS_SCARR-URL = 'http://www.airkorea.com'.
GS_SCARR-ZCOL2 = SY-DATUM.


APPEND GS_SCARR TO GT_SCARR.

CLEAR GS_SCARR.

GS_SCARR-CARRID = 'Z2'.
GS_SCARR-CARRNAME = 'AIR KOREA'.
GS_SCARR-URL = 'http://www.airkorea.com123'.


APPEND GS_SCARR TO GT_SCARR.

MODIFY SCARR FROM TABLE GT_SCARR.

WRITE : / SY-SUBRC.
ZCOL2의 값이 없는 것을 확인

- ZCOL2의 값이 없으므로 MODIFY가 INSERT TB_NAME FROM IT_TAB 후에 UPDATE TB_NAME FROM IT_TAB를 각각 실행하는 것을 알 수 있다.

- MODIFY를 의도에 맞게 실행하려면 모든 컬럼 데이터를 인터널 테이블이 제대로 지니고 있어야 한다는 단점이 있다. 어쩌면 ABAP 프로그램의 속도가 느리게 느껴지게 하는 주범일 수도 있을 듯

 

 

TIPS

- ORDER BY 대신 SORT를 사용하여 정렬은 AP에서 수행하는 게 낫다 - AP서버를 여러 대 두어서 그런 듯

- WHERE 조건절에 사용될 인덱스의 값이 없더라도 RANGE 변수를 활용하여 빈 값을 넣어서 NATIVE SQL로 번역될 때 조건에서 제외되지 않도록 한다.

 

 

NATIVE SQL

- 굳이 NATIVE SQL을 사용하는 경우는 크게 2가지가 있다. 데이터베이스의 기능을 활용한 정밀한 SQL 수행과 DB LINK를 통해 다른 데이터베이스에 접근하여 데이터를 가져오는 경우에 사용한다.

 

DB LINK

- DB링크의 설정은 DBCO에서 살펴볼 수 있다.

DBCO

 

DBCO에 등록 후 소스코드에서 직접 연결이름(Connection Name)을 이용해 데이터베이스에 접근하면 된다.

DB LINK를 수행하는 function module 소스 코드

- 중계 인터페이스 테이블에서 데이터를 가져오거나 반대로 중계 인터페이스 테이블에 데이터를 삽입하는 기능을 구현하기 위해 사용한다. 다만 요즘의 시스템 간 데이터 통신 방식과는 맞지 않아 계속해서 안 쓰는 추세로 바뀔 듯..

 

- Native SQL의 특이점은 MANDT(클라이언트)를 WHERE절에 써주어야 한다는 점이다. 클라이언트는 PK므로 인덱스를 사용하기 위해서는 MANDT를 설정해주어야 한다.

 

TRY.
  EXEC SQL PERFORMING F_APPEND_TABLE_HEAD.
    SELECT * INTO :IT_HEAD_INF
      FROM IF_SALE_HEADER
    WHERE SAP_RFLC_DT IS NULL
  ENDEXEC.
CATCH CX_SY_NATIVE_SQL_ERROR INTO LV_EXCEPTION.
  V_SUBRC   = 1.
  V_ERR_MSG = LV_EXCEPTION->GET_TEXT( ).
ENDTRY.
 
- DUMP ERROR가 뜨는 것을 방지하기 위해 TRY-CATCH 구문을 주로 사용하며 EXEC~ENDEXEC는 필수이다.

 

728x90

'OLD' 카테고리의 다른 글

EASY ABAP - 7. Function  (0) 2024.04.08
EASY ABAP - 6. Subroutine  (0) 2024.04.08
EASY ABAP - 4-2. Data Type(2)  (1) 2024.04.04
EASY ABAP - 4-1. Data Type(1)  (1) 2024.04.04
EASY ABAP - 3. TMS와 CTS  (2) 2024.04.04
728x90

프로그램의 Local Type

- 프로그램 내에서 Local Type을 선언하여 프로그램에서만 사용하는 경우

- 프로그램 내의 '글로벌 변수'와 같은 사용 범위를 갖지만 프로그램 내에 한정된다는 의미에서 Local Type이라고 부른다.

 

1. TYPE 선언

TYPES : T_CAHR10(10) TYPE C.

DATA : GV_VAL1 TYPE T_CHAR10.

DATA : GV_VAL2 LIKE GV_VAL1.

 
2. Structured Type 선언
- 여러 개 필드로 이루어진 구조체 타입 선언 가능. (구조체는 변수들 모음 같은 거임. C에는 있지만 자바에는 없음. 자바의 클래스에서 메서드가 없이 인스턴스 변수만 있는 거라고 생각하면 됨)
 
" 구조체 타입 선언
 
TYPES : BEGIN OF T_REN,
    NAME TYPE C LENGTH 20,
    COUNTRY TYPE C LENGTH 15,
    CITY TYPE C LENGTH 10,
END OF T_REN.
 
" 구조체 변수 선언
 
DATA GS_PEOPLE TYPE T_REN.

GS_PEOPLE-NAME = 'TOTODA'.
GS_PEOPLE-COUNTRY = 'JAPAN'.
GS_PEOPLE-CITY = 'TOKYO'.

 


" INCLUDE TYPE 구문 사용
 
TYPES : BEGIN OF T_INFO.
    INCLUDE TYPE T_REN AS REN.
    TYPES : PHONE TYPE C LENGTH 10,
END OF T_INFO.

DATA GS_PEOPLE2 TYPE T_REN.

GS_PEOPLE2-REN-NAME = 'TOTODA'.
GS_PEOPLE2-REN-COUNTRY = 'JAPAN'.
GS_PEOPLE2-REN-CITY = 'TOKYO'.
GS_PEOPLE2-PHONE = '01012345678'.
 
 

" 구조체 타입 선언 없이 구조체 변수 선언
 
DATA : BEGIN OF GS_PEOPLE3,
    NAME TYPE C LENGTH 20,
    COUNTRY TYPE C LENGTH 15,
    CITY TYPE C LENGTH 10,
END OF GS_PEOPLE3.
 
 
 
" INLCUDE STRUCTRUE 사용해서 구조체나 ABAP Dictionary의 구조체 가져오기
 
DATA : BEGIN OF GS_PEOPLE4.
    INCLUDE STRUCTRUE T_REN AS REN. " AS REN 생략 가능 
    DATA : PHONE TYPE C LENGTH 10.
END OF GS_PEOPLE4.
 
 

ABAP Dictionary의 Type

- ABAP Dictionary는 Global Type이다. 모든 프로그램에서 Data Type으로 선언하여 사용할 수 있다.

- Table&View, Data Types, Type Groups 크게 3가지로 나뉜다.

 

1. Table and View

 

DATA : GS_SFLIGHT TYPE SFLIGHT.
 
 
2. Data Element
- Data Element는 개별 필드를 정의. Data Element 타입은 Domain을 참고한다. ( 둘의 차이는 ABAP Dictionary 파트에서 공부할 것)
- 같은 Domain을 여러 Data element가 참고할 수도 있고, 같은 Data Element를 여러 테이블이 사용할 수도 있다.
SE11에서 MATNR 조회
DATA : GV_MATNR TYPE MATNR.
 
 
3. Structure를 이용한 선언
DATA : GS_MCVBAP TYPE MCVBAP. " 영업 문서 : 품목데이타 구조
 
 

4. Type Group을 이용한 Type 선언
- Type Group은 ICLUDE Technique을 기반으로하며, 여러 가지 TYPE을 그룹으로 묶어서 선언할 때 사용된다.

- 개발 시에 자주 사용하는 ALV 프로그램은 SLIS라는 TYPE-POOL을 사용한다.

주문된 자재 유형이라는 타입 그룹
DATA : GV_TGRP1 TYPE WPOPT_S_DET_PO_ART_IN_HEAD.

GV_TGRP1-WERKS = '1110'.

WRITE : /  GV_TGRP1-WERKS.

WRITE : /  wpopt_c_method_old. " 상수를 그대로 사용

 

 

ABAP Dictionary Type 과 ABAP Type

- ABAP의 기본 데이터 타입을 사용하여 자주 사용되는 타입의 모습은 ABAP Dictionary Type으로 미리 생성되어 있다.

- CHAR10 과 같은 타입이다. 자주 사용되는 ABAP Dictionary Type을 알아두면 개발 시 편리하다.

 

CURR 예시

 

 

 

Assinging Value

- 값 할당은 MOVE 또는 WRITE TO 구문을 이용하여 수행한다. 

- MOVE는 자동 형변환이 일어나므로 편리하지만 조심해야 함.

- MOVE-CORESPONDING 구문으로 필드 구조가 다른 경우 매핑을 자동으로 수행하게 할 수 있다.

DATA : BEGIN OF GS_REN,
  NAME(20) VALUE 'TOTODA',
  COUNTRY(10) VALUE 'KOREA',
  CITY(20) VALUE 'SEOUL',
END OF GS_REN.


DATA : BEGIN OF GS_REN2,
  NAME(20) ,
  PHONE(10) VALUE '1234567891',
  CITY(20) ,
  COUNTRY(10) ,
END OF GS_REN2.

MOVE-CORRESPONDING GS_REN TO GS_REN2.

WRITE : / GS_REN2. " TOTODA    1234567891SEOUL               KOREA

 

- offset을 이용한 값 할당 : 인덱스 N 부터 시작해서 (M) 만큼 할당한다.

DATA : GV_F1(8) VALUE 'ABCDEFGH'.

DATA : GV_F2(8).

MOVE GV_F1+2(3) TO GV_F2.

WRITE : / GV_F2. " CDE

 

- 구조체 산술 계산

 

DATA : BEGIN OF GS_RATE, " 가중치
  KIM TYPE F VALUE '0.8',
  LEE TYPE F VALUE '1.0',
END OF GS_RATE.

DATA : BEGIN OF GS_RESULT, " 계산 대상
  KIM TYPE I VALUE 95,
  LEE TYPE I VALUE 70,
END OF GS_RESULT.

MULTIPLY-CORRESPONDING GS_RESULT BY GS_RATE.

WRITE : / GS_RESULT-KIM. " 76
WRITE : / GS_RESULT-LEE. " 70

 

 

ADD-CORRESPONDING

SUBTRACT- CORRESPONDING

MULTIPLY- CORRESPONDING

DIVIDE- CORRESPONDING 

- 4가지가 있다.

 

 

 

Constants

- 프로그램에서 자주 사용하는 값을 상수로 선언하여 사용 -> 변경 불가

- 구조체도 상수로 선언 가능하다.

- 초기값을 설정해주어야 한다.

CONSTANTS : GC_VAR1 TYPE INT4 VALUE 1.

 

Statics : 서브루틴(FORM, FUNCTION)에 사용되는 Local 변수의 값을 지속적으로 저장하여야 하는 경우 사용. 즉, 프로그램이 종료될 때 까지 값을 유지한다.

 

DO 3 TIMES.
    PERFORM CALL_SUBR.
  ENDDO.
 
FORM CALL_SUBR.
    STATICS LV_VAL TYPE I.
    LV_VAL = LV_VAL + 1.
   
    WRITE : / LV_VAL. " 1 2 3
ENDFORM.

 

- FORM 내부의 LOCAL 변수 LV_VAL의 값이 계속 유지되는 것을 알 수 있다.\

 

 

TABLES 선언

- TABLES 구문을 사용하여 TABLE, VIEW, STRUCTURE를 선언하게 되면 대상과 같은 Data Object를 생성한다. Table Work Area라고 부르며 프로그램 내에서 구조체 처럼 사용할 수 있다.

 

SELECT *
  FROM MARA  " 구조체와 테이블 이름이 같아서 INTO MARA 생략
 WHERE MTART = 'ZHAL'.
 
 WRITE : / MARA-MATNR.
 
ENDSELECT.

 

728x90

'OLD' 카테고리의 다른 글

EASY ABAP - 6. Subroutine  (0) 2024.04.08
EASY ABAP - 5. SQL  (3) 2024.04.05
EASY ABAP - 4-1. Data Type(1)  (1) 2024.04.04
EASY ABAP - 3. TMS와 CTS  (2) 2024.04.04
EASY ABAP - 2. Package에 대해서  (1) 2024.04.01

+ Recent posts