SAP BOX

'26/10/11 UPDATE

ABAP / FIELD NOTES

MOVE-CORRESPONDING으로 내부 테이블을 옮겼더니 왜 기존 행이 사라질까

내부 테이블끼리의 MOVE-CORRESPONDING은 같은 이름 열만 채우는 문장이 아니라 대상 테이블을 지우고 원본 행 수대로 다시 만드는 문장입니다. KEEPING TARGET LINES가 행을 덧붙이기만 하는 이유, 기존 행을 키로 보강하는 CORRESPONDING 조회 테이블, 헤더 라인 함정을 가상 예로 봅니다.

주문 테이블에 상태 값만 채워 넣으려고 상태 테이블을 MOVE-CORRESPONDING으로 옮겼더니, 주문이 세 건에서 두 건으로 줄고 고객과 금액이 모두 비어 버렸습니다. 구조체에서 쓰던 그대로 내부 테이블에 쓰면 흔히 겪는 일입니다. 이 글을 읽고 나면 내부 테이블끼리의 MOVE-CORRESPONDING이 대상 테이블을 어떻게 다시 만드는지 설명하고, 기존 행을 보강할 때 무엇을 써야 하는지, 오래된 코드의 헤더 라인 때문에 엉뚱한 대입이 일어나는 자리를 찾아낼 수 있습니다.

예시는 주문 테이블 lt_orders(열 order_id, customer, amount, status)와 상태 테이블 lt_status(열 order_id, status)입니다. lt_orders에는 4711, 4712, 4713 세 주문이 상태 없이 있고, lt_status에는 4711의 상태 B와 4713의 상태 C가 있습니다. 주문번호, 고객, 금액, 프로그램은 모두 지어낸 것이고 코드는 실제 시스템에서 실행하지 않았습니다. 글에 적은 결과는 ABAP 키워드 문서의 MOVE-CORRESPONDING과 CORRESPONDING 조회 테이블 페이지(7.58판, 7.50판)의 규칙을 그대로 적용해 따진 값이며, 문서 본문은 영어입니다.

내부 테이블끼리는 대상을 지우고 다시 만든다

MOVE-CORRESPONDING lt_status TO lt_orders.를 실행하면 ABAP는 두 테이블의 행 타입에서 이름이 같은 성분을 찾습니다. 여기서는 order_id와 status입니다. 같은 이름이 하나라도 있으면 대상 테이블 lt_orders를 먼저 지우고, 원본 lt_status의 행 수만큼 초기값 행을 넣습니다. 그다음 원본 행을 LOOP와 같은 순서로 읽어 각 행의 같은 이름 성분을 새 행에 대입합니다.

결과는 두 행입니다. 4711과 4713에는 상태 B와 C가 들어가지만 customer는 비어 있고 amount는 0.00입니다. lt_status에 없던 4712 주문은 아예 사라집니다. 이 문장은 주문번호로 짝을 찾아 상태만 채워 주는 문장이 아닙니다. 원본 테이블의 모양대로 대상 테이블을 새로 만드는 문장입니다.

SAP 노트

상태 2건을 옮겼더니 주문 3건이 2건으로

가상 예: lt_orders(주문번호·고객·금액·상태)에 lt_status(주문번호·상태)를 옮긴다

  1. 실행 전 lt_orders

    lines( lt_orders ) = 3

    • 4711 · CUST-A · 120.00 · 상태 없음
    • 4712 · CUST-B · 80.00 · 상태 없음
    • 4713 · CUST-C · 45.50 · 상태 없음
  2. 옮기는 lt_status

    MOVE-CORRESPONDING lt_status TO lt_orders.

    • 4711 · 상태 B
    • 4713 · 상태 C
    • 같은 이름: order_id, status
  3. 실행 후 lt_orders

    lines( lt_orders ) = 2

    • 4711 · (빈칸) · 0.00 · B
    • 4713 · (빈칸) · 0.00 · C
    • 4712 행과 고객·금액이 사라짐

주문번호·고객·금액은 가상입니다. 결과는 ABAP 키워드 문서(7.58판, 7.50판)의 MOVE-CORRESPONDING 규칙으로 따진 값이며 실행 결과가 아닙니다.

내부 테이블끼리의 MOVE-CORRESPONDING은 주문번호로 행을 찾아 상태만 채우지 않습니다. 대상 테이블을 비우고 원본 행 수만큼 새 행을 만든 뒤 같은 이름의 열만 채웁니다.

구조체에서 배운 감각이 틀리는 이유

같은 문장을 구조체에 쓰면 동작이 다릅니다. MOVE-CORRESPONDING ls_status TO ls_order.는 같은 이름 성분인 order_id와 status만 대입하고, 나머지 성분은 건드리지 않습니다. 그래서 customer와 amount가 그대로 남습니다. 많은 개발자가 이 동작을 먼저 익히고, 테이블에서도 "같은 이름 열만 바뀌고 나머지는 남는다"고 짐작합니다. 하지만 테이블 변형에서 남는 것은 열이 아니라 원본의 행 수입니다.

규칙에는 예외도 하나 있습니다. 두 행 타입에 같은 이름 성분이 하나도 없으면 아무것도 대입하지 않고 대상 테이블도 지우지 않습니다. 열 이름이 우연히 하나만 겹쳐도 대상이 지워진다는 뜻이기도 합니다. 또 같은 테이블을 자기 자신에게 MOVE-CORRESPONDING하면 문장이 무시되어, 지웠다가 다시 채우는 일도 일어나지 않습니다.

SAP 노트

구조체와 내부 테이블은 규칙이 다르다

같은 문장 MOVE-CORRESPONDING, 피연산자에 따라 대상의 나머지 값이 달라진다

  • 구조체끼리

    ls_order-status = ls_status-status

    • 같은 이름 성분만 대입
    • 다른 성분은 영향 없음
    • 고객·금액이 그대로 남음
  • 내부 테이블끼리

    lines( ) 3 → 2

    • 대상 테이블을 먼저 삭제
    • 원본 행 수만큼 빈 행 삽입
    • 행마다 같은 이름 열만 대입
  • KEEPING TARGET LINES

    lines( ) 3 → 5

    • 기존 3행은 지우지 않음
    • 새 행 2개를 뒤에 덧붙임
    • 기존 행에 상태가 들어가지는 않음

같은 이름의 열이 하나도 없으면 대상 테이블은 지워지지도 않고 그대로 남습니다. 값은 문서 규칙으로 따진 것이며 실행 결과가 아닙니다.

구조체에 쓰던 감각으로 내부 테이블에 쓰면 안 됩니다. KEEPING TARGET LINES도 행을 합치지 않고 덧붙일 뿐입니다.

KEEPING TARGET LINES는 합치지 않고 덧붙인다

대상 행이 지워지는 것을 막는 추가 문법이 KEEPING TARGET LINES입니다. MOVE-CORRESPONDING lt_status TO lt_orders KEEPING TARGET LINES.는 기존 세 행을 지우지 않고, 원본 행 수만큼 초기값 행을 뒤에 덧붙인 뒤 그 새 행에 원본을 대입합니다. 결과는 다섯 행입니다. 기존 4711, 4712, 4713 행의 상태는 여전히 비어 있고, 주문번호와 상태만 있는 4711, 4713 행이 따로 두 개 더 생깁니다.

대상 테이블에 고유 키가 있으면 덧붙이는 행이 기존 행과 키가 겹칠 수 있습니다. 예를 들어 lt_orders가 order_id를 고유 키로 가진 정렬 테이블이라면 4711이 두 번 들어가게 됩니다. 문서는 고유성이 깨지면 해당 예외가 발생하고, 일어날 수 있는 런타임 오류는 INSERT와 같다고 적습니다. INSERT 문서는 여러 행을 한꺼번에 넣다가 고유 키가 겹치는 경우를 잡을 수 없는 런타임 오류 ITAB_DUPLICATE_KEY로 분류하므로, TRY에 기대기보다 덧붙이기 전에 키 중복을 막아야 합니다. 생성 연산자 itab2 = CORRESPONDING #( BASE ( itab2 ) itab1 ).은 KEEPING TARGET LINES를 붙인 문장과 같은 결과이고, 7.51판 이후 문서에서는 여기에 DISCARDING DUPLICATES를 붙여 중복 행을 버릴 수 있습니다.

기존 행을 보강하려면 조회 테이블을 쓴다

원래 하려던 일, 곧 주문번호가 같은 행에 상태만 채우는 일은 CORRESPONDING의 조회 테이블 변형이 합니다. lt_orders = CORRESPONDING tt_order( lt_orders FROM lt_status USING order_id = order_id ).는 lt_orders의 행마다 lt_status에서 주문번호가 같은 행을 찾습니다. 찾으면 같은 이름 성분을 대입하고, 못 찾으면 그 행을 그대로 둡니다. 검색에 쓴 order_id는 기본으로 대입하지 않습니다. 결과는 세 행이고 4711은 B, 4712는 빈칸, 4713은 C이며 고객과 금액은 그대로입니다.

조건이 하나 있습니다. 조회 테이블은 정렬 키나 해시 키로 검색되어야 합니다. KEY를 지정하지 않으면 lt_status가 정렬 테이블이나 해시 테이블이어야 하고, 비교 열이 그 키를 모두 덮어야 합니다. 표준 테이블이라면 보조 키를 정의해 USING KEY로 지정합니다. 이 변형도 7.50판과 7.58판 문서에 같은 규칙으로 있습니다. 익숙한 방법을 원하면 필드 심볼로 lt_orders를 돌면서 READ TABLE lt_status로 주문번호를 찾고, sy-subrc가 0일 때만 status를 바꾸는 루프로 써도 결과는 같습니다.

SAP 노트

기존 행에 값을 채우려면

주문번호로 짝을 찾는 일은 MOVE-CORRESPONDING이 하지 않는다

  • CORRESPONDING ... FROM

    USING order_id = order_id

    • lt_orders 행마다 lt_status에서 검색
    • 찾으면 같은 이름 열을 대입
    • 못 찾은 4712는 그대로 남음
  • LOOP와 READ TABLE

    IF sy-subrc = 0.

    • 필드 심볼로 lt_orders를 돌며
    • 주문번호로 lt_status를 읽고
    • 찾았을 때만 status를 바꿈
  • 행 추가가 목적일 때

    lines( ) 3 + 2 = 5

    • KEEPING TARGET LINES 또는
    • CORRESPONDING #( BASE ( ... ) ... )
    • 고유 키 중복이면 런타임 오류

CORRESPONDING ... FROM은 lt_status가 정렬 키나 해시 키로 검색될 수 있어야 합니다. 예시는 가상이며 실행하지 않았습니다.

기존 행을 보강하는 일은 조회 테이블을 쓰는 CORRESPONDING이나 LOOP와 READ TABLE로 합니다. 행을 덧붙이는 것이 목적일 때만 KEEPING TARGET LINES나 BASE를 씁니다.

헤더 라인과 릴리스에 따라 다른 점

내부 테이블을 피연산자로 쓰는 MOVE-CORRESPONDING은 ABAP 7.40 SP05에서 들어왔습니다. 그 전에는 구조체끼리만 쓸 수 있었습니다. 같은 릴리스에 EXPANDING NESTED TABLES, KEEPING TARGET LINES, 생성 연산자 CORRESPONDING도 함께 추가되었습니다.

오래된 프로그램에서는 헤더 라인을 먼저 봅니다. WITH HEADER LINE이나 DATA BEGIN OF ... OCCURS로 선언한 테이블, SELECT-OPTIONS, 함수 모듈의 TABLES 파라미터에는 테이블과 이름이 같은 작업 영역인 헤더 라인이 있습니다. 이런 테이블의 이름만 MOVE-CORRESPONDING에 쓰면 테이블 본문이 아니라 헤더 라인이 피연산자가 되어, 행 한 개짜리 구조체 대입이 일어납니다. 테이블 전체를 옮기려면 t_status[]처럼 []를 붙여 본문을 지정합니다. 헤더 라인은 폐기된 기능이고 클래스 안에서는 선언할 수 없습니다.

SAP GUI ABAP 편집기 화면. MOVE-CORRESPONDING lt_status TO lt_orders 뒤에 행이 2개가 되고 고객과 금액이 초기값이라는 주석, KEEPING TARGET LINES를 붙이면 3행에 새 2행이 더해져 5행이라는 주석, CORRESPONDING tt_order( lt_orders FROM lt_status USING order_id = order_id )이면 3행에 4711 B, 4712 빈칸, 4713 C라는 주석, 헤더 라인이 있는 옛 코드에서 t_status[]와 t_orders[]로 테이블 본문을 지정하는 줄에 번호 네 개가 표시되어 있다.
같은 lt_orders와 lt_status로 세 가지 쓰는 법과 헤더 라인 표기를 한 화면에 모은 예시. 각 문장은 첫 줄 주석의 같은 상태에서 따로 시작한다고 가정한다. ZDEMO_MOVE_CORR는 가상 프로그램이고 코드는 실행하지 않았으며, 주석의 결과는 문서의 규칙으로 따진 값이다.

위 화면의 번호를 코드 점검 순서로 읽으면 이렇습니다. 1번은 대상 테이블이 지워지고 원본 행 수만큼 다시 만들어지는 자리입니다. 2번은 KEEPING TARGET LINES가 행을 합치지 않고 덧붙이는 자리입니다. 3번은 주문번호로 짝을 찾아 상태만 채우는 조회 테이블 변형입니다. 4번은 헤더 라인이 있는 옛 코드에서 []로 테이블 본문을 지정한 줄입니다.

코드에서 확인할 것

내부 테이블끼리의 MOVE-CORRESPONDING을 만나면 세 가지를 봅니다. 대상 테이블에 이 문장 전에 넣어 둔 행이 있는가. 그 행의 값을 남기려는 의도였는가. 피연산자가 헤더 라인이 있는 테이블인가. 기존 행을 키로 보강하는 것이 목적이면 조회 테이블을 쓰는 CORRESPONDING이나 READ TABLE 루프로 바꾸고, 행을 덧붙이는 것이 목적일 때만 KEEPING TARGET LINES나 BASE를 씁니다.

하나만 기억한다면 이것입니다. 내부 테이블끼리의 MOVE-CORRESPONDING은 값을 채우는 문장이 아니라, 원본의 행 수대로 대상 테이블을 새로 만드는 문장입니다.

끝 · END OF NOTE목록으로
COMMENTS BOX

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

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

최신순

로그인 상태 확인 중…

댓글을 불러오는 중…