Составные индексы: сначала самый выборочный столбец?

17

Я читал о, composite indexesи я немного запутался по поводу заказа. Эта документация (чуть менее половины пути) говорит

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

Однако вскоре после этого говорится

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

Oracle также говорит это здесь другими словами

Если все ключи используются в предложениях WHERE одинаково часто, то упорядочение этих ключей от наиболее избирательных к наименее селективным в операторе CREATE INDEX лучше всего повышает производительность запросов.

Тем не менее, я нашел SO ответ, который говорит по-другому. Это говорит

Расположите столбцы с наименее селективным столбцом первым и наиболее избирательным столбцом последним. В случае связующего провода с колонкой, которая, скорее всего, будет использоваться самостоятельно.

Первая документация, на которую я ссылался, гласит, что вы должны сначала перейти на наиболее часто используемые, тогда как ответ SO говорит, что это должно быть только для разрыва связи. Тогда они также различаются по порядку.

Эта документация также говорит skip scanningи говорит

Пропускное сканирование выгодно, если в начальном столбце составного индекса имеется несколько отдельных значений и много значений в неконечном ключе индекса.

Другая статья говорит

Столбец префикса должен быть наиболее разборчивым и наиболее широко используемым в запросах.

который я считаю наиболее различительным будет означать наиболее характерным.

Все эти исследования все еще приводят меня к одному и тому же вопросу; Должен ли самый селективный столбец быть первым или последним? Должен ли первый столбец быть наиболее используемым и только самым избирательным на тай-брейке?

Эти статьи, кажется, противоречат друг другу, но они предлагают некоторые примеры. Из того, что я понял, кажется, что более эффективно least selective columnбыть первым в заказе, если вы ожидаете Index Skip Scans. Но я не совсем уверен, правильно ли это.

Эрик
источник

Ответы:

8

От AskTom

(в 9i есть новое «сканирование с пропуском индекса» - найдите его там, чтобы прочитать об этом. Иногда это делает индекс (a, b) ИЛИ (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)будет лучше. Если во всех ваших запросах всегда указываются оба столбца, то не имеет значения, какой столбец стоит первым в составном индексе.

    В заключение следует отметить, что основными соображениями при упорядочении столбцов составного индекса являются сжатие ключа индекса и то, как вы собираетесь использовать этот индекс в своих запросах.

    Ссылки:

  • Порядок столбцов в указателе
  • Менее эффективно иметь низкое количество ведущих колонок в индексе (справа)?
  • Пропуск сканирования индекса - имеет ли значение порядок столбцов индекса? (Предупреждающий знак)

  • JSapkota
    источник
    3

    Первый выбор наиболее полезен, только если этот столбец находится в фактическом предложении WHERE.

    Когда SELECT относится к большей группе (менее избирательной), а затем, возможно, по другим неиндексированным значениям, индекс с менее селективными столбцами может все еще быть полезным (если есть причина не создавать другой).

    Если есть таблица АДРЕС, с

    УЛИЦА ГОРОДА СТРАНЫ, что-то еще ...

    Индексирование STREET, CITY, COUNTRY даст самые быстрые запросы с названием улицы. Но при запросе всех улиц города индекс будет бесполезным, и запрос, скорее всего, произведет полное сканирование таблицы.

    Индексирование COUNTRY, CITY, STREET может быть немного медленнее для отдельных улиц, но индекс можно использовать для других запросов, выбирая только по стране и / или городу.

    Эрик Харт
    источник
    3

    При выборе порядка столбцов индекса важнее всего:

    Есть ли в моих запросах (равенство) предикаты против этого столбца?

    Если столбец никогда не появляется в предложении where, индексировать его не стоит (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';

    Что вы индексируете?

    Индекс только на times_val только немного лучше, чем полное сканирование таблицы:

    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 )

    Хитрый вопрос!

    Ответ ни один.

    Конечно, little_vals - это длинная строка. Таким образом, вы можете получить хорошее сжатие из этого. И вы (возможно) получите сканирование с пропуском индекса для запросов с использованием (many_vals, lots_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    

    Обратите внимание, что коэффициент кластеризации для two_lots в 10 раз выше, чем для лотов и lots_few! И это в демонстрационной таблице с идеальной кластеризацией для начала. В реальных базах данных эффект, вероятно, будет хуже.

    Так что в этом плохого?

    Фактор кластеризации является одним из ключевых факторов, определяющих, насколько «привлекательным» является индекс. Чем оно выше, тем меньше вероятность, что оптимизатор выберет его. Особенно, если lots_vals на самом деле не уникален, но все же обычно имеет несколько строк на значение. Если вам не повезло, этого может быть достаточно, чтобы оптимизатор решил, что полное сканирование дешевле ...

    Хорошо, поэтому составные индексы с less_vals и lots_vals имеют преимущества только в крайнем случае.

    Как насчет запросов, фильтрующих little_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 ввода-вывода

    1: В некоторых случаях может быть целесообразно включить столбец в индекс, если это означает, что все столбцы в вашем запросе находятся в индексе. Это позволяет сканировать только по индексу, поэтому вам не нужно обращаться к таблице.

    2. Если у вас есть лицензия на диагностику и настройку, вы можете принудительно пропустить план с помощью SQL Plan Management

    ADDEDNDA

    PS - документы, которые вы цитировали, есть от 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 )в Oracle. Таким образом, вам нужно сделать отдельный подзапрос и подсчитать результаты или конкатенацию, как указано выше. Я просто сделал это здесь для принудительного доступа к таблице (а не для сканирования только по индексу) и просто получил одну строку в результате
    Крис Саксон,
    1

    Больше элементов запроса вносит свой вклад в окончательное решение о том, что должен начинаться с составного индекса и / или содержать помимо селективности столбца.

    например:

    1. какой тип оператора запроса используется: если в запросах есть операторы типа
      ">,> =, <, <="
    2. Сколько фактических строк ожидается в результате запроса: Будет ли результат запроса составлять большинство строк таблицы.
    3. Используются ли какие-либо функции для столбца таблицы во время предложения Where: Если в запросе есть какая-либо функция UPPER, LOWER, TRIM, SUBSTRING, используемая для столбца, используемого в условии WHERE.

    Тем не менее, чтобы поддержать разговор, мой следующий ответ относится к следующей ситуации:

    1. Msgstr "90% типов запросов в данной таблице имеют предложение WHERE с оператором ="
    2. «В большинстве случаев запрос возвращает 10% от общего количества строк в таблице»
    3. msgstr "в столбце таблицы в предложении WHERE не используются никакие функции"
    4. «в большинстве случаев используемые в предложении WHERE столбцы в основном имеют тип номер,
      строка»

    По моему опыту, администратор БД должен помнить об этом.

    Давайте представим, что применяется только одно правило:

    1) Если я создаю индекс с наиболее селективным столбцом, являющимся первым, но этот столбец фактически не используется большинством запросов к этой таблице, он не используется для механизма обработки БД.

    2) Если я создаю индекс с наиболее широко используемым столбцом в запросе, который является первым в индексе, но столбец имеет низкую селективность, то и производительность моего запроса не будет хорошей.

    Я перечислю столбцы, которые в основном используются в 90% запросов к таблице. Затем поместите их только в порядке наибольшего количества элементов к наименьшему количеству элементов.

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

    Ануп Шах
    источник
    1

    Теоретически наиболее селективный столбец дает самый быстрый поиск. Но на работе я просто наткнулся на ситуацию, когда у нас составной индекс из 3 частей, причем сначала наиболее селективная часть. (дата, автор, издательская компания, скажем, в таком порядке, таблица следит за публикациями), и у меня есть запрос, который использует все 3 части. Mysql по умолчанию использует только авторский индекс, пропуская составной индекс, содержащий компанию и дату, несмотря на то, что они присутствуют в моем запросе. Я использовал Force Index, чтобы использовать композит, и запрос на самом деле работал медленнее. Почему это случилось? Я скажу вам:

    Я выбирал диапазон для даты, поэтому, несмотря на то, что дата является очень избирательной, тот факт, что мы используем ее для сканирования диапазона (даже если диапазон относительно короткий, 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

    Разные случаи для разных ситуаций. Знай свою цель; затем создайте свои индексы и выполните планы объяснения для каждого из них, и у вас будет лучший ответ для вашей ситуации.

    RMPJ
    источник
    -2

    Из порядка столбцов в указателе на вопрос Тома:

    Итак, порядок столбцов в вашем индексе зависит от того, КАК написаны ваши запросы. Вы хотите иметь возможность использовать индекс для максимально возможного количества запросов (чтобы сократить общее количество индексов, которые у вас есть), что будет определять порядок столбцов. Ничего другого (селективность a или b не считается вообще).

    Согласитесь, что мы должны упорядочивать столбцы, основываясь на предложении where, но утверждение «(селективность a или b не учитывается вообще)» неверно.) «. Наиболее избирательные столбцы должны быть ведущими, если удовлетворена первая роль ("где пункт")

    Анжелько Миовчич
    источник