SAP BOX

'26/10/17 UPDATE

ABAP / FIELD NOTES

범위 테이블이 비어 있는데 WHERE IN은 왜 모든 행을 읽을까

범위 테이블에 줄이 하나도 없으면 WHERE ... IN은 항상 참이 되어, 프로그램이 채운 대상 목록이 0건인 날 모든 행을 읽습니다. I·E 줄이 조건으로 바뀌는 규칙, 선택 화면에서는 이것이 왜 편리한지, SELECT 앞의 확인과 SELECT-OPTIONS의 s_kunnr[]·OBLIGATORY를 가상 예로 봅니다.

매일 밤 도는 가상의 배치 프로그램이 있습니다. 먼저 그날 결제를 보류할 고객 번호를 읽어 범위 테이블 lr_kunnr에 담고, 이어서 WHERE kunnr IN @lr_kunnr로 그 고객들의 미결 항목만 읽어 보류 처리합니다. 평소에는 두세 명이던 대상이 어느 날 한 명도 없었습니다. 그런데 그날 프로그램은 아무것도 하지 않은 것이 아니라 모든 고객의 미결 항목을 읽었습니다. 이 글을 읽고 나면 빈 범위 테이블이 왜 '조건 없음'이 되는지, 같은 규칙이 선택 화면의 SELECT-OPTIONS에서는 왜 편리한 동작인지, 그리고 프로그램이 채우는 범위 테이블을 어디서 어떻게 막아야 하는지 고를 수 있습니다.

예시의 Z 테이블 zdemo_blocklist, zdemo_open_items, 프로그램 ZDEMO_RANGE_EMPTY, 고객 번호 0000100001 등은 모두 지어낸 것이고, 코드는 실제 시스템에서 실행하지 않았습니다. 글의 결과는 ABAP 키워드 문서의 ABAP SQL의 IN range_tab, ABAP 비교식의 IN, SELECT-OPTIONS 페이지(7.58판, 앞의 둘은 7.50판과 대조)의 규칙을 적용해 따진 값이며, 문서는 영어입니다.

범위 테이블이 비면 IN은 항상 참이다

범위 테이블은 SIGN, OPTION, LOW, HIGH 네 열로 조건을 줄 단위로 적는 내부 테이블입니다. IN은 이 줄들을 비교식으로 바꿔 하나의 조건으로 묶습니다. 그리고 문서는 범위 테이블이 초기값, 곧 줄이 하나도 없으면 IN range_tab은 항상 참이라고 적습니다. ABAP SQL 페이지는 한 걸음 더 나아가, 이 경우 데이터베이스에 아무 조건도 넘기지 않으며, IN 말고 다른 조건이 없으면 데이터 소스의 모든 행이 선택된다고 설명합니다.

예시로 돌아가면, 첫 SELECT가 0건을 돌려주는 순간 lr_kunnr는 비고, 둘째 SELECT의 WHERE kunnr IN @lr_kunnr는 사실상 사라집니다. 결과는 오류도 경고도 아닌, 정상 종료된 '전체 조회'입니다. 같은 문장이 7.50판에서는 'selection table'이라는 이름으로 적혀 있을 뿐 내용은 같습니다. 여러 릴리스에 걸쳐 바뀌지 않은 규칙이라는 뜻입니다.

SAP 노트

같은 SELECT, 범위 테이블 상태는 셋

가상 예: SELECT ... FROM zdemo_open_items WHERE kunnr IN @lr_kunnr

  • I 줄 두 개

    kunnr = '0000100001' OR kunnr = '0000100007'

    • I EQ 0000100001
    • I EQ 0000100007
    • 두 고객의 미결 항목만 읽는다
  • 줄이 하나도 없음

    lines( lr_kunnr ) = 0

    • 보류 대상 SELECT가 0건이었다
    • IN 조건은 항상 참
    • DB에는 이 조건이 가지 않는다
  • E 줄 하나만

    NOT ( kunnr = '0000100003' )

    • E EQ 0000100003
    • I 줄이 없으면 E 조건의 부정만 남는다
    • 그 고객을 뺀 전부를 읽는다

테이블·필드·고객번호는 가상입니다. 결과는 ABAP 키워드 문서(7.58·7.50)의 IN 규칙으로 따진 값이며 실행 결과가 아닙니다.

범위 테이블에 I 줄이 있으면 그 값만 읽지만, 줄이 하나도 없으면 IN 조건은 항상 참이 되어 모든 행을 읽습니다. E 줄만 있을 때도 결과는 '거의 전부'입니다.

그림의 셋째 경우도 같은 계열의 함정입니다. 범위 테이블에 제외(E) 줄만 있으면 문서의 규칙상 포함(I) 쪽 조건이 없으므로, 제외 조건을 부정한 식만 남습니다. 한 고객을 빼려고 넣은 줄이 '그 고객을 뺀 모든 고객'을 읽게 만드는 것입니다.

줄이 있을 때 IN이 조건을 만드는 방법

문서가 설명하는 조합 순서는 이렇습니다. SIGN이 I인 줄들의 비교식은 OR로 묶입니다. E인 줄들도 OR로 묶은 뒤 NOT으로 부정합니다. 두 종류가 다 있으면 둘을 AND로 잇습니다. 포함 집합에서 제외 집합을 뺀 것이 결과라고 생각하면 쉽습니다. OPTION이 EQ, NE, GE 같은 값이면 LOW와의 비교가 되고, BT와 NB는 LOW와 HIGH 사이의 구간 조건이 됩니다.

ABAP SQL에서만 달라지는 점도 있습니다. CP와 NP 줄은 LIKE 조건으로 바뀌는데, 이 LIKE는 대소문자를 가립니다. ABAP 안의 CP 비교가 대소문자를 가리지 않는 것과 반대입니다. 또 범위 테이블의 값은 SQL 문의 입력값으로 데이터베이스에 넘어가므로, 문서에 따르면 DB 시스템마다 다르지만 보통 2000~10000개를 넘으면 CX_SY_OPEN_SQL_DB 예외가 납니다. 범위 테이블에 잘못된 SIGN이나 OPTION 값이 들어 있으면 잡을 수 없는 예외가 나고, LOW와 HIGH의 타입이 비교 대상과 무손실 대입 규칙에 맞지 않으면 7.40 SP08부터 엄격 모드 문법 검사가 이를 확인합니다.

SAP 노트

IN 범위 테이블을 조건으로 바꾸는 규칙

줄마다 SIGN·OPTION·LOW·HIGH를 비교식 하나로 만든 뒤 묶는다

  • I 줄과 E 줄

    ( I1 OR I2 ) AND NOT ( E1 OR E2 )

    • I 줄끼리 OR로 묶는다
    • E 줄끼리 OR로 묶고 NOT
    • 둘 다 있으면 AND로 잇는다
  • 빈 범위 테이블

    SELECT ... FROM zdemo_open_items

    • ABAP의 IN: 항상 참
    • ABAP SQL의 IN: 항상 참
    • 다른 조건이 없으면 모든 행
  • ABAP SQL에서만 다른 점

    CX_SY_OPEN_SQL_DB

    • CP·NP는 LIKE로 바뀌어 대소문자 구분
    • 값은 SQL 입력값으로 넘어간다
    • 보통 2000~10000개를 넘으면 예외

ABAP 키워드 문서 rel_exp - IN, sql_cond - IN range_tab(7.58), 같은 페이지의 7.50판 기준입니다. 입력값 상한은 DB 시스템에 따라 다릅니다.

I 줄은 OR로 모이고, E 줄은 OR로 모은 뒤 부정합니다. 줄이 하나도 없으면 ABAP에서도 ABAP SQL에서도 IN은 항상 참이고, ABAP SQL에서는 이 조건이 아예 DB로 넘어가지 않습니다.

비슷해 보이는 FOR ALL ENTRIES에 빈 테이블을 넘기는 경우와는 범위가 다릅니다. 그쪽은 WHERE 조건 전체가 무시되지만, 빈 범위 테이블은 그 IN 조건 하나만 참이 되고 날짜 같은 다른 조건은 그대로 남습니다. 결과가 '전체'에 가까워지는 원인이 서로 다르니 대책도 따로 생각해야 합니다.

선택 화면에서는 편리하고, 프로그램 안에서는 위험하다

이 규칙이 일부러 그렇게 정해진 이유는 선택 화면을 보면 보입니다. SELECT-OPTIONS s_kunnr FOR ...는 범위 테이블과 같은 구조의 선택 테이블을 만들고, 사용자가 입력한 값을 그 줄로 옮깁니다. 사용자가 고객 번호 칸을 비워 두면 선택 테이블도 비고, WHERE kunnr IN @s_kunnr는 고객 번호로는 거르지 않습니다. '입력하지 않은 조건은 제한하지 않는다'는, 보고서 화면에서 기대하는 바로 그 동작입니다.

문제는 같은 문법을 프로그램이 직접 채우는 목록에 쓸 때 생깁니다. 이때 빈 목록의 뜻은 '대상이 없음'인데, IN은 그것을 '제한 없음'으로 읽습니다. 그러니 해결의 첫 단계는 코드가 아니라 뜻을 정하는 일입니다. 범위 테이블마다 비어 있을 때 '전부'인지 '아무것도 아님'인지 정하고, 뒤의 경우라면 SELECT 앞에서 IF lr_kunnr IS INITIAL. RETURN. ENDIF.처럼 직접 끊어야 합니다. 보류·삭제·전기처럼 결과에 따라 무언가를 바꾸는 처리라면 더욱 그렇습니다.

SAP 노트

빈 범위 테이블을 막는 순서

같은 가상 프로그램에서 SELECT 앞에 둘 것

  1. 비었을 때 뜻 정하기

    lr_kunnr ≠ s_kunnr

    • 선택 화면: 비면 '제한 없음'
    • 프로그램이 채운 목록: 비면 '대상 없음'
    • 둘을 같은 코드로 다루지 않는다
  2. SELECT 앞에서 확인

    IF lr_kunnr IS INITIAL. RETURN. ENDIF.

    • 범위 테이블이 비면 읽지 않고 끝낸다
    • SELECT-OPTIONS는 헤더 라인이 있다
    • 본문은 s_kunnr[]로 확인한다
  3. 화면에서 막기

    SELECT-OPTIONS s_kunnr FOR lv_kunnr OBLIGATORY.

    • OBLIGATORY: 첫 입력 필드 필수
    • 비어 있으면 F8로 실행할 수 없다
    • 필수는 첫 입력 필드 하나뿐

프로그램과 변수는 가상이고 코드는 실행하지 않았습니다. SELECT-OPTIONS·OBLIGATORY·헤더 라인 규칙은 ABAP 키워드 문서 7.58판 기준입니다.

먼저 '비어 있음'이 그 자리에서 무슨 뜻인지 정하고, 프로그램이 채운 범위 테이블은 SELECT 앞에서 비었는지 확인해 끝냅니다. 선택 화면이라면 OBLIGATORY로 첫 입력을 요구할 수 있습니다.

선택 테이블을 확인할 때 붙이는 []

선택 화면의 조건도 비어 있으면 안 되는 보고서라면 두 가지 방법이 있습니다. 하나는 선언에 OBLIGATORY를 붙이는 것입니다. 그러면 첫 입력 필드가 필수가 되어, 비어 있는 동안에는 실행(F8)으로 화면을 넘어갈 수 없고 뒤로·종료·취소만 할 수 있습니다. 필수가 되는 것은 첫 입력 필드라는 점은 기억해 둘 만합니다.

다른 하나는 프로그램에서 직접 확인하는 것인데, 여기에 작은 함정이 있습니다. SELECT-OPTIONS가 만드는 선택 테이블은 헤더 라인이 있는 표준 테이블입니다. 헤더 라인은 테이블과 이름이 같은 작업 영역이어서, 문서에 따르면 대부분의 피연산자 위치에서 이름만 쓰면 테이블 본문이 아니라 헤더 라인을 뜻합니다. 그래서 IF s_kunnr IS INITIAL은 선택 테이블에 줄이 있는지가 아니라 헤더 라인 한 줄이 비었는지를 봅니다. 본문을 확인하려면 s_kunnr[] IS INITIAL처럼 대괄호를 붙입니다. 문서는 헤더 라인을 프로그램에서 읽지도 바꾸지도 말라고 권합니다. 반면 TYPE RANGE OF로 선언한 lr_kunnr에는 헤더 라인이 없으므로 lr_kunnr IS INITIAL이 곧 본문 확인입니다.

아래 편집기 예시는 이 순서를 한 화면에 모은 것입니다. ①은 보류 대상을 범위 테이블로 모으는 SELECT, ②는 비었을 때 끝내는 세 줄, ③은 IN으로 미결 항목을 읽는 줄, ④는 선택 테이블 본문을 확인하는 s_kunnr[]입니다. 기존 프로그램에서 범위 테이블을 SELECT나 루프로 채운 뒤 바로 IN에 넘기는 곳이 있다면, ②에 해당하는 줄이 있는지부터 확인해 보세요.

SAP GUI ABAP 편집기 화면에 번호 네 개가 있다. 1은 zdemo_blocklist에서 'I', 'EQ', kunnr를 골라 lr_kunnr를 채우는 SELECT, 2는 IF lr_kunnr IS INITIAL. RETURN.으로 비면 끝내는 줄, 3은 zdemo_open_items를 WHERE kunnr IN @lr_kunnr로 읽는 줄, 4는 IF s_kunnr[] IS INITIAL과 헤더 라인이 아니라 선택 테이블 본문이라는 주석이다.
범위 테이블을 채우고, 비었는지 확인한 뒤, IN으로 읽는 순서를 한 화면에 모은 예시. ZDEMO_RANGE_EMPTY와 Z 테이블, 필드는 가상이고 코드는 실행하지 않았으며, 주석의 결과는 문서의 규칙으로 따진 값이다.

기억할 것은 하나입니다. IN에게 빈 범위 테이블은 '아무것도 아님'이 아니라 '제한 없음'입니다. 프로그램이 채운 범위 테이블이라면, 비었을 때 무엇을 할지를 SELECT 바로 앞에 코드로 적어 두세요.

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

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

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

최신순

로그인 상태 확인 중…

댓글을 불러오는 중…