인덱스 튜닝의 두 가지 핵심요소
NL 조인이 대량 데이터 조일할 때 느린 이유도 랜덤 I/O때문이고, 이를 보완한 소트머지 조인과 해시 조인도 결국 랜덤 I/O를 극복하는 것을 목표로 개발된 것이다.
ROWID = 데이터 블록 주소 + 로우 번호
데이터 블록 주소 = 데이터 파일 번호 + 블록 번호
블록 번호 = 데이터 파일 내에서 부여한 상대적 순번
로우 번호 = 블록 내 순번
인덱스를 정상적으로 사용한다는 의미는 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 것을 의미한다. 즉, 리프 블록 일부만 스캔하는 Index Range Scan을 의미한다.
인덱스 컬럼을 가공했을 때 인덱스를 정상적으로 사용할 수 없는 이유는 인덱스 스캔 시작점을 찾을 수 없기 때문이다.
LIKE로 중간 값을 검색 할 때도 Index Range Scan을 사용할 수 없다. 'Word'로 시작하는 값은 특정 구간에 모여 있지만, 포함하는 값은 전체 구간에 있기 때문이다.
OR 조건 = IN () 으로 검색할 때도 Range Scan 방식을 사용할 수 없다. 때문에 UNION ALL을 사용해서 개별적으로 조회하면 Range Scan이 가능하다.
IN 조건절에 대해서는 SQL 옵티마이저가 IN-List Iterator 방식을 사용한다. IN-List 개수 만큼 Index Range Sacn을 반복한다.
Q&A
UNION ALL 방식으로 SELECT를 사용하는 방식을 사용하면 DISK I/O가 많아져서 오히려 안좋은게 아닌가??
잘된 예시
SELECT * FROM 주문 상품 WHERE 주문일자 = :ord_dt AND 상품번호 = :ord_num
잘못된 예시
SELECT * FROM 주문 상품 WHERE 주문일자 = :ord_dt AND 상품번호 LIKE '%PING%'
SELECT * FROM 주문 상품 WHERE 주문일자 = :ord_dt AND SUBSTR(상품번호, 1, 4) = 'PING';
SELECT * FROM 상태변경이력 WHERE 장비번호 = 'C' AND 변경일자 = '20220316'
으로 조회를 한다면 변경순번으로 정렬된다. -> ORDER BY 변경순번없이!!ORDER BY 변경일자 || 변경순번으로 컬럼을 가공한다면 소트 연산 생략을 할 수 없다.
SELECT문에서 가공한 컬럼을 ALIAS로 지정 후 ORDER BY에서 ALIAS로 사용하는 경우
SELECT *
FROM (
SELECT TO_CHAR(A.주문번호, 'FM000000') AS 주문번호, A.업체번호
FROM 주문 A
WEHRE A. 주문일자 = :dt
AND A. 주문번호 > NVL(:next_ord_no, 0)
ORDER BY 주문번호
)
WHERE ROWNUM <= 30
SELECT NVL(MAX(TO_NUMBER(변경순번)), 0)
FROM 상태변경이력
WHERE 장비번호 = 'C'
AND 변경일자 = '20180316'
SELECT * FROM 거래
WHERE 계좌번호 LIKE :acnt_no || '%'
AND 거래일자 between :trd_dt1 and :trd_dt2
인덱스가 ename, sal일 때
SELECT * FROM emp
WHERE sal > 9000
order by ename;
SELECT /*+ first_rows*/* FROM emp
WHERE sal > 9000
order by ename;
SELECT * FROM 사원 WHERE 연봉 between 2000 and 4000
Index Skip Scan
선두 컬럼에 대한 조건절은 있지만 중간 컬럼에 대한 조건절이 없는 경우에도 Skip Scan을 사용할 수 있다.
Distinct Value가 적은 두 개의 선두 컬럼이 모두 조건절에 없는 경우에도 유용하게 사용할 수 있다.
선두 컬럼이 부등호, BETWEEN, LIKE같은 범위 검색 조건일 때도 Index Skip Scan을 사용할 수 있다.
물리적으로 디스크에 저장된 순서대로 인덱스 리프 블록들을 읽어들인다.
디스크로부터 대량의 인덱스 블록을 읽어야 할 때 큰 효과를 발휘한다. 하지만 인덱스 리프 노드가 갖는 연결 리스트 구조를 무시하고 데이터를 읽어서 결과집합이 인덱스 키 순서대로 정렬되지 않는다.
쿼리에 사용한 컬럼이 모두 인덱스에 포함되어 있을 때만 사용할 수 있다.
인덱스가 파티션 되어 있지 않더라도 병렬 쿼리가 가능하다.
관련 힌트 index_ffs, no_index_ffs
Index Full Scan과 Index Fast Full Scan의 차이점