Indeksy złożone: najpierw najbardziej selektywna kolumna?


17

Czytałem o composite indexesi jestem trochę zdezorientowany co do zamawiania. Ta dokumentacja (nieco mniej niż w połowie) mówi

Ogólnie rzecz biorąc, w indeksie należy umieścić kolumnę, która ma być najczęściej używana.

Jednak wkrótce potem mówi

utwórz indeks złożony, stawiając na pierwszym miejscu najbardziej selektywną kolumnę; to znaczy kolumna z największą liczbą wartości.

Innymi słowy, Oracle mówi to tutaj

Jeśli wszystkie klucze są używane w klauzulach WHERE jednakowo często, wówczas uporządkowanie tych kluczy od najbardziej selektywnego do najmniej selektywnego w instrukcji CREATE INDEX najlepiej poprawia wydajność zapytania.

Znalazłem jednak odpowiedź SO, która mówi inaczej. To mówi

Ułóż kolumny tak, aby kolumna najmniej selektywna była pierwsza, a kolumna najbardziej selektywna na końcu. W przypadku ołowiu wiążącego z kolumną, który z większym prawdopodobieństwem będzie używany samodzielnie.

Pierwsza dokumentacja, o której wspomniałem, mówi, że powinieneś najpierw przejść do najczęściej używanych, podczas gdy odpowiedź SO mówi, że powinno to dotyczyć tylko rozstrzygania remisów. Następnie różnią się również w kolejności.

Ta dokumentacja również mówi skip scanningi mówi

Pomijanie skanowania jest korzystne, jeśli w wiodącej kolumnie indeksu złożonego jest kilka różnych wartości i wiele różnych wartości w kluczu nieprowadzącym indeksu.

Kolejny artykuł mówi

Kolumna prefiksu powinna być najbardziej dyskryminująca i najczęściej używana w zapytaniach

co moim zdaniem najbardziej dyskryminujące oznaczałoby najbardziej charakterystyczne.

Wszystkie te badania wciąż prowadzą mnie do tego samego pytania; czy najbardziej selektywna kolumna powinna być pierwsza czy ostatnia? Czy pierwsza kolumna powinna być najczęściej używana i tylko najbardziej selektywna w przypadku remisu?

Artykuły te wydają się ze sobą sprzeczne, ale podają kilka przykładów. Z tego, co zebrałem, wydaje się, że bardziej efektywne least selective columnjest bycie pierwszym w kolejności, jeśli się spodziewasz Index Skip Scans. Ale nie jestem pewien, czy to prawda.


Odpowiedzi:


8

Od AskTom

(w 9i pojawiło się nowe „skanowanie pomijania indeksów” - wyszukaj je, aby przeczytać o tym. To sprawia, że ​​indeks (a, b) LUB (b, a) jest czasem użyteczny w obu powyższych przypadkach!)

Tak więc kolejność kolumn w indeksie zależy od tego, w JAKI SPOSÓB są zapisywane Twoje pytania. Chcesz mieć możliwość używania tego indeksu do tylu zapytań, ile możesz (aby zmniejszyć liczbę wszystkich indeksów, które posiadasz) - to będzie decydowało o kolejności kolumn. Nic więcej (selektywność a lub b w ogóle się nie liczy).

Jednym z argumentów za ułożeniem kolumn w indeksie złożonym w kolejności od najmniej rozróżniających (mniej wyraźnych wartości) do najbardziej dyskryminujących (bardziej wyraźnych wartości) jest kompresja klucza indeksu.

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

Według statystyk indeksu pierwszy indeks jest bardziej ściśliwy.

Innym jest sposób wykorzystania indeksu w zapytaniach. Jeśli najczęściej używane są zapytania col1,

Na przykład jeśli masz pytania takie jak-

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

    -występowałby index(col1,col2)lepiej.

    Jeśli najczęściej używane są zapytania col2,

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

    -występowałby index(col2,col1)lepiej. Jeśli wszystkie zapytania zawsze określają obie kolumny, nie ma znaczenia, która kolumna jest pierwsza w indeksie złożonym.

    Podsumowując, kluczowymi zagadnieniami przy porządkowaniu kolumn indeksu złożonego są kompresja klucza indeksu i sposób użycia tego indeksu w zapytaniach.

    Bibliografia:

  • Kolejność kolumn w indeksie
  • Mniej efektywne jest posiadanie wiodących kolumn o niskiej liczności w indeksie (po prawej)?
  • Indeks Pomiń skanowanie - czy kolejność kolumn indeksu ma już znaczenie? (Znak ostrzegawczy)


  • 3

    Najpierw najbardziej selektywna jest przydatna tylko wtedy, gdy kolumna znajduje się w rzeczywistej klauzuli WHERE.

    Gdy SELECT jest według większej grupy (mniej selektywnej), a następnie być może według innych, nieindeksowanych wartości, indeks z mniej selektywnymi kolumnami może być nadal użyteczny (jeśli istnieje powód, aby nie tworzyć innego).

    Jeśli jest tabela ADRES, z

    COUNTRY CITY STREET, coś jeszcze ...

    indeksowanie ULICY, MIASTA, PAŃSTWO przyniesie najszybsze zapytania o nazwie ulicy. Ale zapytanie do wszystkich ulic miasta, indeks będzie bezużyteczne, a zapytanie prawdopodobnie wykona pełny skan tabeli.

    Indeksowanie KRAJ, MIASTO, ULICA może być nieco wolniejsze dla poszczególnych ulic, ale indeksu można używać do innych zapytań, wybierając tylko według kraju i / lub miasta.


    3

    Wybierając kolejność kolumn indeksu, nadrzędnym problemem jest:

    Czy w moich zapytaniach są predykaty (równości) względem tej kolumny?

    Jeśli kolumna nigdy nie pojawia się w klauzuli where, nie warto indeksować (1)

    OK, więc masz tabelę i zapytania dotyczące każdej kolumny. Czasami więcej niż jeden.

    Jak decydujesz, co indeksować?

    Spójrzmy na przykład. Oto tabela z trzema kolumnami. Jedna zawiera 10 wartości, kolejna 1000, ostatnia 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     

    Są to liczby wypełnione zerami. Pomoże to zwrócić uwagę na kompresję później.

    Masz trzy typowe zapytania:

    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';

    Co indeksujesz?

    Indeks tylko kilku_wartości jest tylko nieznacznie lepszy niż pełny skan tabeli:

    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 |  
    -------------------------------------------------------------------------------------------------------------

    Jest więc mało prawdopodobne, aby warto było samodzielnie indeksować. Zapytania na Lot_vals zwracają kilka wierszy (w tym przypadku tylko 1). Jest to zdecydowanie warte indeksowania.

    Ale co z zapytaniami do obu kolumn?

    Jeśli indeksujesz:

    ( few_vals, lots_vals )

    LUB

    ( lots_vals, few_vals )

    Podchwytliwe pytanie!

    Odpowiedź nie jest żadna.

    Jasne, że kilka_wartości jest długim ciągiem. Dzięki temu możesz uzyskać dobrą kompresję. I możesz (może) uzyskać skanowanie pomijania indeksu dla zapytań przy użyciu (kilka_wartości, wiele_wartości), które mają predykaty tylko na wartości_wielkości. Ale mnie tu nie ma, chociaż działa znacznie lepiej niż pełny skan:

    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 |  
    ----------------------------------------------------------------------------------------------------------------------

    Czy lubisz hazard? (2)

    Nadal potrzebujesz indeksu z Lot_Vals jako wiodącą kolumną. I przynajmniej w tym przypadku indeks złożony (kilka, partii) wykonuje tyle samo pracy, co jeden na jednym (partiach)

    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 |  
    ----------------------------------------------------------------------------------------------------------------------  

    Zdarzają się przypadki, w których indeks złożony oszczędza 1-2 IO. Ale czy warto mieć dwa indeksy dla tego oszczędzania?

    I jest inny problem z indeksem złożonym. Porównaj współczynnik klastrowania dla trzech indeksów, w tym 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    

    Zauważ, że współczynnik grupowania dla kilku_botów jest 10-krotnie wyższy niż dla wielu i wielu_lotów! I to jest na stole demo z doskonałym klastrowaniem na początek. W rzeczywistych bazach danych efekt będzie prawdopodobnie gorszy.

    Co w tym takiego złego?

    Czynnik grupowania jest jednym z kluczowych czynników decydujących o tym, jak „atrakcyjny” jest indeks. Im wyższy, tym mniej prawdopodobne jest, że optymalizator go wybierze. Zwłaszcza jeśli lot_vals nie jest tak naprawdę unikalny, ale zwykle ma kilka wierszy na wartość. Jeśli masz pecha, może to wystarczyć, aby optymalizator uznał, że pełne skanowanie jest tańsze ...

    OK, więc indeksy złożone z parametrami kilka_wartości i wartości_lotów mają tylko zalety przewagi przypadku.

    Co z zapytaniami filtrującymi kilka_wartości i wiele_wartości?

    Indeksy z pojedynczymi kolumnami dają tylko niewielkie korzyści. Ale w połączeniu zwracają niewiele wartości. Tak więc indeks złożony jest dobrym pomysłem. Ale w którą stronę?

    Jeśli umieścisz kilka pierwszych, skompresowanie wiodącej kolumny spowoduje jej zmniejszenie

    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      

    Przy mniejszej liczbie różnych wartości w kolumnie wiodącej kompresuje się lepiej. Więc jest niewiele mniej pracy na odczytanie tego indeksu. Ale tylko nieznacznie. Oba są już dobrym kawałkiem mniejszym niż oryginał (zmniejszenie rozmiaru o 25%).

    I możesz pójść dalej i skompresować cały indeks!

    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   

    Teraz oba indeksy powróciły do ​​tego samego rozmiaru. Zauważ, że wykorzystuje to fakt, że istnieje relacja między nielicznymi a wieloma. Ponownie jest mało prawdopodobne, że zobaczysz tego rodzaju korzyści w prawdziwym świecie.

    Do tej pory mówiliśmy tylko o kontrolach równości. Często z indeksami złożonymi będziesz mieć nierówność względem jednej z kolumn. np. zapytania takie jak „otrzymaj zamówienia / przesyłki / faktury dla klienta w ciągu ostatnich N dni”.

    Jeśli masz tego rodzaju zapytania, potrzebujesz równości z pierwszą kolumną indeksu:

    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 |  
    ----------------------------------------------------------------------------------------------------------------------  

    Zauważ, że używają przeciwnego indeksu.

    TL; DR

    • Kolumny z warunkami równości powinny być pierwsze w indeksie.
    • Jeśli w zapytaniu masz wiele kolumn o równości, umieszczenie na początku tej z najmniejszą liczbą różnych wartości zapewni najlepszą przewagę kompresji
    • Chociaż skany indeksów są możliwe, musisz mieć pewność, że pozostanie to opłacalną opcją w dającej się przewidzieć przyszłości
    • Indeksy złożone, w tym prawie unikalne kolumny, dają minimalne korzyści. Upewnij się, że naprawdę musisz zapisać 1-2 IO

    1: W niektórych przypadkach warto uwzględnić kolumnę w indeksie, jeśli oznacza to, że wszystkie kolumny w zapytaniu znajdują się w indeksie. Umożliwia to skanowanie tylko indeksu, więc nie musisz uzyskiwać dostępu do tabeli.

    2: Jeśli masz licencję na diagnostykę i strojenie, możesz zmusić plan do pominięcia skanowania za pomocą SQL Plan Management

    ADDEDNDA

    PS - cytowane tam dokumenty pochodzą z 9i. To naprawdę stare. Zostałbym przy czymś nowszym


    Czy zapytanie jest select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )naprawdę powszechne? Czy Oracle nie zezwala na składnię select count (distinct few_vals, many_vals, lots_vals )- która nie dokonuje konkatenacji ciągów, nie potrzebuje kolumn jako typów tekstowych i nie polega na braku :znaku?
    ypercubeᵀᴹ

    @ ypercubeᵀᴹ nie możesz zrobić count ( distinct x, y, z )w Oracle. Musisz więc wykonać odrębne podzapytanie i policzyć wyniki lub konkatenację jak wyżej. Zrobiłem to tutaj, aby wymusić dostęp do tabeli (zamiast skanowania tylko indeksu) i po prostu mieć jeden wiersz w wyniku
    Chris Saxon

    1

    Jest więcej elementów zapytania, które przyczyniają się do ostatecznej decyzji o tym, od czego powinien zacząć się indeks złożony i / lub oprócz selektywności kolumny.

    na przykład:

    1. jaki typ operatora zapytania jest używany: Jeśli zapytania zawierają operatory takie jak
      „>,> =, <, <=”
    2. Ile rzeczywistych wierszy oczekiwano w wyniku zapytania: Czy wynikiem zapytania będzie większość wierszy z tabeli.
    3. Czy jakieś funkcje są używane w kolumnie tabeli podczas klauzuli Where: Jeśli zapytanie ma jakąkolwiek funkcję GÓRNA, DOLNA, PRZYCINANIE, SUBSTRING używana w kolumnie używanej w GDZIE.

    jednak aby zachować aktualność rozmowy, moja poniższa odpowiedź dotyczy następującej sytuacji:

    1. „90% typów zapytań w danej tabeli ma klauzulę WHERE z operatorem =”
    2. „co najwyżej zapytanie zwraca 10% łącznej liczby wierszy w tabeli”
    3. „w kolumnie tabeli w klauzuli WHERE nie są używane żadne funkcje”
    4. „większość kolumn czasowych w klauzuli WHERE jest przeważnie typu,
      ciągu znaków”

    Z mojego doświadczenia wynika, że ​​zarówno DBA powinna mieć na uwadze.

    Wyobraźmy sobie, że stosowana jest tylko jedna zasada:

    1) Jeśli utworzę indeks z najbardziej selektywną kolumną jako pierwszą, ale kolumna ta nie jest faktycznie używana przez większość zapytań w tej tabeli, to nie ma zastosowania dla silnika db.

    2) Jeśli utworzę indeks z najczęściej używaną kolumną w zapytaniu, która jest pierwsza w indeksie, ale kolumna ma niską selektywność, wówczas moja wydajność zapytania nie będzie dobra.

    Wymienię kolumny, które są najczęściej używane w 90% zapytań tabelowych. Następnie ułóż je tylko w kolejności od największej do najmniejszej.

    Używamy indeksów do poprawy wydajności zapytania do odczytu, a przepływ pracy (typy zapytania do odczytu) powinien tylko napędzać tworzenie indeksu. W rzeczywistości, gdy dane rosną (miliardy wierszy), skompresowany indeks może zaoszczędzić miejsce, ale na pewno pogorszy wydajność zapytania do odczytu.


    1

    Teoretycznie najbardziej selektywna kolumna daje najszybsze wyszukiwanie. Ale w pracy natknąłem się na sytuację, w której mamy indeks złożony z 3 części, z których najbardziej selektywna jest część pierwsza. (data, autor, firma wydawnicza, powiedzmy, w tej kolejności, tabela monitoruje kciuki do góry na postach) i mam zapytanie, które wykorzystuje wszystkie 3 części. Mysql domyślnie korzysta z autorskiego indeksu tylko pomijając indeks złożony zawierający firmę i datę, mimo że są one obecne w moim zapytaniu. Użyłem indeksu siły, aby użyć kompozytu, a zapytanie faktycznie działało wolniej. Dlaczego tak się stało? Powiem ci:

    Wybrałem zakres w dniu, więc pomimo tego, że data jest bardzo wybiórcza, fakt, że używamy go do skanowania zasięgu (chociaż zasięg jest stosunkowo krótki, 6 miesięcy z 6 lat danych) spowodował, że kompozyt był szkodliwy dla mysql. Aby użyć kompozytu w tym konkretnym przypadku, mysql musi pobrać wszystkie artykuły napisane od nowego roku, a następnie zanalizować, kim jest autor, i biorąc pod uwagę, że autor nie napisał tylu artykułów w porównaniu do innych autorów, mysql wolał po prostu znaleźć tego autora .

    W innym przypadku zapytanie działało znacznie szybciej na kompozycie, tak było w przypadku, gdy autor był niezwykle popularny i był właścicielem większości rekordów, sortowanie według daty miało sens. Ale mysql nie wykrył tej sprawy automatycznie, musiałem wymusić indeks ... Więc wiesz, to się zmienia. Skany zakresu mogą sprawić, że Twoja selektywna kolumna nie będzie użyteczna. Dystrybucja danych może sprawić, że kolumny będą bardziej selektywne dla różnych rekordów ...

    To, co zrobiłbym inaczej, to przesunięcie daty (która teoretycznie jest najbardziej selektywna) w prawo, ponieważ wiem, że teraz wykonam na niej skanowanie zakresu i to robi różnicę.


    1
    Jeśli twoje zapytanie zawierało coś takiego, WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)to indeks włączony (author, publishing_company, date)lub włączony (publishing_company, author, date)byłby lepszy i zostałby użyty - bez wymuszania go.
    ypercubeᵀᴹ

    -2

    Różne przypadki dla różnych sytuacji. Poznaj swój cel; następnie utwórz swoje indeksy i uruchom plany wyjaśniania dla każdego z nich, a uzyskasz najlepszą odpowiedź na swoją sytuację.


    -2

    Z kolejności kolumn w Indeksie na Zapytaj Toma:

    Tak więc kolejność kolumn w indeksie zależy od tego, w JAKI SPOSÓB są zapisywane Twoje pytania. Chcesz mieć możliwość używania tego indeksu do tylu zapytań, ile możesz (aby zmniejszyć liczbę wszystkich indeksów, które posiadasz) - to będzie decydowało o kolejności kolumn. Nic więcej (selektywność a lub b w ogóle się nie liczy).

    Zgadzam się, że musimy uporządkować kolumny na podstawie klauzuli where, ale stwierdzenie „(selektywność a lub b wcale się nie liczy)” nie jest poprawne.) „Najbardziej selektywne kolumny powinny być wiodące, jeśli spełniają pierwszą rolę („klauzula gdzie”)

    Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
    Licensed under cc by-sa 3.0 with attribution required.