티스토리 뷰

04) Internal Table


본 게시물은 SAP 혁신성장 청년인재 양성과정에서 교육받는 교육생이 정리 목적으로 포스팅하는 것으로,

전문성 및 실용성이 다소 떨어지거나 정보가 부정확할 수 있으니 참고 목적으로만 읽어주시기 바랍니다.

 

 

Internal Table

 

 앞서 Data를 담을 수 있는 공간인 Data Object에 대해 이미 살펴본 바 있는데, 이번 포스팅에서는 Data Object 중 ABAP에서 가장 많이 쓰이고 핵심적이라 할 수 있는 Internal Table에 대해 살펴보려 한다.

 

 

먼저 데이터베이스 관점으로 '테이블'의 특징에 대해 차근차근 알아보자.

 

대부분의 데이터(정보)는 2차원 형태의 테이블 구조에서 아주 쉽게 정의될 수 있다. 복잡한 내용도 가로 세로 일목요연하게 표(테이블)로 정리하면 데이터를 한눈에 보기 쉬워지고 내가 원하는 데이터도 쉽게 찾을 수도 있다. 이런 특징 때문에 대부분의 데이터는 열(칼럼)과 행(로우)으로 구성되는 2차원적인 테이블 구조를 가진다. 때문에, 우리는 저장되어있는 데이터를 다루기 위해 '테이블'을 도구로 사용할 수밖에 없는 것이다. 

 

아무개 대학교 어떤 수업의 '학생' 이라는 정보는 다음과 같이 2차원 구조인 표로 나타내면 정리가 아주 쉬워진다.

  

 테이블은 위와 같이 테이블을 구성하는 데이터의 속성(Attribute)을 열(컬럼)에 정의한다. 행(로우)은 학생 한 명 한 명으로 지정된 컬럼 데이터로 이루어진 하나의 묶음(Structure)라고 볼 수 있다.  이처럼 2차원 구조에 데이터를 성격에 맞게 분리해 가급적 중복되지 않게 저장하고 필요한 데이터는 테이블 간에 관계를 맺어 추출하는 것을 '관계형 데이터베이스'라고 한다. 관계형 데이터베이스는 요즘과 같이 변화무쌍한 시대에서 다양한 정보를 하나의 컬럼에 정의해 줄 수 없다는 단점이 있지만, 데이터를 정리하고 추출하기 쉽다는 장점이 워낙 강력해서 여전히 다양한 DB 프로그램에서 광범위하게 쓰인다.

 

 위 테이블을 다시 살펴보자, '고길동' 씨를 찾고 싶을 땐 어떻게 하면 될까? '학생' 테이블의 2번째 인덱스를 참조하면 된다. 하지만 '고길동'이라는 이름을 가진 사람이 2명 이상이면 어떻게 해야 하는가? 이 같은 문제를 해결하고자 관계형 데이터베이스 관리 시스템(RDBMS)에서는 데이터 테이블에 반드시 하나의 키가 되는 컬럼을 두도록 권고하고 있다. 수많은 학생들은 서로 다른 고유의 번호 '학번'을 가지고 있다. unique 한 특징을 가진 '학번'이 바로 '키'가 되어 데이터의 정확성을 보장해주고 올바른 데이터를 유지하게 해주는 것이다. 

 

 데이터베이스에는 다양한 키가 있지만, 개념적인 부분은 이것으로 충분할 것 같아 데이터베이스에 관한 설명은 여기까지만 다루겠다. 당장 '관계형 데이터베이스'로 구글링만 해도 정리가 아주 잘 된 정보가 넘쳐나니 공부가 더 필요하다면 여기보다는 그 글을 참고하는 게 날 것이다.

 

 

 ABAP에서는 무수히 많은 데이터를 다룬다. 고객사의 자재와 운송장, 제품에 대한 재고와 항공편의 내역까지 다양한 데이터가 데이터베이스에 저장되어 있다. 데이터베이스에 저장된 데이터를 끌어다 쓰거나, 데이터를 삽입/수정/삭제할 때 데이터베이스에 '요청'한다. 이를 '쿼리(Query) 문'이라고 한다. SQL(Structured Query Language)라고 불리는 쿼리문은 데이터베이스에 접근할 때 사용되는 언어이다. 테이블에 데이터를 저장하고 저장된 데이터를 테이블에 불러오고, 결국 데이터베이스는 쿼리가 전부이고 테이블은 이를 위한 '도구' 중 하나일 뿐이다.

 

 이번 포스팅에서는 SQL을 위한 도구인 '테이블'에만 집중하려 하기 때문에 SQL은 다음에 다루도록 하겠다.

 


 

 테이블은 결국 데이터를 저장하고 읽기 위한 공간이다. 데이터를 저장하고 읽기 위한 방법은 다양한데, 이런 기능적인 측면에서 테이블의 종류를 살펴보자. 

 

출처 : https://sapforbeginner.wordpress.com/tag/hashed-table/

 

 테이블은 결국 데이터를 읽고 쓰는데 목적이 있으므로 효율적으로 데이터를 다루는 것이 무엇보다 중요하다.

이러한 기능적인 측면에서 테이블은 위와 같이 구분할 수 있는데, 각 테이블마다 데이터에 접근하는 방법 및 속도가 다르다. 먼저 가장 많이 쓰이는 Standard Table에 대해 살펴보자.

 

 

 

Standard Table

가장 많이 쓰이는 일반적인 테이블이다. 

 

다음과 같이 선언해 줄 수 있다.

TYPES: BEGIN OF TY_STUDENTS,
ID(8)          TYPE N,
NAME(30) TYPE C,
END OF TY_STUDENTS.

DATA: LS_STUDENT TYPE TY_STUDENTS.

*Referring to local data type
DATA: LT_STUDENT_STD1 TYPE STANDARD TABLE OF TY_STUDENTS.

*Referring to local data object
DATA: LT_STUDENT_STD2 LIKE TABLE OF LS_STUDENT.

*Referring to data type in ABAP dictionary
DATA: LT_STUDENT_STD3 TYPE TABLE OF MARA.

  순서대로 로컬 데이터 타입, 데이터 오브젝트(Structure) ABAP Dictionary에 있는 Global Data Object를 참조하여 정의하는 방법이다. 테이블을 정의할 때 별다른 옵션을 주지 않으면 기본적으로 Standard Table이 생성된다.

 

Standard Table은 내부적으로 로우의 넘버링(인덱스)이 유지되어 일반적으로 인덱스 값으로 데이터를 참조하는 특징을 가진다. (key를 통한 접근도 가능하다.)

 

인덱스로 데이터를 참조하는 게 무슨 말인가 하면, 위 학생 테이블에서 '경제학과'의 '김길동'씨에 대한 정보를 찾아오려면 첫 번째 인덱스... 두 번째 인덱스 ... 를 지나 다섯 번째 인덱스에서 필요한 정보를 찾을 수 있다는 것이다.

 

인덱스로 데이터를 참조하면 $$O(n^2)$$의 시간 복잡도를 가진다. 즉, 컬럼 * 로우만큼 데이터를 탐색해야 하니 가장 느린 탐색 속도를 가지는 것이다. 하지만 인덱스만 알고 있으면 그 인덱스에 대한 데이터를 조회/수정 가능하니 조작 관점에서는 강점을 가진다. 속도는 느릴 수 있지만 직관적이고 강력한 것이다.

 

 

Sorted Table

 문자 그대로 정렬된 테이블이다. 특정한 키를 지정해 주면 테이블 내부에서 자동적으로 정렬해준다. Standard Table에도 Sort 하는 방법이 있기 때문에 많이 사용하지는 않는다고 한다. 테이블을 선언할 때 Sorted옵션 값을 주어서 생성하고 정렬되는 기준인 Key를 꼭 지정해주어야 한다.

TYPES: BEGIN OF TY_STUDENTS,
ID(8)          TYPE N,
NAME(30) TYPE C,
END OF TY_STUDENTS.*Referring to local data type
DATA: LT_STUDENT_SORT TYPE SORTED TABLE OF TY_STUDENTS WITH NON-UNIQUE KEY ID.

 위와 같이 테이블을 선언해주면 ID를 기준으로 정렬되어 데이터가 삽입된다.

 

Sorted Table은 인덱스와 키로 둘 다 참조가 가능하다. 데이터가 내부적으로 정렬되기 때문에 이진 탐색이 가능하다.

이진 탐색이란 쉽게 설명하자면 탐색 부분의 중간을 정하고 원하는 탐색 값이 중간보다 크거나 작은 지를 계산해 필요한 부분은 남기고 나머지 절반은 계속 버리면서 탐색하는 방법을 말한다. 절반은 계속 버려지기 때문에 $$(1/2)^n$$의 시간 복잡도. 즉 $$O(log  n)$$의 시간 복잡도를 가진다. Standard Table보다는 빠르지만 원하지 않아도 데이터가 정렬된다는 단점이 있다.

 

 

Hasehd Table

 해시드 테이블이란 '해시'라는 고유의 주소값을 이용하는 테이블로, 테이블의 키 값을 '해싱 함수'라는 함수를 거쳐서 해시값으로 반환해주어 데이터를 참조하는 방식을 가진다. 키와 해시는 1:1로 매칭이 되어야 하기 때문에 해시드 테이블의 키는 꼭 'Unique' 해야 한다. (키와 해싱이 1:n으로 매칭이 되면 해시 충돌이 일어나게 된다.)

테이블을 선언할 때 hashed 옵션 값을 주어 선언하며 with unique key가 꼭 명시되어야 한다.

TYPES: BEGIN OF TY_STUDENTS,
ID(8)          TYPE N,
NAME(30) TYPE C,
END OF TY_STUDENTS.
*Referring to local data type
DATA: LT_STUDENT_HSH TYPE HASHED TABLE OF TY_STUDENTS WITH UNIQUE KEY ID.

 

 해시드 테이블은 키 값으로만 참조가 가능하다. 키와 해시는 1:1로 할당되므로 Hashed Table은 $$O(c)$$, 즉 상수의 시간 복잡도를 가져 가장 빠르다.

 

다음과 같이 Standard, Sorted, Hashed 테이블이 만들어졌다.

 

 

 어떤 테이블을 쓰는 지는 취향과 요구에 맞게 쓰면 된다. 필자의 과정에서는 Standard Table만 다뤄진다고 하니 앞으로의 포스팅도 Standard Table 위주로 다루려고 한다.

 테이블의 특징과 선언 방법을 알았으니 이제 데이터를 테이블에 불러오거나 테이블에 데이터를 담아주면 된다.

 

 

 

헤더 라인이 있는 테이블과 없는 테이블

 

 테이블의 구조적인 분류로 헤더 라인이 있는 테이블과 없는 테이블이 있다. 헤더 라인이 없는 테이블은 요즘 실무에서는 거의 사용하지 않는다고 한다(라고 ABAP 강사님에게 들었다). 때문에 이게 뭔지만 잠깐 알아보고 넘어가도록 하겠다.

 

 헤더라인이 있는 테이블이란 테이블을 생성하면 헤더 라인이 자동적으로 생성되는 테이블을 말한다. 헤더 라인은 무엇인가? 위 '학생' 테이블에서 헤더 라인은 '이름', '나이', '학번' 등... 컬럼의 명세를 나타내는 것이다. 헤더 라인은 즉 컬럼의 정의를 Structure구조로 표현한 것이다.  

 

 헤더라인이 있는 테이블은 다음과 같이 WITH HEADER LINE 옵션을 주어 선언해 줄 수 있으며, 옵션을 주지 않은 기본값은 항상 헤더 라인이 없는 테이블이다.

TYPES: BEGIN OF TY_STUDENTS,
ID(8)          TYPE N,
NAME(30) TYPE C,
END OF TY_STUDENTS.
*Referring to local data type
DATA LT_STUDENT_WITHHEADER TYPE TABLE OF TY_STUDENTS WITH HEADER LINE.

 

생성된 테이블을 디버그해보면 다음과 같이 Struture (헤더 라인)이 생성되어 있다.

 

헤더라인(Structure)이 자동적으로 생성되었다.

 

헤더라인의 내부

 

' [ ] ' 가 붙은 Object 는 테이블 Body를 나타낸다. 헤더 라인이 있는 테이블은 데이터의 삽입과 추출을 생성된 헤더 라인을 이용해야 한다. 사용법이 다르지만 헤더 라인이 있는 테이블은 이제 잘 사용되지 않는다고 해서 더 이상 알아보지는 않겠다. 

 

 

 

테이블에 데이터 삽입 및 조회

 

 테이블은 Structure의 연속이다. 따라서 테이블에 데이터를 담아줄 때에는 담아주는 형식 역시 Structre와 동일한 형식을 가져야 한다. 즉, 테이블과 동일한 구조를 가진 Structure를 이용하여 테이블에 담아줘야 한다.

 

 데이타 타입과 그 타입을 참조하는 Structure, Standard Table을 선언하고 Structure에 값을 담아주겠다.

 

TYPES: BEGIN OF TY_STUDENTS,
ID(8)          TYPE N,
NAME(30) TYPE C,
END OF TY_STUDENTS.

*Referring to local data type
DATA LS_STUDENT TYPE TY_STUDENTS.
DATA LT_STUDENT TYPE STANDARD TABLE OF TY_STUDENTS.

*Insert Values into Structure
LS_STUDENT-ID = 00000001.
LS_STUDENT-NAME = 'person_A'.

 

 Structre LS_STUDENT에 위와 같이 값을 넣어준 후 Table LT_STUDENT에 데이터를 넣어주면 된다.

 

 데이터를 테이블에 삽입할 때에는 Insert 혹은 Append를 사용해 준다.

Insert는 테이블의 특정 인덱스에 데이터를 넣을 경우 사용하고 Append는 항상 테이블의 마지막 로우에 데이터를 삽입해 준다. Insert에서 인덱스를 생략할 경우 마지막 줄에 삽입해준다.

 다음과 같이 데이터를 넣어보자.

*Insert Values into Structure
LS_STUDENT-ID = 00000001.
LS_STUDENT-NAME = 'person_A'.

*Insert Structure(row) Into Table
APPEND LS_STUDENT TO LT_STUDENT.
CLEAR LS_STUDENT.


LS_STUDENT-ID = 00000002.
LS_STUDENT-NAME = 'person_B'.

INSERT LS_STUDENT INTO TABLE LT_STUDENT.
CLEAR LS_STUDENT.

 

Insert에 인덱스를 주어 삽입도 해보자.

LS_STUDENT-ID = 00000003.
LS_STUDENT-NAME = 'person_C'.

INSERT LS_STUDENT INTO LT_STUDENT INDEX 2.
CLEAR LS_STUDENT.

이렇게 하면 테이블에 다음과 같은 데이터가 삽입되어있다.

 

앞서 설명한대로 Insert에 인덱스를 주었더니 가운데에 person_C가 삽입되었다. 위와 같이 빈 테이블에 데이터를 삽입해 줄 때에는 Insert보다는 Append를 써주는 게 낫다. Insert와 Append의 자세한 옵션까지 설명하자면 다음과 같다. 

 

 

* 뒤에는 데이터오브젝트 혹은 필드 혹은 인덱스 값입니다.

˙APPEND LINES OF *table1 [FROM indexTO index] TO *table2

- 다수의 로우(레코드)를 추가할 때 사용한다. TABLE1의 index1 ~ index2 (생략 시 전체 범위)의 레코드를 TABLE2 맨 뒤에 확장한다.

 

˙APPEND *structure TO *table SORTED BY *field

- 테이블에 데이터를 추가한 후 FIELD 기준으로 정렬한다.

 

˙INSERT LINES OF *table1[FROM index TO index] INTO *table2[INDEX index]

- Append와 비슷하지만 table2의 특정 인덱스에 데이터를 추가해 줄 수 있다.

 

˙INSERT LINES OF *table1 [FROM index TO index] INTO TABLE *table2

- 위와 비슷하지만 테이블 형식에 제약이 없다.

 

 

위와 같이 Append와 Insert를 활용하여 Internal Table에 데이터를 넣어줄 수 있다.

 

 

 

이번에는 Internal Table에 이미 기록된 데이터를 읽는 방법이다.

 

READ TABLE LT_STUDENT INDEX 1 INTO LS_STUDENT.
WRITE:/ LS_STUDENT-ID, LS_STUDENT-NAME.

Strutcture LS_STUDENT에 Table LT_STUDENT 첫 번째 인덱스가 삽입된다. 다음과 같이 LOOP문으로 테이블을 안의 값들을 순차적으로 읽어올 수도 있다.

LOOP AT LT_STUDENT INTO LS_STUDENT.
  WRITE:/ LS_STUDENT-ID, LS_STUDENT-NAME.
  CLEAR LS_STUDENT.
ENDLOOP.

*or new ABAP expression
LOOP AT LT_STUDENT INTO DATA(IS_STUDENT).
  WRITE:/ IS_STUDENT-ID, IS_STUDENT-NAME.
  CLEAR IS_STUDENT.
ENDLOOP.

*참고) NEW ABAP은 버전 7.40 이후 새로 생긴 표현식이다

 

테이블과 관련하여 시스템 변수 SY-TABIX가 매우 유용하게 쓰이니 이를 잘 활용하기 바란다.

그러나 TABIX가 중복돼서 사용될 경우 (LOOP 안에서 READ를 수행하는 경우가 그 예이다.) 의도하는 바와는 다른 결과가 초래될 수도 있다. READ를 할 경우 SY-TABIX가 READ문에 해당되는 테이블 INDEX로 갱신되기 때문에 원래의 TABLE INDEX를 나타내지 않기 때문이다. 이럴 경우 LOOP문 가장 처음 부분에서 TABIX를 변수 하나에 저장해두면 된다.

 

데이터베이스에 있는 테이블(External Table)을 참조하여 Internal Table로 가져오는 방법도 있다.

 

TCL (트랜잭션 제어어)라고 불리는 SQL이 그것인데, 이는 다음 SQL 포스팅에서 자세하게 다루도록 하겠다.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함