Programming/Oracle

or 연산자

초록깨비 2008. 12. 2. 13:26
728x90

 

논리합 연산자를 사용하는 것은 득보다는 실이 많은 것이 사실이다. 여기에서는 논리합 연산자를 사용할 수 밖에 없는 상황에서 약간의 아이디어를 통해 논리합 연산자를 사용하지 않고 또는 단순 논리합 연산자로 처리하는 방법에 대해서 알아본다.

 

(KEY WORD : 논리합 연산자, IN, OR )

 

어느 보험회사의 보험 인수실적을 관리하는 테이블에는 '개포준구분'이라는 컬럼이 있으며, 개별(10), 포괄(20), 준포괄(30) 등의 값을 가지고 있다. 사용자가 화면에서 선택한 구분값에 따라 '개별'일 경우와 '포괄' 또는 '준포괄'등을 포함한 '포괄외'의 두가지 경우로 조회하고자 한다. 여기서는 이 사례 설명의 목적상 성능 및 인덱스와 관련한 설명은 설명한다.

 

  • 다음의 SQL을 보자.

SELECT ………………………..
FROM TAB1 X, TAB2 Y, TAB3 Z
WHERE join_conditions
……………………..
  AND ((:
선택구분 = '1'
        AND
개포준구분 = '10'
        AND
인수일자
            BETWEEN TO_CHAR(TO_DATE(:
기준일,'YYYYMMDD') ?60, 'YYYYMMDD')
                AND :
기준일
        AND
종목코드 = :종목 )
  OR
       ( :
선택구분 = '2'
        AND
개포준구분 BETWEEN '20' AND '30'
        AND
인수일자
            BETWEEN TO_CHAR(TO_CHAR(:
기준일, 'YYYYMMDD') - 120,
                                                     'YYYYMMDD')
                AND :
기준일
        AND
종목코드 = :종목 )) ;

  • 위와 같은 방법을 사용하는 것은 3세대 언어를 사용해왔던 사용자의 입장에서 보면 너무나 당연한 표현 방법일 것이며, 이러한 형태로 작성한 것이 특별히 큰 문제를 일으킬 것으로 생각하지 않는 것이 어쩌면 더 보편적일지도 모른다. 그러나 사용자의 요구를 받아서 실제 프로그래밍은 데이터베이스가 작성해 주는 관계형 데이터베이스에서는 결코 작은 문제가 아니다.
     
  • 다음과 같은 SQL을 보자.

SELECT …………………………….
FROM TAB1 X, TAB2 Y, TAB3 Z
WHERE join_conditions
…………………..
  AND
개포준구분 IN (DECODE(:선택구분,'1','10','20'),
      DECODE(:
선택구분,'2','30') )
  AND
인수일자
      BETWEEN TO_CHAR(TO_DATE(:
기준일,'YYYYMMDD') ? 60 * :선택구분,
                                                         'YYYYMMDD')
          AND :
기준일
  AND
종목코드 = :종목 ;

  • SQL은 위의 SQL에 비해 훨씬 단순하고 명확해졌다. 옵티마이져 입장에서 보면 '종목코드' '=', '개포준구분' 'IN', 그리고 '인수일자' 'BETWEEN'을 사용했다는 것만 생각하면 된다. 비교되는 상수값이 제 아무리 복잡하게 얽혀 있더라도 실행계획을 수립할 때는 전혀 감안할 필요가 없다.
     
  • SQL '개포준구분' 'IN'으로 통합한 부분에서 두번째 DECODE를 사용한 구분은 ':선택구분' '1'인 경우 NULL이 된다. 하지만 IN을 사용한 경우에 비교값이 NULL인 경우는 전혀 액세스에 부담을 주지 않으므로 걱정할 필요가 없다.
     
  • 더 자세한 내용은 '대용량 데이터베이스 솔루션 II page 4-18'을 참조하기 바란다.

 

 

 

 

논리합 연산자를 사용하는 것은 득보다는 실이 많은 것이 사실이다. 여기에서는 논리합 연산자를 사용할 수 밖에 없는 상황에서 약간의 아이디어를 통해 논리합 연산자를 사용하지 않고 또는 단순 논리합 연산자로 처리하는 방법에 대해서 알아본다.

 

(KEY WORD : 논리합 연산자, IN, OR )

 

어느 회사의 자재관리의 출고를 관리하는 예이다.

 

  • 다음의 SQL을 보자.

SELECT CHULNO, CUSTNO, CHULDATE, UNCOST FROM CHULGOT WHERE (    :SW = 1        AND (STATUS LIKE '1%' OR STATUS LIKE '2%' ))   OR  (    :SW = 2        AND (STATUS LIKE '3%' )) ORDER BY STATUS ;

  • SQL STATUS 컬럼이 인덱스를 가지고 있더라도 '전체 테이블 스캔'으로 실행계획이 작성된다. 물론 데이터베이스 제품이다 버전에 따라 그렇지 않을 수도 있겠지만 대부분의 경우가 그렇게 된다. 그 이유는 단순하게 나열된 OR 연산자가 아니라, 위의 예처럼 OR 분기 내에 다시 OR가 분기된 복잡한 형태는 대부분 '전체 테이블 스캔' 방식으로 처리되기 때문이다
  • 더구나 사용된 조건을 보더라도 이 SQL의 처리범위는 좁지 않다는 것을 알 수 있다. 뿐만 아니라 'ORDER BY'가 사용되어 부분범위처리도 불가능해졌기 때문에 이대로는 도저히 수행속도를 향상시킬 방법이 없어 보인다. 하지만 다음과 같은 SQL로 바꾸어 보자.

SELECT CHULNO, CUSTNO, CHULDATE, UNCOST FROM CHULGOT WHERE STATUS LIKE DECODE(:SW,1,'2%')    OR STATUS LIKE DECODE(:SW,1,'1%','3%') ;

  • SQL은 이제 단순하게 나열된 OR 연산자로 바뀌었다. ORDER BY는 아예 없애버렸다. 그러나 결과는 동일하게 나타난다. 뿐만 아니라 부분범위로 처리되므로 수행속도는 많이 개선된다
  • 왜 이렇게 되는지에 대해서 살펴보자. 먼저 실행계획을 보자.

CONCATENATION  TABLE ACCESS BY ROWID CHULGOT    INDEX (RANGE SCAN) STATUS_INDEX  TABLE ACCESS BY ROWID CHULGOT    INDEX (RANGE SCAN) STATUS_INDEX

  • 이 실행계획은 우리가 원하는 결합처리 방법으로 수립되었다. 만약 :SW = 1 인 경우에는 어떤 SQL로 되겠는가? 다음과 같을 것이다.

WHERE STATUS LIKE '2%'   OR STATUS LIKE '1%'

  • 결합처리 실행계획의 원리에 따라서 '1%'를 처리하는 실행계획은 위의 실행계획의 아래부분이 처음 부분이 될 것이다. à 자연스럽게 정렬을 유도하였다. 이것은 :SW = 2 일 경우에도 마찬가지이다
  • 만약, STATUS NULL을 허용하여 실제로 NULL을 가진 데이터가 매우 많을 때를 걱정하는 사람이 있을 수도 있다. 하지만 NULL인 데이터를 찾기 위해서는 'IS NULL' 조건이 유일한 방법이라는 것을 알면 걱정할 필요가 없을 것이다
  • 더 자세한 내용은 '대용량 데이터베이스 솔루션 II page 4-21'을 참조하기 바란다

 

728x90