Post

(Oracle) 인덱스 힌트

쿼리 플랜에서 특정 INDEX 탄다고 나왔지만 실제로는 타지 않는 사례

[!warning] 쿼리 플랜 결과와 실제 옵티마이저 수행이 다른 경우가 생각보다 많기 때문에, 의도한 인덱스가 있다면 인덱스 힌트는 항상 써주는 것이 좋다.

쿼리 플랜에서 특정 INDEX를 탄다고 나와도, 실제로는 그 INDEX를 타지 않아 굉장히 오래 걸릴 수도 있다. 플랜은 그저 예상이다.

인덱스 정보

1
2
INK5_DR (CMPL_YMD ASC, RSLT_CD ASC, SETL_YN ASC)
INK1...10 총 10개 인덱스 존재함.

문제의 쿼리

1
2
3
4
SELECT *
FROM DR d
WHERE d.CMPL_YMD > '2023-04-24'
AND d.RSLT_CD = '99'

문제의 쿼리 플랜

분명 쿼리플랜에서는 INK5_DR 인덱스를 탄다고 알려주지만, 실제로 돌려보면 DBMS가 INK5_DR 인덱스를 태우지 않아 쿼리가 굉장히 오래걸린다.

아래처럼 직접 index hint 적어주면 제대로 인덱스를 탄다.

1
2
3
4
5
SELECT /*+ index(d INK5_DR) */
    *
FROM DR d
WHERE d.CMPL_YMD > '2023-04-24'
AND d.RSLT_CD = '99'

parallel_index() 쓰는 경우에도 별도로 index()를 같이 적어주어야 한다.

문제의 쿼리

1
2
3
4
5
6
SELECT /*+ parallel_index(d INK5_DR 4) */
    NVL(SUM(d.amt), 0),
    COUNT(*)
FROM DR d
WHERE {conditions...}
GROUP BY {conditions...}

문제 쿼리 실제 수행 결과

/*+ parallel_index(d INK5_DR 4) */ 했기 때문에 당연히 INK5_DR 탈거라고 생각했지만, 옵티마이저가 INK1_DR을 사용해 INDEX SKIP SCAN(참고) 해버렸다.

아래처럼 직접 index hint 적어주면 제대로 인덱스를 탄다.

1
SELECT /*+ index(d INK5_DR) parallel_index(d INK5_DR 4) */

[!warning] parallel_index 쓰는 경우에도 index 빼먹지 않도록 한다.
비슷하게 parallel 쓰는 경우에는 full 빼먹지 않도록 한다.

index hint 문법 관련

자주 하는 실수 - alias

1
2
3
if (alias를 지정하지 않는 경우) 테이블명으로 써도 인덱스힌트가 걸린다.
if (alias를 지정하는 경우) 테이블명이 아니라 alias를 써야만! 인덱스 힌트가 걸린다. 
    테이블명으로 걸면 적용되지 않는다.
  • 단일 테이블 쿼리가 확장되면서 join 테이블이 추가되고, 여기에 각각 alias를 붙여줄 때, 인덱스 힌트도 alias로 고쳐주어야 하는데 이를 누락하는 경우가 종종 있다.
  • 따라서 단일 테이블 쿼리여도, 테이블과 index hint 모두 alias를 써주는게 안전하다.
  • Oracle index hint syntax

SELECT와 오라클 힌트 앞에 다른 주석이 있어도 괜찮다.

The index hint is enclosed within a comment, so any other comments before or after the hint will be treated as regular comments and will not affect the hint itself.

1
SELECT /* ... */ /*+ hint */
This post is licensed under CC BY 4.0 by the author.