Post

Index 기본2

#naver-import

원문: https://blog.naver.com/qoxmfaktmxj/222885284658

인덱스 컬럼을 가공하면 인덱스를 정상적으로 사용(Range Scan)할 수 없다.

인덱스 탐색 과정은 수직, 수평적 탐색 2가지로 나뉘게 되는데

인덱스 컬럼을 가공했을 때 인덱스를 정상적으로 사용할 수 없는 이유는 인덱스 스캔 시작점을 찾을 수 없기 때문이다.

Index Range Scan에서 Range는 범위를 의미하는데, 일정 범위를 스캔하려면 시작점과 끝점이 있어야 한다

생년월일 2001년 1.1일부터 10.10일까지 검색

where 생년월일 between ‘20010101’ and ‘20011010’; 위는 시작 끝점을 알 수 있는데 between이라 그런거 아님

아래는 전체 인원 검색 후 찾아야 한다. 기준이 되는 부분이 없다. 생년월일이 5월인 사람 검색

where substr(생년월일,5,2) = ‘05’

아래도 마찬가지로 가공하지 않은 주문수량으로 인덱스를 만들었는데, 값이 NULL이면 0 으로 치환한 값을 기준으로 100보다 작은 레코드를 찾아 달라고 하면 전체 검색해야 한다. where nvl(주문수량,0) < 100

where 업체명 like ‘%제철%’ like로 중간값을 검색해도 마찬가지

OR조건도 수직적 탐색을 통해 전화번호가 01012345678이거나 고객명이 홍길동 인 어느 한 시작지점을 바로 찾을 수가 없다. where (전화번호 = :tel_no OR 고객명 = :c_nm)

OR EXPANSION

where (전화번호 = :tel_no OR 고객명 = :c_nm) 위 부분을 Index Range Scan 가능하도록 변경해보자

select * from 고객 where 고객명 = :c_nm union all select * from 고객 where 젆와번호 = :tel_no and (고객명 <> :c_nm or 고객명 is null)

혹은 힌트 주기

select /*+ use_concat */ * from 고객 where (전화번호 = :tel_no OR 고객명 = :c_nm)

where 전화번호 in (:tel_no1, :tel_no2)

IN 절은 어떨까?

OR조건의 다른 표현 방식일 뿐이다. 안된다.

union all로 진행하면 Index Range Scan 된다

select * from 고객 where 전화번호 = :tel_no1

union all

select * from 고객 where 전화번호 = :tel_no2

인덱스를 Range Scan 한다고 해서 무조건 성능이 좋은 것은 아니다.

TXA1234_IX02 인덱스 : 기준연도 + 과세구분코드 + 보고회차 + 실명확인번호

SELECT * FROM TXA1234 WHERE 기준연도 = :stdr_year AND substr(과세구분코드 , 1, 4) = :txtn_dcd AND 보고회차 = :rpt_tmrd AND 실명확인번호 = :rnm_cnfm_no

이 부분에서는 인덱스 선두 컬럼인 기준연도를 조건절에서 가공하지 않았기 때문에 RANGE SCAN 가능하다

위처럼 인덱스를 Range Scan 할 수 있다고 해서 무조건 성능이 좋아진다고 볼 수는 없는 것이

어차피 성능을 결정하는 것과 인덱스를 Range Scan하는 것 다 목적은 하나다

원하는 값을 얻기 위해서 얼마나 더 적게 Scan하느냐가 핵심 포인트다.

그런면에서 위 substr한 가공된 컬럼인 과세구분코드는 스캔 범위를 줄이는 데 전혀 역할을 하지 못한다.

마지막으로 인덱스를 Range Scan 할 수 있는 이유는 당연하게 데이터가 정렬되어 있기 때문이다.

따라서, 인덱스를 이용해 소트 연산을 생략할 수 있다.

PK가 A+ B + C 로 구성되어 있을 때

PK 인덱스를 where 절에 A B 잡아주면 C는 정렬이 이미 되어 있어서

ORDER BY C 를 해줄 필요 없다. 애초에 Optimizer에서 이미 정렬되어 있다고 판단해 제외한다

ALIAS를 준 테이블에 대해서 정렬할때에는 ALIAS를 붙여줘야 실행계획에 SORT ORDER BY 가 나타나지 않는다.

SELECT * FROM ( SELECT TO_CHAR(A.ORDER_NO, ‘FM000000’) AS ORDER_NO , A.ITEM_NO, A.ITEM_WON FROM ORDER A WHERE A.ORDER_DATE = : XX AND A.ORDER_NO > NVL(:NEXT_ORDER_NO,0) ORDER BY ORDER_NO ) WHERE ROWNUM <=30 여기서 SORT ORDER BY ~ 가 들어가게 되는데 ORDER BY 절에서 ORDER_NO 를 A.ORDER_NO로 변경해주면 튜닝이 된다. 위 ORDER_NO는 가공된 컬럼이기 때문

댓글