DATA: gs_scarr TYPE scarr.
gs_scarr-carrid = 'AA'.
DELETE scarr FROM gs_scarr.
3. 내부 테이블로 여러 행 삭제
DATA: gt_scarr TYPE STANDARD TABLE OF scarr,
gs_scarr TYPE scarr.
gs_scarr-carrid = 'AA'.
APPEND gs_scarr TO gt_scarr.
gs_scarr-carrid = 'AF'.
APPEND gs_scarr TO gt_scarr.
DELETE scarr FROM TABLE gt_scarr.
4. 삭제 후 확인
SELECT SINGLE * FROM scarr INTO gs_scarr WHERE carrid = 'AA'.
IF sy-subrc <> 0.
WRITE: / 'AA 항공사 삭제 완료!'.
ELSE.
WRITE: / 'AA 항공사가 아직 존재함.'.
ENDIF.
DATA: gs_scarr TYPE scarr,
gv_carrid TYPE scarr-carrid.
gv_carrid = 'AA'.
gs_scarr-carrid = gv_carrid.
gs_scarr-carrname = 'Updated Airline'.
gs_scarr-url = 'https://updated-airline.com'.
UPDATE scarr FROM gs_scarr.
CLEAR gs_scarr.
SELECT SINGLE * INTO gs_scarr FROM scarr WHERE carrid = gv_carrid.
WRITE: / 'Carrid:', gs_scarr-carrid,
/ 'Carrname:', gs_scarr-carrname,
/ 'URL:', gs_scarr-url.
2. 필드를 지정해서 업데이트할 때
UPDATE scarr
SET carrname = 'Korea Airlines', url = 'https://www.koreanairlines.com'
WHERE carrid = 'KO'.
UPDATE scarr SET carrname = 'Korea Airlines', url = 'https://www.airlinekorean.com'
WHERE carrid = 'KO'.
* DB에서 다시 읽어오기
SELECT SINGLE * INTO gs_scarr FROM scarr WHERE carrid = 'KO'.
WRITE: / 'carrid: ', gs_scarr-carrid,
/ 'CARNAME: ', gs_scarr-carrname,
/ 'URL: ', gs_scarr-url.
URL이 바뀐 것을 확인할 수 있다.
db에서 읽어오려면, SELECT 로 DB를 읽어올 수 있다.
- 이렇게 하면 UPDATE가 성공적으로 되는 것을 확인할 수 있다!
3. 내부 테이블로 여러 행 업데이트 (UPDATE FROM TABLE)
DATA: gt_scarr TYPE STANDARD TABLE OF scarr,
gs_scarr TYPE scarr.
" 첫 번째 업데이트 데이터
gs_scarr-carrid = 'AA'.
gs_scarr-carrname = 'Updated American'.
gs_scarr-url = 'https://www.updatedaa.com'.
APPEND gs_scarr TO gt_scarr.
" 두 번째 업데이트 데이터
gs_scarr-carrid = 'AF'.
gs_scarr-carrname = 'Updated France'.
gs_scarr-url = 'https://www.updatedaf.com'.
APPEND gs_scarr TO gt_scarr.
" 내부 테이블로 업데이트
UPDATE scarr FROM TABLE gt_scarr.
" 확인용 SELECT 출력
CLEAR gs_scarr.
SELECT SINGLE * INTO gs_scarr FROM scarr WHERE carrid = 'AA'.
WRITE: / 'AA ->', gs_scarr-carrname, gs_scarr-url.
SELECT SINGLE * INTO gs_scarr FROM scarr WHERE carrid = 'AF'.
WRITE: / 'AF ->', gs_scarr-carrname, gs_scarr-url.
INSERT INTO <target> <lines>.
<f2> [ASCENDING | DESCENDING] ...
- 테이블에 하나 또는 여러 개의 데이터를 삽입하면, <target>은 테이블 이름으로, 동적으로 선언할 수 있다.
1) single line
INSERT INTO <target> VALUES <wa>.
INSERT <target> FROM <wa>.
INSERT <dbtab>.
첫번째 data의 gs_scarr을 출력한 것은 메모리에 넣은 변수이고, 두번째 data gs_scarr은 db에 있는 scarr의 내용을 select로 확인할 수 있다.
2) several lines
- 항공사 운항일정을 저장하는 spfli테이블에 데이터를 추가하고 출력해보았다.
잘나옴!
하지만 위 출력결과는 DB로 부터 읽어온 결과가 아닌 메모리상 내부테이블을 읽어온것
DB에 있는 내용을 출력하고 싶으면 아래 코드로 컴파일하면 된다.
SELECT * INTO TABLE gt_spfli FROM spfli WHERE carrid = 'KO'.
LOOP AT gt_spfli INTO gs_spfli.
WRITE: / gs_spfli-carrid, gs_spfli-connid, gs_spfli-cityfrom, gs_spfli-cityto.
ENDLOOP.
INSERT spfli FROM TABLE gt_spfli ACCEPTING DUPLICATE KEYS.
- INSERT는 기본적으로 중복 키가 있으면 RUN TIME ERROR가 발생함
- 위와 같이 INSERT를 쓰면 중복 키인 행은 건너뛰고, 정상적인 행은 삽입함
전체적인 INSERT 구문 종류 정리
1.INSERT INTO … VALUES
- 한 건(insert one row) 삽입할 때 사용
DATA: gs_scarr TYPE scarr.
gs_scarr-carrid = 'KO'.
gs_scarr-carrname = 'Air Korea'.
gs_scarr-url = 'https://www.airkorea.co.kr/'.
INSERT INTO scarr VALUES gs_scarr.
- VALUES 뒤에 구조체를 넘겨서 한 줄만 삽입
2.INSERT … FROM TABLE
- 내부 테이블 전체(insert multiple rows) 삽입할 때 사용
DATA: gt_spfli TYPE TABLE OF spfli.
" gt_spfli에 데이터 채우기...
INSERT spfli FROM TABLE gt_spfli ACCEPTING DUPLICATE KEYS.
- abap 프로그럄에서는 테이블 간의 join을 하기 보다는 인터널 테이블에 데이터를 저장하고, LOOP 구문을 이용해,
추가 정보를 SELECT 하여 인터널 테이블 내용을 MODIFY하는 경우가 많다
2. inner join
TYPES : BEGIN OF t_str,
carrid TYPE sflight-carrid,
carrname TYPE scarr-carrname,
END OF t_str.
DATA : gs_str TYPE t_str.
SELECT SINGLE a~carrid b~carrname
INTO CORRESPONDING FIELDS OF gs_str
FROM sflight as a
INNER JOIN scarr as b
on a~carrid = b~carrid
WHERE a~carrid = 'AA'.
WRITE : gs_str-carrid, gs_str-carrname.
출력 => AA American Airlines
- inner join은 a와 b 테이블에서 조건이 있는 공통점들을 추출하는 조인이다.
3. Outer join
- Inner join은 두 테이블 모두 일치하는 값이 있어야 결과가 나오지만, Outer join은 한 쪽 테이블에는 데이터가 있고 다른 쪽엔 없어도 결과를 보여준다.
JOIN 종류
설명
INNER JOIN
양쪽 테이블에 모두 존재하는 데이터만 결과로 가져옴
LEFT OUTER JOIN
왼쪽 테이블 기준, 오른쪽이 없어도 왼쪽 데이터는 결과에 포함됨
RIGHT OUTER JOIN
오른쪽 테이블 기준, 왼쪽이 없어도 오른쪽 데이터는 결과에 포함됨
- outer join은 단독으로 쓸 수 없으며 방향을 무조건 지정해야한다(Left or Right).
- 아밥 문법적으로 명확한 방향성이 있어야하기 때문이다.
예를들어,
INSA 테이블은 사원 기본 정보를 저장, CERT 테이블은 사원이 취득한 자격증 벙보를 저장하고 있다고 치자.
사원 Konie와 Wonie는 자격증을 가지고 있지만, 사원 sonie는 자격증이 없다. 사원 번호 필드를 기준으로 Inner join 하게 되면 사원 sonie는 자격증이 없기 떄문에 결과에서 제외된다.
기본적으로는 모든 사원 정보는 조회하고, 자격증이 있는 경우만 추가적으로 보여주고자 할 때 OUTER JOIN이 필요하다.
1) interval조건
- 조건에 범위 값을 사용할 때 사용한다.
Select ... where <s> [not] BETWEEN <f1> AND <f2>
2) String 비교
- 문자열 비교할 떄는 LIKE or '_'문자를 사용한다.
COL2 = 'ABCDEFGHIJK'.
SELECT ~ WHERE COL2 LIKE 'ABC%'.
// 4문자 구할 때
WHERE COL2 LIKE 'ABC_'.
3) list value
SELECT a~carrid, a~connid, b~carrname
INTO CORRESPONDING FIELDS OF TABLE @gt_str
FROM sflight AS a
LEFT OUTER JOIN scarr AS b
ON a~carrid = b~carrid
WHERE a~carrid IN ('AA', 'ZZ').
- List Value : in 구문을 사용해, 여러 조건에 속한 경우의 값을 가져올 수 있다. AA, ZZ 일 경우 whrere in ('aa', 'zz')를 사용할 수 있다.
4) selection table
SELECT-OPTIONS: so_carrid FOR sflight-carrid.
이 줄은 -> ABAP이 자동으로 아래와 같은 내부테이블을 만든다.
sign
option
low
high
I
EQ
AA
I
BT
AB
AD
I
EQ
LH
5) Dynamic 조건
- select 구문의 조건을 설정하는 where구문을 동적으로 구성할 수 있다. Itab은 최대 72자리의 Internal table로 선언해야한다.
- 그래서 왜 dynamic조건일까? 그냥 where 다음의 조건절을 빼서, 그 변수에다 선언하면 되기 때문이다.
즉, 문자열로 구성된 조건식을 where절에 직접 넘기기 때문에 다이나믹 이라고 불린다.
이 코드에서
CONCATENATE 'carrid = ''' gv_carrid '''' INTO gs_where.
// 결과 : carrid = 'AC'
결과는 carrid = 'AC'로 나온다.
이 문자열을 where (gs_where)에서 조건식처럼 사용할 수 있다.
// 일반적인 정적 조건문
SELECT SINGLE carrname
INTO gv_carrname
FROM scarr
WHERE carrid = 'AC'.
// 다이나믹 조건문
DATA: gs_where TYPE c LENGTH 72.
WHERE (gs_where).
-> 조건식 자체가 문자열로 되어있고, 실행 시점에 바뀔 수 있다.
- 사용자 입력값이나 설정값에 따라 매번 달라지는 경우
- select-options, parameters를 직접 파싱해서 동적 조건으로 만들고 싶을 때
- 복잡한 검색조건을 조합해서 쿼리에 반영할 때 유용하다.
- concatenate로 문자열을 이어붙여서 2개 이상의 조건이 필요할 경우, 동적으로 구성이 가능하다.
6) for all entries
SELECT ...
FROM <table>
INTO TABLE <result_itab>
FOR ALL ENTRIES IN <source_itab>
WHERE <field> = <source_itab>-<field>.
DATA: lt_carrid TYPE TABLE OF sflight,
lt_scarr TYPE TABLE OF scarr.
SELECT carrid
FROM sflight
INTO TABLE lt_carrid
WHERE price > 500.
IF lt_carrid IS NOT INITIAL.
SELECT *
FROM scarr
INTO TABLE lt_scarr
FOR ALL ENTRIES IN lt_carrid
WHERE carrid = lt_carrid-carrid.
ENDIF.
7) GROUPING
- group by는 SQL 집계 함수(count, sum, max, min)과 함께 데이터를 그룹별로 묶어서 처리할 때 사용하는 구문.
함수
설명
COUNT(*)
행 수를 센다
SUM(필드)
합계를 구한다
MAX(필드)
최대값
MIN(필드)
최소값
AVG(필드)
평균값 (SAP HANA에서만 가능)
STDDEV
표준편차
VARIANCE
분산
SELECT carrid, COUNT(*) AS cnt
FROM sflight
INTO TABLE @DATA(result)
GROUP BY carrid.
LOOP AT result INTO DATA(ls_result).
WRITE: / 'carrid: ', ls_result-carrid, 'cnt: ', ls_result-cnt.
ENDLOOP.
- 이런식으로 grouping을 하면, 각 carrid에 있는 항공사명의 Cnt를 셀 수 있게 된다.
- 항공기 ID별 평균 예약 점유율을 SELECT하는 구문을 실습하면, `Unknown column name "AVG"`라고 뜬다.
SELECT carrid, connid, price
INTO TABLE @DATA(result)
FROM sflight
WHERE price > (
SELECT AVG( price )
FROM sflight
).
✔️ 실습문제 예시
문제: sflight 테이블에서 평균 가격보다 높은 항공편의 carrid, connid, price를 출력하세요.
SELECT carrid, connid, price
INTO TABLE @DATA(gt_result)
FROM sflight
WHERE price > (
SELECT AVG( price ) FROM sflight
).
EXISTS 서브쿼리
**서브쿼리 결과가 존재하는지(true/false)**를 조건으로 확인.
보통 상관 서브쿼리 형태로 많이 씀.
SELECT carrid, connid, price
INTO TABLE @DATA(gt_exists)
FROM sflight AS a
WHERE EXISTS
( SELECT * FROM scarr AS b WHERE b~carrid = a~carrid AND b~carrname = 'Lufthansa' ).
📝 의미: sflight에서 carrid가 Lufthansa인 항공사에 속한 데이터만 가져오기
IN 서브쿼리
값이 특정 리스트 안에 있는지 확인할 때 사용
SELECT carrid, connid, price
INTO TABLE @DATA(gt_in) FROM sflight
WHERE carrid IN ( SELECT carrid FROM scarr WHERE currcode = 'EUR' ).
- 여러 라인을 조회할 때는 select 결과가 내부 테이블에 저장되는데, 이를 인터널 테이블이라고 부른다.
Distinct 를 사용하게 되면 중복된 값이 제외된다.
`SELECT [DISTINCT] <cols> ... WHERE`
- INTO구문의 결과가 저장되는 곳이 인터널 테이블이 아닌 필드orWorkArea(구조체)일 때는 마지막에 ENDSELECT를 사용해야한다.
- gt_itab = 내부테이블
- gs_wa = 워크에어리어_구조체
- 구조체를 출력하는 코드
- sap 테이블인 sflight는 여러 필드_컬럼을 갖고 있음 => gs_wa는 로컬구조체가 되고, gs_wa-carrid 처럼 개별 필드에 접근할 수 있음
- TYPE sflight을 쓰면, 그 테이블의 행 구조를 그대로 복사하고 데이터는 들어있지 않고 구조만 복사됨
- Select 쓰면 실제 행을 읽어와서 gs_wa에 데이터를 담는 작업이 실행됨
- g_itab 내부테이블을 출력하는 코드
- DB에서 조건에 맞는 모든 데이터를 한꺼번에 gt_itab에 담고, LOOP AT으로 내부에서 반복함(메모리 반복)
비교
항목
SELECT INTO TABLE
SELECT ... ENDSELECT
데이터 처리 시점
DB에서 모두 가져와서 한꺼번에 메모리로
DB에서 한 줄씩 읽고 바로 처리
반복 방식
LOOP AT 필요
자체 반복 (SELECT-ENDSELECT가 루프 역할)
성능
대량 데이터 시, 메모리 사용 많음
대량 데이터 시, I/O 부담 적음
예시 비교 (결과는 같음)
① SELECT INTO TABLE + LOOP
SELECT * FROM sflight INTO TABLE gt_itab WHERE carrid = 'AA'.
LOOP AT gt_itab INTO gs_wa.
WRITE: / gs_wa-carrid, gs_wa-connid.
ENDLOOP.
② SELECT ... ENDSELECT
SELECT * FROM sflight INTO gs_wa WHERE carrid = 'AA'.
WRITE: / gs_wa-carrid, gs_wa-connid.
ENDSELECT.
=> 둘 다 "AA"인 항공편들을 같은 순서로 출력하게 됨
- as(Alias)
AS구문을 사용해서, 칼럼 명에 병명을 지정할 수 있음
gs_line = 'CARRID CONNID'.
SELECT DISTINCT carrid connid INTO TABLE gt_itab FROM sflight.
gt_itab는 내부 테이블, 여러 줄 데이터를 담는다.
gs_wa는 그 내부 테이블의 한 줄과 동일한 구조를 가진 구조체.
gs_wa는 오직 하나의 row만 저장할 수 있다.
LIKE LINE OF로 선언한 변수는 내부 테이블의 한 줄(row)을 임시로 담는 용도, 즉 "임시 저장소(temp variable)" 역할
Select intto corresponding fields of <Wa>
SELECT *
INTO CORRESPONDING FIELDS OF <워크에어리어>
FROM <테이블>
WHERE <조건>.
- 구조체에 필드가 일부만 포함되어 있을 때
- 또는 필드명이 DB테이블과 완전히 일치할 때 자동으로 매핑시켜줌
TYPES: BEGIN OF ty_flight_short,
carrid TYPE sflight-carrid,
connid TYPE sflight-connid,
END OF ty_flight_short.
DATA: gs_short TYPE ty_flight_short.
SELECT *
INTO CORRESPONDING FIELDS OF gs_short
FROM sflight
WHERE carrid = 'AA'.
WRITE: / gs_short-carrid, gs_short-connid.
EXIT.
ENDSELECT.
② Parameters
- Parameters 명령어를 이용하면 사용자가 값을 입력하는 화면_Selection Screen이 조회된다.
- gs_wa-carrid와 gs_wa-connid가 생성된다.
GPT와 함께 하는 실습 문제 set.
1.
첫번째 답
DATA : gs_wa TYPE sflight.
SELECT SINGLE * INTO gs_wa FROM sflight WHERE carrid = 'AA'.
WRITE: gs_wa-carrid.
ENDSELECT. "❌ 오류 발생
select문인데 왜 Endselect를 했을 때 오류가 생길까?
select single 은 단일 행 조회이기 때문에 block이 없기 때문이다.
data : gs_wa TYPE sflight.
SELECT single * into gs_wa from sflight where carrid EQ 'AA'.
- 이렇게 조회하면 AA의 이름을 가진 carrid가 출력이 된다.
2.
DATA : gt_itab TYPE TABLE OF sflight,
gt_temp TYPE sflight.
SELECT carrid, connid FROM sflight INTO CORRESPONDING FIELDS OF TABLE
@gt_itab WHERE carrid = 'AA'.
LOOP AT gt_itab INTO gt_temp.
WRITE : / gt_temp-carrid, gt_temp-connid.
ENDLOOP.
- 내부테이블에서 LOOP AT <내부테이블> into <작업구조> 형태로 루프를 돌릴 땐, 루프 안에서 한 줄씩 담아줄 작업구조, 즉 워크에어리어가 필요하다.
- `LOOP AT gt_itab ASSIGNING FILED-SYMBOL(<fs>)`처럼 field-symbol을 쓰면 gt_temp가 없어도 된다
3.
DATA : gt_itab TYPE TABLE OF sflight,
gt_temp TYPE sflight.
SELECT carrid, price INTO CORRESPONDING FIELDS OF TABLE
@gt_itab TYPE sflight
WHERE carrid = 'AA' AND price >= 300.
LOOP AT gt_itab INTO gt_temp.
WRITE : / gt_temp-carrid, gt_temp-price.
ENDLOOP.
- 꼭 @이 필요할까?
- @은 ABAP 7.40 이후 버전에서 도입된 ABAP new Syntax 때문에 등장함
✅ 다음 문제 (문제 5)
문제 5: SELECT SINGLE + 구조체 + 조건
sflight 테이블에서 carrid = 'AA'이고 connid = '0017'인 데이터를 구조체 하나에 담아서,
fldate, price, currency 3개 필드를 WRITE로 출력해보세요.
- 문제의 조건은 single이였는데, 못보고 그냥 풀어버렸다.
- single로 푼 문제.
처음에는 `SELECT SINGLE * INTO gt_itab...` 이렇게 풀려고 했으나, 컴파일이 안되는 것을 확인했다.
그 이유는 gt_itab 내부테이블은 여러 개의 행을 저장할 수 있는 자료형이지만, select single은 단 하나의 행만 가져오기 때문에
DATA lv TYPE string VALUE 'ABAP'.
SHIFT lv RIGHT CIRCULAR.
WRITE: / lv. " 결과: PABA
5. CONDENSE – 공백 압축
여러 공백을 하나로 줄이기, 양끝 공백도 제거 가능
문법
CONDENSE lv_text. " 기본: 중간 공백 압축 + 앞뒤 제거
CONDENSE lv_text NO-GAPS. " 모든 공백 제거
예시
DATA lv_text TYPE string VALUE ' ABAP IS FUN '.
CONDENSE lv_text.
WRITE: / lv_text. " 결과: 'ABAP IS FUN'
=> 중간 공백 압축 + 앞 뒤 제거가 된다.
=> `condense lv_tex2 no-gaps1` 하면 공백이 다 제거된다.
6. OVERLAY – 문자 겹쳐쓰기
기존 문자열을 다른 문자로 덮어쓰기
문법
OVERLAY lv_target WITH '****'.
예시
DATA lv_text TYPE string VALUE 'ABAP'.
OVERLAY lv_text WITH '####'.
WRITE: / lv_text. " 결과: ####
gv_str = ' B . p '.
gv_chr = 'AAAA'.
overlay gv_str with gv_chr.
write / gv_str.
7. CONCATENATE – 문자열 연결
여러 문자열을 하나로 합침
문법
CONCATENATE lv1 lv2 INTO lv_result [SEPARATED BY space].
예시
DATA: lv1 TYPE string VALUE 'Hello', lv2 TYPE string VALUE 'World', lv_result TYPE string.
CONCATENATE lv1 lv2 INTO lv_result SEPARATED BY space.
WRITE: / lv_result. " 결과: Hello World
gv_str 변수에 gv_chr를 합친다.
8. SPLIT – 문자열 나누기
구분자를 기준으로 문자열 분해
문법
SPLIT lv_text AT ',' INTO lv_a lv_b.
예시
DATA: lv_text TYPE string VALUE 'ABAP,PROGRAMMING', lv_a TYPE string, lv_b TYPE string.
SPLIT lv_text AT ',' INTO lv_a lv_b.
WRITE: / lv_a, / lv_b. " 결과: ABAP / PROGRAMMING
* 연습하기*
'Hello_World_42' → 'Hello', 'World', '42'로 나누고, 숫자 부분은 숫자로 바꿔서 2 더한 결과 출력하기
data lv_str type string value 'Hello_world_42', lv_a type string, lv_b type string, lv_c type i.
split lv_str at '_' into lv_a lv_b lv_c.
lv_c = lv_c + 2.
write / lv_c.
=> welcome to abap world 만들기
DATA lv_text TYPE string VALUE '##HELLO_world42##'.
REPLACE ALL OCCURRENCES OF '##' IN lv_text WITH ''.
REPLACE ALL OCCURRENCES OF '_' IN lv_text WITH ' '.
REPLACE ALL OCCURRENCES OF REGEX '[0-9]' IN lv_text WITH ''.
TRANSLATE lv_text TO LOWER CASE.
CONDENSE lv_text.
WRITE: / lv_text. " 결과: hello world