복합 인덱스 : 가장 선택적인 열이 먼저입니까?


17

나는 읽고 composite indexes있었고 주문에 대해 약간 혼란 스럽습니다. 이 문서는 (반쯤 내려 가면 안됩니다)

일반적으로 색인에서 가장 자주 사용될 것으로 예상되는 열을 넣어야합니다.

그러나 그것이 말한 직후

가장 선택적인 열을 먼저 놓는 복합 색인을 작성하십시오. 즉, 값이 가장 많은 열입니다.

오라클은 또한 다른 말로 여기 에 말합니다

모든 키가 WHERE 절에서 동일하게 자주 사용되는 경우 CREATE INDEX 문에서 이러한 키를 가장 선택적에서 가장 덜 선택적인 순서로 정렬하면 쿼리 성능이 가장 향상됩니다.

그러나 나는 다르게 말하는 SO 답변 을 발견했습니다 . 그것은 말한다

가장 열이 가장 적은 열을 먼저 선택하고 가장 열이 가장 높은 열을 마지막으로 정렬하십시오. 컬럼이있는 타이 리드의 경우 자체적으로 사용될 가능성이 높습니다.

내가 참조 한 첫 번째 문서는 가장 자주 사용되는 문서를 먼저 사용해야하지만 SO 답변은 넥타이 끊기에만 사용해야한다고 말합니다. 그런 다음 순서도 다릅니다.

문서 는 또한 이야기 skip scanning하고 말합니다

스킵 스캔은 복합 인덱스의 선행 열에 고유 값이 거의없고 인덱스의 비 리딩 키에 많은 고유 값이있는 경우 유리합니다.

다른 기사 는 말합니다

접두사 열은 가장 차별화되고 쿼리에서 가장 널리 사용되어야합니다.

나는 가장 차별적 인 것이 가장 독창적이라고 생각합니다.

이 모든 연구는 여전히 같은 질문으로 이어집니다. 가장 선택적인 열이 첫 번째 또는 마지막이어야합니까? 타이 브레이크에서 첫 번째 열이 가장 많이 사용되고 가장 선택적이어야합니까?

이 기사들은 서로 모순되는 것처럼 보이지만 몇 가지 예를 제시합니다. 내가 모은 것에서, 당신이 예상한다면 주문 least selective column에서 첫 번째 가되는 것이 더 효율적인 것 같습니다 Index Skip Scans. 그러나 그것이 올바른지 확실하지 않습니다.


답변:


8

AskTom에서

(9i에는 새로운 "인덱스 건너 뛰기 스캔"이 있습니다.이 정보를 읽으려면 검색하십시오.이 경우 두 경우 모두 인덱스 (a, b) OR (b, a)가 유용합니다!)

따라서 인덱스의 열 순서는 쿼리 작성 방법에 따라 다릅니다. 가능한 많은 쿼리에 대해 인덱스를 사용할 수 있기를 원합니다 (모든 인덱스 수를 줄이십시오). 그러면 열의 순서가 결정됩니다. 다른 것은 없습니다 (a 또는 b의 선택성은 전혀 계산되지 않습니다).

복합 식별에서 열을 가장 작은 구별 (더 작은 값)에서 가장 구별되는 (더 뚜렷한 값) 순서로 배열하기위한 인수 중 하나는 인덱스 키 압축입니다.

SQL> create table t as select * from all_objects;

Table created.

SQL> create index t_idx_1 on t(owner,object_type,object_name);

Index created.

SQL> create index t_idx_2 on t(object_name,object_type,owner);

Index created.

SQL> select count(distinct owner), count(distinct object_type), count(distinct object_name ), count(*)  from t;

COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_TYPE) COUNT(DISTINCTOBJECT_NAME)      COUNT(*)
-------------------- -------------------------- --------------------------      ----------
                 30                         45                       52205      89807

SQL> analyze index t_idx_1 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave from index_stats;

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          2           28

SQL> analyze index t_idx_2 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave  from index_stats; 

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          1           14

인덱스 통계에 따르면 첫 번째 인덱스는 압축률이 높습니다.

다른 하나는 쿼리에서 인덱스가 사용되는 방법입니다. 검색어가 주로을 사용하는 col1경우

예를 들어 다음과 같은 검색어가있는 경우

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col1 = :a;

    그런 index(col1,col2)다음 더 잘 수행합니다.

    검색어가 주로을 사용하는 col2경우

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col2 = :b;

    그런 index(col2,col1)다음 더 잘 수행합니다. 모든 쿼리가 항상 두 열을 모두 지정하면 복합 인덱스에서 어떤 열이 먼저 나오는지는 중요하지 않습니다.

    결론적으로 복합 인덱스의 열 순서에서 고려해야 할 주요 사항은 인덱스 키 압축과 쿼리에서이 인덱스를 사용하는 방법입니다.

    참고 문헌 :

  • 인덱스의 열 순서
  • 낮은 카디널리티 선행 컬럼을 인덱스 (오른쪽)에 두는 것이 덜 효율적입니까?
  • 색인 건너 뛰기 스캔 – 색인 열 순서가 더 중요합니까? (경고 표시)


  • 3

    가장 선택적인 첫 번째는이 열이 실제 WHERE 절에있는 경우에만 유용합니다.

    SELECT가 더 큰 그룹에 의해 (선택적이지 않은) 다음에 색인화되지 않은 다른 값에 의해 선택 될 때, 선택 컬럼이 적은 인덱스가 여전히 유용 할 수 있습니다 (다른 컬럼을 작성하지 않는 이유가있는 경우).

    ADDRESS 테이블이 있으면

    COUNTRY CITY STREET, 다른 것 ...

    STREET, CITY, COUNTRY를 색인하면 거리 이름으로 가장 빠른 검색어가 생성됩니다. 그러나 도시의 모든 거리를 쿼리하면 인덱스가 쓸모없고 쿼리가 전체 테이블 스캔을 수행 할 수 있습니다.

    COUNTRY, CITY, STREET 인덱싱은 개별 거리에서 약간 느려질 수 있지만 다른 쿼리에는 국가 및 / 또는 도시별로 선택하여 인덱스를 사용할 수 있습니다.


    3

    인덱스 열 순서를 선택할 때 가장 중요한 관심사는 다음과 같습니다.

    쿼리에서이 열에 대해 (동일) 조건자가 있습니까?

    where 절에 열이 나타나지 않으면 인덱싱 할 가치가 없습니다 (1)

    자, 각 열에 대한 테이블과 쿼리가 있습니다. 때때로 하나 이상.

    색인을 생성 할 대상을 어떻게 결정합니까?

    예를 봅시다. 다음은 세 개의 열이있는 테이블입니다. 하나는 10 개의 값을 보유하고 다른 하나는 마지막 10,000을 보유합니다.

    create table t(
      few_vals  varchar2(10),
      many_vals varchar2(10),
      lots_vals varchar2(10)
    );
    
    insert into t 
    with rws as (
      select lpad(mod(rownum, 10), 10, '0'), 
             lpad(mod(rownum, 1000), 10, '0'), 
             lpad(rownum, 10, '0') 
      from dual connect by level <= 10000
    )
      select * from rws;
    
    commit;
    
    select count(distinct few_vals),
           count(distinct many_vals) ,
           count(distinct lots_vals) 
    from   t;
    
    COUNT(DISTINCTFEW_VALS)  COUNT(DISTINCTMANY_VALS)  COUNT(DISTINCTLOTS_VALS)  
    10                       1,000                     10,000     

    이들은 0으로 채워진 숫자입니다. 이렇게하면 나중에 압축에 대한 정보를 얻을 수 있습니다.

    따라서 세 가지 일반적인 쿼리가 있습니다.

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';

    당신은 무엇을 색인합니까?

    few_vals에 대한 인덱스는 전체 테이블 스캔보다 약간 우수합니다.

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------
    
    select /*+ index (t (few_vals)) */
           count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      58 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      58 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   3 |    HASH GROUP BY                       |          |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   5 |      INDEX RANGE SCAN                  | FEW      |      1 |   1000 |   1000 |00:00:00.01 |       5 |  
    -------------------------------------------------------------------------------------------------------------

    따라서 자체적으로 인덱싱 할 가치가 없을 것입니다. lots_vals의 쿼리는 몇 개의 행을 반환합니다 (이 경우 1 만). 따라서 이것은 인덱싱 할 가치가 있습니다.

    그러나 두 열에 대한 쿼리는 어떻습니까?

    색인을 작성해야합니까?

    ( few_vals, lots_vals )

    또는

    ( lots_vals, few_vals )

    트릭 질문!

    대답은 없습니다.

    물론 few_vals는 긴 문자열입니다. 따라서 압축을 잘 풀 수 있습니다. 그리고 lots_vals에 술어 만있는 (few_vals, lots_vals)를 사용하여 쿼리에 대한 인덱스 스킵 스캔을 얻을 수 있습니다. 그러나 전체 스캔보다 현저하게 성능이 우수하지만 여기에 없습니다.

    create index few_lots on t(few_vals, lots_vals);
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |      1 |      1 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------  
    
    select /*+ index_ss (t few_lots) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      13 |     11 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   5 |      INDEX SKIP SCAN                   | FEW_LOTS |      1 |     40 |      1 |00:00:00.01 |      12 |     11 |  
    ----------------------------------------------------------------------------------------------------------------------

    도박을 좋아합니까? (2)

    따라서 여전히 주요 열로 lots_vals가있는 인덱스가 필요합니다. 그리고이 경우 적어도 복합 지수 (몇, 로트)는 하나의 작업과 동일한 양의 작업을 수행합니다 (많은)

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_LOTS |      1 |      1 |      1 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    create index lots on t(lots_vals);
    
    select /*+ index (t (lots_vals)) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | LOTS     |      1 |      1 |      1 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    복합 인덱스가 1-2 IO를 절약하는 경우가 있습니다. 그러나이 절약을 위해 두 가지 색인을 가질 가치가 있습니까?

    복합 지수에는 또 다른 문제가 있습니다. LOTS_VALS를 포함하여 세 개의 인덱스에 대한 클러스터링 요소를 비교하십시오.

    create index lots on t(lots_vals);
    create index lots_few on t(lots_vals, few_vals);
    create index few_lots on t(few_vals, lots_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor
    from   user_indexes
    where  table_name = 'T';
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_LOTS    47           10,000         530                
    LOTS_FEW    47           10,000         53                 
    LOTS        31           10,000         53                 
    FEW         31           10             530    

    few_lots의 군집 계수는 lot 및 lots_few보다 10 배 높습니다 ! 그리고 이것은 완벽한 클러스터링을 갖춘 데모 테이블에 있습니다. 실제 데이터베이스에서는 효과가 더 나빠질 수 있습니다.

    그래서 뭐가 그렇게 나쁜가요?

    클러스터링 팩터는 인덱스의 "매력적인"정도를 결정하는 주요 동인 중 하나입니다. 높을수록 최적화 프로그램이 선택할 가능성이 줄어 듭니다. 특히 lots_vals가 실제로 고유하지는 않지만 일반적으로 값당 행이 거의 없습니다. 운이 좋지 않으면 옵티마이 저가 전체 스캔이 더 저렴하다고 생각하게 할 수 있습니다 ...

    좋습니다. 따라서 few_vals 및 lots_vals가있는 복합 인덱스에는 대소 문자 만 이점이 있습니다.

    few_vals 및 many_vals를 필터링하는 쿼리는 어떻습니까?

    단일 열 인덱스는 작은 이점 만 제공합니다. 그러나 결합하면 값이 거의 반환되지 않습니다. 따라서 복합 인덱스는 좋은 생각입니다. 그러나 어느 방향으로?

    몇 개를 먼저 배치하지 않으면 선행 열을 압축하면 크기가 작아집니다.

    create index few_many on t(many_vals, few_vals);
    create index many_few on t(few_vals, many_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    47           1,000          10,000             
    MANY_FEW    47           1,000          10,000   
    
    alter index few_many rebuild compress 1;
    alter index many_few rebuild compress 1;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    MANY_FEW    31           1,000          10,000             
    FEW_MANY    34           1,000          10,000      

    선행 열에서 다른 값이 적을수록 더 잘 압축됩니다. 따라서이 인덱스를 읽는 작업이 거의 없습니다. 그러나 약간만. 그리고 둘 다 이미 원본보다 작은 덩어리입니다 (25 % 크기 감소).

    더 나아가 전체 인덱스를 압축 할 수 있습니다!

    alter index few_many rebuild compress 2;
    alter index many_few rebuild compress 2;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    20           1,000          10,000             
    MANY_FEW    20           1,000          10,000   

    이제 두 인덱스가 같은 크기로 돌아 왔습니다. 이것은 소수와 많은 관계가 있다는 사실을 이용합니다. 다시 한 번 현실 세계에서 이런 종류의 혜택을 볼 수 없을 것입니다.

    지금까지는 평등 검사에 대해서만 이야기했습니다. 복합 인덱스를 사용하면 열 중 하나에 대해 불평등이 발생합니다. 예 : "지난 N 일 동안 고객에 대한 주문 / 배송 / 인보이스 받기"와 같은 쿼리

    이러한 종류의 쿼리가있는 경우 인덱스의 첫 번째 열에 대해 동등성을 원합니다.

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals < '0000000002'
    and    many_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   3 |    HASH GROUP BY                       |          |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_MANY |      1 |     10 |     10 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001'
    and    many_vals < '0000000002';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | MANY_FEW |      1 |      1 |     10 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    반대 인덱스를 사용하고 있습니다.

    TL; DR

    • 동등 조건을 가진 열이 색인에서 먼저 가야합니다.
    • 쿼리에 동일한 열을 가진 여러 열이있는 경우 가장 적은 값을 가진 열을 먼저 배치하면 압축 이점이 가장 좋습니다
    • 인덱스 스킵 스캔이 가능하지만, 이것은 가까운 장래에 실행 가능한 옵션으로 유지 될 것이라는 확신이 필요합니다
    • 거의 고유 한 열을 포함한 복합 인덱스는 최소한의 이점을 제공합니다. 1-2 IO를 저장해야합니다

    1 : 쿼리의 모든 열이 인덱스에 있음을 의미하는 경우 인덱스에 열을 포함시키는 것이 좋습니다. 이렇게하면 인덱스 만 검색 할 수 있으므로 테이블에 액세스 할 필요가 없습니다.

    2 : 진단 및 조정에 대한 라이센스가있는 경우 SQL 계획 관리를 사용하여 계획을 건너 뛰도록 강제로 계획 할 수 있습니다.

    ADDEDNDA

    추신-당신이 인용 한 문서는 9i에서 온 것입니다. 그 말은 오래되었습니다. 나는 더 최근의 것을 고수했다


    쿼리가 select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )실제로 일반적인가요? Oracle은 구문을 허용 select count (distinct few_vals, many_vals, lots_vals )하지 않습니다. 문자열 연결을 수행하지 않고 열이 텍스트 유형일 필요가 없으며 문자가없는 것에 의존하지 :않습니까?
    ypercubeᵀᴹ

    @ ypercubeᵀᴹ 당신은 count ( distinct x, y, z )오라클에서 할 수 없습니다 . 따라서 별도의 하위 쿼리를 수행하고 위와 같은 결과 또는 연결을 계산해야합니다. 방금 인덱스 액세스 만하지 않고 테이블 액세스를 강제로 수행하고 결과에 한 행만
    Chris Saxon

    1

    복합 인덱스가 열의 선택성 외에 무엇으로 시작하고 포함해야하는지에 대한 최종 결정에 기여하는 쿼리 요소가 더 많습니다.

    예를 들면 다음과 같습니다.

    1. 사용중인 쿼리 연산자 유형 : 쿼리에
      ">,> =, <, <="
    2. 쿼리 결과로 예상되는 실제 행 수 : 쿼리 결과가 테이블의 대부분의 행이됩니까?
    3. Where 절 동안 테이블 열에서 함수를 사용하고 있습니까? 쿼리에 WHERE 조건에서 사용중인 열에 사용 된 UPPER, LOWER, TRIM, SUBSTRING 함수가있는 경우.

    아직 대화를 유지하려면 아래 답변이 다음 상황에 적용됩니다.

    1. "주어진 테이블에 대한 90 % 유형의 쿼리에 연산자 ="가있는 WHERE 절이 있습니다.
    2. "대부분의 쿼리는 결과적으로 테이블의 총 행의 10 %를 반환합니다"
    3. "WHERE 절의 테이블 컬럼에서 어떤 종류의 함수도 사용되지 않습니다"
    4. "WHERE 절의 대부분의 시간 열은 대부분 숫자,
      문자열 유형입니다. "

    내 경험상 DBA가 염두에 두어야 할 것은 둘 다이다.

    단 하나의 규칙 만 적용되는 것을 상상해 봅시다.

    1) 가장 선택적 열을 먼저 사용하여 인덱스를 생성하지만 해당 열은 실제로 테이블 엔진의 대부분의 쿼리에서 사용되지 않지만 db 엔진에는 사용되지 않습니다.

    2) 쿼리에서 가장 널리 사용되는 열이 인덱스에서 첫 번째 인 인덱스를 생성하지만 열의 선택성이 낮 으면 쿼리 성능이 좋지 않습니다.

    테이블 쿼리의 90 %에서 주로 사용되는 열을 나열합니다. 그런 다음 카디널리티가 가장 낮은 순서 대로만 카디널리티를 최소화하십시오.

    읽기 쿼리 성능을 향상시키기 위해 인덱스를 사용하며 워크 플로 (읽기 쿼리 유형) 만 인덱스 생성을 유도해야합니다. 실제로 데이터가 증가함에 따라 (수십억 행) 압축 된 인덱스는 스토리지를 절약 할 수 있지만 읽기 쿼리 성능을 저하시킬 수 있습니다.


    1

    이론적으로 가장 선택적인 열은 가장 빠른 검색을 제공합니다. 그러나 직장에서 가장 선택적인 부분이 가장 먼저 3 부분으로 된 복합 지수가있는 상황에 처했습니다. (날짜, 저자, 출판 회사는 테이블 순서대로 게시물을 표시합니다) 3 부분을 모두 사용하는 쿼리가 있습니다. Mysql은 기본적으로 내 쿼리에 존재하지만 회사와 날짜를 포함하는 복합 인덱스를 건너 뛰는 저자 onlny 인덱스를 사용합니다. 복합 색인을 사용하기 위해 강제 색인을 사용했으며 쿼리가 실제로 느리게 실행되었습니다. 왜 그런 일이 일어 났습니까? 나는 당신에게 말할 것이다 :

    날짜에서 범위를 선택하고 있었으므로 날짜가 매우 선택적이지만 범위 스캔에 사용한다는 사실은 (범위가 비교적 짧고 6 년 동안 6 개월 동안 6 개월이더라도) 복합 재료에 유해한 결과를 가져 왔습니다 MySQL. 특정 경우에 복합을 사용하기 위해 mysql은 새해 이후 작성된 모든 기사를 가져 와서 저자가 누구인지를 알아야하며 저자가 다른 기사와 비교하여 많은 기사를 쓰지 않았다면 mysql은 해당 저자를 찾는 것을 선호했습니다. .

    또 다른 경우에는 쿼리가 복합에서 훨씬 더 빨리 실행 된 경우가 있는데, 그 이유는 저자가 대인기였으며 대부분의 레코드를 날짜별로 정렬하여 의미가있는 경우였습니다. 그러나 mysql 은이 경우를 자동으로 감지하지 못했습니다. 인덱스를 강제해야했습니다. 그래서 다양합니다. 범위 스캔은 선택 열을 쓸모 없게 만들 수 있습니다. 데이터 분포는 열이 다른 레코드에 대해 더 선택적인 경우를 만들 수 있습니다 ...

    내가 다르게 할 것은 날짜를 오른쪽으로 옮기는 것입니다 (이론에서는 가장 선택적인 것입니다). 지금 범위 스캔을 수행 할 것이므로 차이가 있습니다.


    1
    쿼리가 같은 것을 가지고 있다면 WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)다음의 인덱스 (author, publishing_company, date)나에는 (publishing_company, author, date)더 좋을 것이다 및 사용되는 -을 시작하지 않고.
    ypercubeᵀᴹ

    -2

    상황에 따라 다른 경우. 당신의 목표를 알고; 그런 다음 색인을 작성하고 각각에 대한 설명 계획을 실행하면 상황에 가장 적합한 답변을 얻을 수 있습니다.


    -2

    에서 인덱스의 컬럼 순서 톰 질문에 :

    따라서 인덱스의 열 순서는 쿼리 작성 방법에 따라 다릅니다. 가능한 많은 쿼리에 대해 인덱스를 사용할 수 있기를 원합니다 (모든 인덱스 수를 줄이십시오). 그러면 열의 순서가 결정됩니다. 다른 것은 없습니다 (a 또는 b의 선택성은 전혀 계산되지 않습니다).

    where 절을 기준으로 열을 정렬해야하지만 "(a 또는 b의 선택성이 전혀 계산되지 않음)"문은 정확하지 않습니다. " ( "where 절")

    당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
    Licensed under cc by-sa 3.0 with attribution required.