Складені індекси: Найперше вибірковий стовпець?


17

Я читав про composite indexesі я трохи розгублений щодо замовлення. Ця документація (трохи менше половини шляху вниз) говорить

Загалом, слід розмістити стовпець, який, як очікується, найчастіше використовується спочатку в індексі.

Однак незабаром це говорить

створити складений індекс, поставивши першу найбільш селективну колонку; тобто стовпець з найбільшою кількістю значень.

Oracle також говорить це тут іншими словами

Якщо всі ключі використовуються в пунктах WHERE однаково часто, то впорядкування цих ключів від найбільш селективного до найменш селективного в операторі CREATE INDEX найкраще покращує продуктивність запитів.

Однак я знайшов відповідь, яка говорить інакше. Він говорить

Впорядкуйте стовпчики спочатку з найменш вибірковим стовпцем та останнім із найбільш вибірковим стовпцем. Що стосується стяжки, то це стовпчик, який, швидше за все, буде використаний самостійно.

Перша документація, на яку я посилався, говорить про те, що спочатку слід пройти найчастіше використовуваний, тоді як у відповіді "ТА" сказано, що це стосується лише розриву краватки. Потім вони також розрізняються по впорядкованості.

Ця документація також говорить 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 є більшою групою (менш вибірковою), а потім, можливо, іншими, неіндексованими значеннями, індекс із менш вибірковими стовпцями все ще може бути корисним (якщо є причина не створювати інший).

    Якщо є таблиця АДРЕСА, с

    КРАЇНА МІСЬКА ВУЛИЦЯ, щось інше ...

    індексація ВУЛИЦЯ, МІСТО, КРАЇНА дасть найшвидші запити з назвою вулиці. Але запитуючи всі вулиці міста, індекс буде марним, і запит, швидше за все, сканує повну таблицю.

    Індексація КРАЇНИ, МІСТА, УЛИЦИ може бути дещо повільнішою для окремих вулиць, але індекс можна використовувати для інших запитів, вибираючи лише країну та / або місто.


    3

    Вибираючи порядок стовпців індексу, головним питанням є:

    Чи є предикати (рівність) проти цього стовпця в моїх запитах?

    Якщо стовпець ніколи не з’являється в пункті де, не варто індексувати (1)

    Гаразд, значить, у вас є таблиця та запити проти кожного стовпця. Іноді більше одного.

    Як ви вирішите, що індексувати?

    Давайте розглянемо приклад. Ось таблиця з трьома стовпцями. Один містить 10 значень, інший 1000, останні 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     

    Це цифри, залишені знаками нулів. Це допоможе пізніше розглянути питання про стиснення.

    Отже, у вас є три поширені запити:

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

    Що ви індексуєте?

    Індекс лише у декількох колах є лише кращим, ніж сканування повного столу:

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

    Тож навряд чи варто було б індексувати самостійно. Запити на lot_vals повертають кілька рядків (у цьому випадку всього 1). Тож це, безумовно, варто індексувати.

    А як щодо запитів до обох стовпців?

    Якщо ви індексуєте:

    ( few_vals, lots_vals )

    АБО

    ( lots_vals, few_vals )

    Хитрість питання!

    Відповідь - ні.

    Звичайно, кілька_ валів - це довга струна. Таким чином, ви можете отримати гарне стиснення від цього. І ви (можливо) отримаєте сканування пропуску індексу для запитів, використовуючи (мало_значень, лотів_валів), які мають лише предикати на lot_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)

    Отже, вам все ще потрібен індекс з lot_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    

    Зауважте, що коефіцієнт кластеризації для little_lots на 10 разів вищий, ніж для лотів та lot_few! І це в демонстраційній таблиці з ідеальною кластеризацією для початку. У реальних базах даних ефект, ймовірно, буде гіршим.

    То що в цьому поганого?

    Коефіцієнт кластеризації є одним із ключових факторів, що визначають, наскільки "привабливим" є індекс. Чим він вищий, тим менше шансів оптимізатора вибрати його. Особливо, якщо lot_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 Plan Management

    ДОДАТИ

    PS - документи, які ви цитували, є з 9i. Це reeeeeeally старе. Я б дотримувався чогось більш недавнього


    Чи є запит із 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 ) в Oracle. Отже, вам потрібно зробити окремий підзапит і порахувати результати або конкатенацію, як описано вище. Я щойно зробив це для того, щоб змусити доступ до таблиці (а не сканувати лише покажчик) і мати лише один рядок у результаті
    Кріс Саксон

    1

    Є більше елементів запиту, що сприяє остаточному вирішенню питання про те, з чого слід починати та / або містити складений індекс, крім селективності стовпця.

    наприклад:

    1. який тип оператора запитів використовується: Якщо запити мають операторів типу
      ">,> =, <, <="
    2. Скільки фактичних рядків очікується в результаті запиту: чи буде результат запиту більшою частиною рядків таблиці.
    3. Чи використовуються будь-які функції на стовпчику таблиці під час пункту «Де»: Якщо запит має яку-небудь функцію ВНУТРІШНІЙ, НІЖЧИЙ, ТРИМИЧНІ, ПІДТРИМКИ, що використовуються на стовпчику, що використовується в умовах WHERE.

    але щоб підтримувати розмову актуальною, моя нижченаведена відповідь стосується такої ситуації:

    1. "90% тип запитів у даній таблиці містить пункт WHERE з оператором ="
    2. "щонайбільше запит повертає 10% від загальної кількості рядків у таблиці"
    3. "жодних функцій не використовується в стовпці таблиці в пункті WHERE"
    4. "Більшість стовпців часу в використовуваному пункті WHERE в основному мають тип типу,
      рядка"

    На мій досвід, DBA слід пам’ятати і про те, і про це.

    Уявімо, що застосовується єдине правило:

    1) Якщо я створюю індекс, коли перший селективний стовпець є першим, але цей стовпець насправді не використовується більшістю запитів у цій таблиці, ніж він не використовується для db engine.

    2) Якщо я створюю індекс із найбільш широко використовуваним стовпцем у запиті, який є першим в індексі, але стовпець має низьку селективність, ніж також моє запит не буде хорошим.

    Я перерахую стовпці, які в основному використовуються в 90% запитів таблиці. Потім поставте ті лише в порядку більшості кардинальності до найменшої кардинальності.

    Ми використовуємо індекси для покращення продуктивності запиту читання, і цей робочий процес (типи запиту читання) повинен рухати лише створення індексу. Насправді, у міру зростання даних (мільярди рядків) стислий індекс може економити сховище, але впевнено погіршує роботу запиту читання.


    1

    Теоретично найселективніший стовпчик дає найшвидший пошук. Але на роботі я просто натрапив на ситуацію, коли у нас є складений індекс з 3 частин із найвибірковішою частиною. (дата, автор, видавнича компанія дозволяє сказати, що в такому порядку таблиця відстежує великі пальці публікацій), і у мене є запит, який використовує всі 3 частини. Mysql за замовчуванням використовує авторський індексний індекс, пропускаючи складений індекс, що містить компанію та дату, незважаючи на те, що вони містяться в моєму запиті. Я використовував індекс сили, щоб використовувати композит, і запит фактично працював повільніше. Чому так сталося? Я скажу вам:

    Я вибирав діапазон у дату, тому, незважаючи на дату, яка була дуже вибірковою, той факт, що ми використовуємо його для сканування діапазону (хоча діапазон порівняно короткий, 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 взагалі не враховується).

    Погодьтеся, що нам потрібно замовити стовпці на основі пункту де, але твердження "(вибірковість a або b взагалі не враховується)" є невірним.) ". Найбільш селективні стовпці повинні бути провідними, якщо він задоволений першою роллю ("де пункт")

    Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
    Licensed under cc by-sa 3.0 with attribution required.