SAP BOX

'26/11/13 UPDATE

ABAP / FIELD NOTES

한 칼럼만 고치려던 UPDATE … FROM wa, 나머지 칼럼은 왜 비었을까

키와 STATUS만 넣은 작업 영역으로 UPDATE … FROM을 실행하면 sy-subrc는 0인데 나머지 칼럼이 초기값으로 덮입니다. FROM 작업 영역이 행 전체를 바꾸는 이유, sy-subrc와 sy-dbcnt가 알려 주지 않는 것, SET·INDICATORS(7.55 이상)·먼저 읽고 쓰기·MODIFY 가운데 무엇을 고를지 가상 예로 정리합니다.

출하 한 건의 상태만 'S'로 바꾸려고 작업 영역에 키와 STATUS만 넣은 뒤 UPDATE zship_status FROM @ls_ship.을 실행하면, sy-subrc는 0인데 그 행의 운송사, 예정일, 변경자 칼럼이 빈 값이 됩니다. 이 글을 읽고 나면 UPDATE … FROM 작업 영역이 행의 어디까지 바꾸는지 설명할 수 있고, 칼럼 몇 개만 바꾸려던 자리를 SET, INDICATORS, 먼저 읽고 고쳐 쓰기 가운데 하나로 고칠 수 있습니다.

예시는 가상 Z 테이블 ZSHIP_STATUS(키 MANDT, SHIP_ID, 일반 칼럼 STATUS, CARRIER, PLAN_DATE, CHANGED_BY)와 가상 프로그램 ZDEMO_SHIP_STATUS입니다. 출하 0000004711 행은 처음에 STATUS P, CARRIER C001, PLAN_DATE 20261120, CHANGED_BY USER_A입니다. 코드는 실제 시스템에서 실행하지 않았습니다. 동작은 ABAP 키워드 문서 7.58판의 UPDATE source 설명(영어)과 관련 페이지에 적힌 대로 옮겼습니다.

FROM 작업 영역은 행 전체를 바꾼다

UPDATE로 데이터베이스 테이블을 바꾸는 길은 둘입니다. SET 뒤에 바꿀 칼럼을 적거나, FROM 뒤에 작업 영역을 주어 행 전체를 덮어쓰는 것입니다. FROM @ls_ship은 작업 영역 앞부분의 기본 키 값으로 같은 행을 찾고, 찾은 행에 작업 영역 내용을 그대로 씁니다. 변환 없이 왼쪽부터 차례로 들어가며, '값을 넣은 칼럼만'이라는 구분은 없습니다.

위 예에서 ls_ship은 테이블을 참조해 선언했으므로 구조는 맞습니다. 하지만 값을 넣은 것은 ship_id와 status뿐입니다. 나머지 세 칼럼에는 형식의 초기값, 곧 문자 칼럼은 공백, 날짜 칼럼은 00000000이 들어 있고, 이 값도 행에 그대로 쓰입니다. 작업 영역의 클라이언트 칼럼은 무시되고 현재 클라이언트의 행이 바뀝니다. 다른 클라이언트를 대상으로 하려면 USING CLIENT 같은 추가 구문을 따로 씁니다.

SAP GUI ABAP 편집기 화면 예시. 가상 테이블 ZSHIP_STATUS를 참조한 작업 영역 ls_ship에 ship_id 0000004711과 status 'S'만 넣고 UPDATE zship_status FROM @ls_ship을 실행한 뒤 IF sy-subrc <> 0으로 검사하는 코드와, 그 아래 UPDATE zship_status SET status = 'S' WHERE ship_id = @lv_ship_id 코드에 번호 네 개가 표시되어 있다.
STATUS만 바꾸려고 쓴 UPDATE … FROM과, 같은 뜻을 SET으로 쓴 문장을 한 화면에 놓은 예시. ZDEMO_SHIP_STATUS, ZSHIP_STATUS와 값은 가상이며 코드는 실행하지 않았다.

sy-subrc 0은 '행을 찾아 바꿨다'는 뜻일 뿐이다

이 실수가 오래 남는 이유는 결과가 정상으로 보이기 때문입니다. 작업 영역으로 UPDATE하면 지정한 행을 바꿨을 때 sy-subrc가 0이고, 바꾼 행 수 sy-dbcnt는 1입니다. 4가 되는 것은 행을 하나도 바꾸지 못했을 때입니다. 키가 같은 행이 없거나, 바꾸면 기본 키나 고유 보조 인덱스에 중복이 생기는 경우입니다. 행이 없어도 UPDATE는 새 행을 넣지 않습니다.

그래서 IF sy-subrc <> 0. 검사는 '그 출하가 있었는가'만 확인합니다. 어느 칼럼이 무엇으로 바뀌었는지는 어떤 시스템 필드도 알려 주지 않습니다. 테스트에서 STATUS가 S로 바뀐 것만 보고 넘어가면, 운송사와 예정일이 빈 행이 운영에 그대로 남습니다.

SAP 노트

같은 행에 세 가지 UPDATE

가상 테이블 ZSHIP_STATUS, 키 SHIP_ID 0000004711의 STATUS만 'S'로 바꾸려 할 때

  • UPDATE 전의 행

    • STATUS P
    • CARRIER C001
    • PLAN_DATE 20261120
    • CHANGED_BY USER_A
  • UPDATE … FROM @ls_ship

    sy-subrc 0 · sy-dbcnt 1

    • STATUS S
    • CARRIER 빈칸(초기값)
    • PLAN_DATE 00000000(초기값)
    • CHANGED_BY 빈칸(초기값)
  • UPDATE … SET status = 'S'

    WHERE ship_id = @lv_ship_id

    • STATUS S
    • CARRIER C001
    • PLAN_DATE 20261120
    • CHANGED_BY USER_A
  • FROM … INDICATORS (7.55 이상)

    SET STRUCTURE col_ind

    • col_ind-status에만 'X'
    • STATUS만 S로 바뀜
    • 나머지 세 칼럼은 그대로
    • 키 칼럼에는 표시가 효과 없음

세 문장 모두 sy-subrc 0으로 끝납니다. 행이 어떻게 바뀌었는지는 sy-subrc가 알려 주지 않습니다. 테이블, 키, 값은 가상입니다.

FROM 작업 영역은 키가 같은 행의 칼럼 전체를 작업 영역 값으로 바꿉니다. SET과 INDICATORS는 지정한 칼럼만 바꿉니다.

칼럼 몇 개만 바꿀 때는 SET

바꾸려는 것이 STATUS 하나라면 처음부터 UPDATE zship_status SET status = 'S' WHERE ship_id = @lv_ship_id.로 씁니다. SET 뒤에는 바꿀 칼럼을 쉼표로 이어 적고, col = col + f처럼 지금 값에 더하는 형태도 쓸 수 있습니다. 한 칼럼은 한 번만 적을 수 있습니다. 적지 않은 칼럼은 이 문장이 건드리지 않습니다.

SET에는 다른 위험이 하나 있습니다. WHERE를 빠뜨리면 대상 테이블의 모든 행, 클라이언트 종속 테이블이라면 현재 클라이언트의 모든 행이 바뀝니다. SET을 쓴 문장은 한 행 이상 바꾸면 sy-subrc가 0이므로, 키 하나를 바꾸려던 문장이라면 실행 뒤 sy-dbcnt가 1인지 확인하는 편이 안전합니다.

구조로 넘겨야 한다면 INDICATORS (7.55 이상)

여러 행을 내부 테이블로 한 번에 바꾸거나 구조 단위로 값을 주고받는 코드라면 SET으로 옮기기 어렵습니다. ABAP 7.55부터는 UPDATE … FROM 뒤에 표시 구조(set indicator)를 붙여 바꿀 칼럼을 고를 수 있습니다. 7.55 릴리스 노트에 TYPES의 INDICATORS 추가 구문과 UPDATE FROM 뒤의 표시 구조가 함께 새 항목으로 실려 있습니다.

먼저 TYPES ty_ship_ind TYPE zship_status WITH INDICATORS col_ind TYPE abap_bool.처럼, 테이블 구조 끝에 칼럼마다 한 글자짜리 표시를 가진 하위 구조 col_ind를 붙인 형식을 만듭니다. 그 형식의 작업 영역에 키와 새 STATUS를 넣고 col_ind-status에만 'X'를 넣은 뒤 UPDATE zship_status FROM @ls_ship_ind INDICATORS SET STRUCTURE col_ind.을 실행하면 STATUS만 바뀝니다. 표시가 'X'가 아닌 칼럼은 바뀌지 않고, NOT SET으로 쓰면 거꾸로 'X'인 칼럼만 빼고 바뀝니다. 키 칼럼도 표시 구조에 들어 있어야 하지만 키 칼럼의 표시는 아무 효과가 없습니다. 7.55보다 앞선 릴리스에서는 이 문법을 쓸 수 없으니 SET이나 아래의 먼저 읽기 방식을 씁니다.

먼저 읽고 고쳐 쓰기, 그리고 MODIFY

행 전체를 쓰는 것이 맞는 경우도 있습니다. 그럴 때는 SELECT SINGLE * FROM zship_status WHERE ship_id = @lv_ship_id INTO @ls_ship.으로 지금 행을 모두 읽고, 필요한 칼럼만 바꾼 뒤 FROM @ls_ship으로 씁니다. 나머지 칼럼은 읽은 값이 그대로 다시 쓰이므로 비지 않습니다. 다만 읽은 뒤 쓰기 전 사이에 다른 프로그램이 CARRIER를 바꿨다면, 그 변경은 읽어 둔 옛 값으로 덮입니다. 같은 행을 여러 곳에서 고칠 수 있다면 읽기 전에 SAP 잠금을 거는 것이 전제입니다.

MODIFY zship_status FROM @ls_ship.도 같은 규칙을 따릅니다. 키가 같은 행이 있으면 UPDATE 규칙대로 덮어쓰고, 없으면 INSERT 규칙대로 새 행을 넣습니다. 키와 STATUS만 채운 작업 영역으로 MODIFY를 실행하면, 행이 있을 때는 앞의 예처럼 칼럼이 비고, 없을 때는 나머지 칼럼이 초기값인 새 행이 생깁니다. 확인한 7.58판 문서의 MODIFY 작업 영역 설명에는 INDICATORS가 나오지 않습니다.

SAP 노트

무엇으로 바꿔 쓸까

바꾸려는 범위에 따라 고르는 네 문장, 가상 테이블 ZSHIP_STATUS 기준

  • 칼럼 몇 개만 바꾼다

    SET status = 'S'

    • UPDATE … SET col = … WHERE 키
    • SET에 적은 칼럼만 바뀜
    • WHERE가 없으면 클라이언트 전체 행
    • 바뀐 행 수는 sy-dbcnt
  • 구조로 넘기되 일부만 (7.55 이상)

    INDICATORS SET STRUCTURE

    • TYPES … WITH INDICATORS col_ind
    • 바꿀 칼럼의 표시에 'X'
    • NOT SET이면 'X'만 빼고 바뀜
    • 그 이전 릴리스에는 문법 없음
  • 행 전체를 쓰는 게 맞다

    UPDATE … FROM @ls_ship

    • 먼저 SELECT SINGLE *로 행 전체를 읽음
    • 필요한 칼럼만 바꾼 뒤 FROM으로 씀
    • 읽은 뒤 남이 바꾼 값도 덮어씀
    • 읽기 전에 잠금이 필요
  • MODIFY … FROM도 같다

    MODIFY zship_status FROM @ls_ship

    • 키가 같은 행이 있으면 UPDATE처럼 덮어씀
    • 없으면 INSERT처럼 새 행을 넣음
    • 작업 영역을 일부만 채웠다면
    • 새 행의 나머지 칼럼은 초기값

확인한 판은 ABAP 키워드 문서 7.58입니다. INDICATORS는 7.55 릴리스 노트에 추가 항목으로 나옵니다. 테이블과 값은 가상입니다.

바꾸려는 것이 칼럼 몇 개인지, 행 전체인지 먼저 정하면 문장이 정해집니다. MODIFY는 덮어쓰기에 더해 없는 행을 새로 넣습니다.

코드에서 먼저 찾아볼 곳

프로그램에서 UPDATE나 MODIFY 뒤에 FROM 작업 영역이 오는 줄을 찾고, 그 작업 영역이 어디서 채워졌는지 거슬러 올라가 봅니다. 새로 선언하거나 CLEAR한 뒤 몇 칼럼만 넣었다면 이 글의 경우입니다. 같은 행을 SELECT SINGLE *로 읽어 채웠다면 칼럼이 비지는 않으니, 잠금이 있는지를 함께 봅니다.

FROM 작업 영역을 볼 때마다 '이 작업 영역의 모든 칼럼이 지금 데이터베이스에 남아야 할 값인가'를 물어보세요. 아니라고 답하는 칼럼이 하나라도 있으면 그 문장은 SET이나 INDICATORS로 바꿀 자리입니다.

끝 · END OF NOTE
RSS 구독목록으로
COMMENTS BOX

이 기록에 대화를 더해 주세요.

궁금한 점, 다른 접근, 직접 해 본 결과를 나눠 주세요.

최신순

로그인 상태 확인 중…

댓글을 불러오는 중…