综合指标:选择性最强的列优先?


17

我一直在阅读composite indexes有关订购的信息,我有些困惑。该文档(至少不到一半)说

通常,应将预期最常使用的列放在索引的第一位。

然而,不久之后说

创建一个将最有选择性的列放在首位的复合索引;即,具有最多值的列。

甲骨文也在这里

如果在WHERE子句中平等地使用所有键,则在CREATE INDEX语句中将这些键从最有选择性的顺序排列到最不有选择性的顺序可以最大程度地提高查询性能。

但是,我找到了一个SO答案,但答案却有所不同。它说

排列这些列,其中最不具选择性的列在前,最不具选择性的列在最后。如果是与柱子捆绑在一起,则更可能单独使用。

我引用的第一个文档说您应该首先去最常用的文档,而SO回答则应该只用于平局。然后它们的订购也有所不同。

文档还讨论skip scanning并说

如果在复合索引的前导列中只有几个不同的值,而在索引的非前导键中有很多不同的值,则跳过扫描是有利的。

一篇文章

前缀列应该是查询中最具区分性和最广泛使用的

我相信最有区别的意思将是最独特的。

所有这些研究仍然将我引向同一问题。最有选择性的栏应该在第一还是最后?在抢七局中,第一栏应该是使用最多,选择最多的栏吗?

这些文章似乎相互矛盾,但是它们确实提供了一些示例。从我收集到的信息来看,如果您期望,那么成为订单中least selective column第一名似乎更有效率Index Skip Scans。但我不确定这是否正确。


Answers:


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)会更好。如果所有查询始终都指定两个列,那么哪个列在复合索引中排在第一位并不重要。

    总之,组合索引的列顺序中的关键注意事项是索引键压缩以及如何在查询中使用此索引。

    参考文献:

  • 索引中的列顺序
  • 在索引中具有低基数前导列的效率较低(右)?
  • 索引跳过扫描–索引列顺序是否再重要?(警告牌)


  • 3

    仅当此列在实际的WHERE子句中时,最有选择性的优先才有用。

    当SELECT由较大的组(选择性较小),然后可能由其他非索引值组成时,具有较少选择性列的索引可能仍然有用(如果有理由不创建另一个索引)。

    如果有一个表ADDRESS,

    乡村城市街,还有别的...

    索引STREET,CITY,COUNTRY的街道名称将产生最快的查询。但是查询城市的所有街道时,索引将无用,并且该查询可能会进行全表扫描。

    对于个别街道,对国家,城市,街道的索引可能会慢一些,但该索引可用于其他查询,仅按国家和/或城市进行选择。


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

    你索引什么?

    仅针对little_vals的索引仅比全表扫描略胜一筹:

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

    因此,单独索引不太可能。lot_vals上的查询返回几行(在这种情况下,仅返回1)。因此,这绝对值得索引。

    但是,针对这两列的查询又如何呢?

    你应该索引:

    ( few_vals, lots_vals )

    要么

    ( lots_vals, few_vals )

    技巧问题!

    答案都不是。

    当然little_vals是一个长字符串。因此,您可以获得很好的压缩效果。而且,您(可能)使用仅对lot_vals谓词的(few_vals,lots_vals)进行查询的索引跳过扫描。但我不在这里,尽管它的性能明显优于完整扫描:

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

    你喜欢赌博吗?(2)

    因此,您仍然需要一个以lots_vals作为前导列的索引。至少在这种情况下,复合指数(少量,手数)所做的工作量与单手(很多)上的工作量相同

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

    在某些情况下,复合索引可以为您节省1-2个IO。但是值得为此节省两个索引吗?

    而且复合索引还有另一个问题。比较三个索引(包括LOTS_VALS)的聚类因子:

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

    请注意,leather_lots的聚类因子比lots和lots_few 高10倍!这是在演示表中,该表首先具有完美的群集。在现实世界的数据库中,效果可能会更糟。

    那有什么不好的呢?

    聚类因子是确定索引“吸引力”的关键驱动因素之一。它越高,优化器选择它的可能性就越小。特别是在lots_vals实际上并非唯一的情况下,但通常每个值仍然只有几行。如果您不走运,这可能足以使优化程序认为完整扫描更便宜...

    好的,因此具有little_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个IO

    1:在某些情况下,如果这意味着查询中的所有列都在索引中,则可能值得在索引中包括一列。这将启用仅索引扫描,因此您无需访问表。

    2:如果您获得了诊断和调优的许可,则可以使用SQL计划管理将计划强制跳过扫描

    阿德尼达

    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中进行操作。因此,您需要执行一个单独的子查询并计算结果或如上的串联。我只是在这里强制表访问(而不是仅索引扫描),并且结果中只有一行
    Chris Saxon

    1

    除了列的选择性之外,还有更多的查询元素有助于最终决定综合索引应以什么开头和/或包含什么。

    例如:

    1. 正在使用哪种类型的查询运算符:如果查询具有
      “>,> =,<,<=”
    2. 查询结果预期有多少实际行:查询结果将是表中的大多数行。
    3. 在where子句期间,是否在表列上使用了任何函数:如果查询在WHERE条件中使用的列上使用了任何函数UPPER,LOWER,TRIM,SUBSTRING。

    仍保持对话相关性,我的以下回答适用于以下情况:

    1. “给定表上90%的查询类型都带有带有运算符=的WHERE子句”
    2. “最多查询结果返回表中总行数的10%”
    3. “在WHERE子句的表列上未使用任何函数”
    4. “在WHERE子句中使用的大多数时间列大多是数字,
      字符串类型”

    以我的经验,DBA应该同时考虑这两种情况。

    假设正在应用唯一的规则:

    1)如果我创建索引时将最有选择性的列排在第一位,但是该列实际上未被该表上的大多数查询使用,因为它对数据库引擎没有用。

    2)如果我创建的索引中查询中使用最广泛的列排在索引的首位,但该列的选择性比我的查询性能低,那么它的性能也不会很好。

    我将列出90%的表查询中最常用的列。然后将那些仅按最大基数到最小基数的顺序排列。

    我们使用索引来提高读取查询的性能,并且工作流程(读取查询的类型)仅应驱动索引的创建。实际上,随着数据的增长(数十亿行),压缩索引可以节省存储空间,但必定会损害读取查询的性能。


    1

    从理论上讲,最有选择性的列会产生最快的搜索。但是在工作中,我偶然发现了这样一个情况:我们有3个部分的综合索引,其中最具有选择性的部分在前。(日期,作者,出版公司可以这样说,表格按顺序监视​​帖子的竖起大拇指),而我有一个使用全部3部分的查询。Mysql默认使用作者onlny索引跳过包含公司和日期的复合索引,尽管它们出现在我的查询中。我使用了强制索引来使用复合索引,而查询实际上运行缓慢。为什么会这样呢?我告诉你:

    我正在选择日期范围,因此尽管日期具有很高的选择性,但事实是我们将其用于范围扫描(即使范围相对较短,也就是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

    从Ask Tom 索引中的列顺序中

    因此,索引中列的顺序取决于如何编写查询。您希望能够将索引用于尽可能多的查询(以减少所拥有的索引总数)-这将驱动列的顺序。没有别的(a或b的选择性根本不算在内)。

    同意,我们必须根据where子句对列进行排序,但是语句“(a或b的选择性根本不算在内)”是不正确的。)。 (“ where子句”)

    By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
    Licensed under cc by-sa 3.0 with attribution required.