Índices compuestos: ¿La columna más selectiva primero?


17

He estado leyendo composite indexesy estoy un poco confundido acerca de ordenar. Esta documentación (poco menos de la mitad) dice

En general, debe colocar la columna que se espera que se use con mayor frecuencia primero en el índice.

Sin embargo, poco después dice

cree un índice compuesto poniendo primero la columna más selectiva; es decir, la columna con más valores.

Oracle también lo dice aquí en otras palabras

Si todas las claves se usan en las cláusulas WHERE con la misma frecuencia, entonces ordenar estas claves de la más selectiva a la menos selectiva en la instrucción CREATE INDEX mejora el rendimiento de la consulta.

Sin embargo, he encontrado una respuesta SO que dice de manera diferente. Dice

Organice las columnas con la columna menos selectiva primero y la columna más selectiva al final. En el caso de un empate con la columna, es más probable que se use solo.

La primera documentación a la que me referí dice que primero debes usar el más utilizado, mientras que la respuesta SO dice que solo debe ser para romper el empate. Entonces también difieren en el pedido.

Esta documentación también habla skip scanningy dice

El escaneo de omisión es ventajoso si hay pocos valores distintos en la columna inicial del índice compuesto y muchos valores distintos en la clave no líder del índice.

Otro artículo dice

La columna de prefijo debe ser la más exigente y la más utilizada en las consultas.

que creo que más discriminatorio significaría más distintivo.

Toda esta investigación todavía me lleva a la misma pregunta; ¿La columna más selectiva debe ser la primera o la última? ¿Debería ser la primera columna la más utilizada y la más selectiva en un desempate?

Estos artículos parecen estar contradiciéndose entre sí, pero ofrecen algunos ejemplos. Por lo que he reunido, parece ser más eficiente least selective columnque sea el primero en el pedido si está anticipando Index Skip Scans. Pero no estoy realmente seguro de si eso es correcto.


Respuestas:


8

De AskTom

(en 9i, hay un nuevo "escaneo de omisión de índice" - búsquelo para leer sobre eso. ¡Hace que el índice (a, b) OR (b, a) sea útil en los dos casos anteriores a veces!)

Entonces, el orden de las columnas en su índice depende de CÓMO se escriben sus CONSULTAS. Desea poder usar el índice para tantas consultas como sea posible (para reducir el número total de índices que tiene), lo que impulsará el orden de las columnas. Nada más (la selectividad de aob no cuenta en absoluto).

Uno de los argumentos para organizar columnas en el índice compuesto en orden desde el menos discriminatorio (valores menos distintos) hasta el más discriminatorio (valores más distintos) es para la compresión de clave de índice.

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

Según las estadísticas del índice, el primer índice es más compresible.

Otra es cómo se usa el índice en sus consultas. Si sus consultas usan principalmente col1,

Por ejemplo, si tiene consultas como:

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

    -Entonces index(col1,col2)se desempeñaría mejor.

    Si sus consultas usan principalmente col2,

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

    -luego index(col2,col1) se desempeñaría mejor. Si todas sus consultas siempre especifican ambas columnas, no importa qué columna aparezca primero en el índice compuesto.

    En conclusión, las consideraciones clave en el orden de las columnas del índice compuesto son la compresión de la clave del índice y cómo va a utilizar este índice en sus consultas.

    Referencias

  • Orden de columnas en el índice
  • ¿Es menos eficiente tener columnas cardinales de baja cardinalidad en un índice (correcto)?
  • Escaneo de omisión de índice: ¿importa el orden de la columna de índice? (Señal de advertencia)


  • 3

    Lo más selectivo primero es útil solo cuando esta columna está en la cláusula WHERE real.

    Cuando SELECT es por un grupo más grande (menos selectivo), y luego posiblemente por otros valores no indexados, un índice con columnas menos selectivas aún puede ser útil (si hay una razón para no crear otro).

    Si hay una tabla DIRECCIÓN, con

    COUNTRY CITY STREET, algo más ...

    indexar STREET, CITY, COUNTRY generará las consultas más rápidas con el nombre de una calle. Pero al consultar todas las calles de una ciudad, el índice será inútil y la consulta probablemente hará un escaneo completo de la tabla.

    La indexación de PAÍS, CIUDAD, CALLE puede ser un poco más lenta para calles individuales, pero el índice se puede usar para otras consultas, solo seleccionando por país y / o ciudad.


    3

    Al elegir el orden de la columna de índice, la preocupación principal es:

    ¿Hay predicados (de igualdad) en esta columna en mis consultas?

    Si una columna nunca aparece en una cláusula where, no vale la pena indexarla (1)

    OK, entonces tienes una tabla y consultas en cada columna. A veces más de uno.

    ¿Cómo decides qué indexar?

    Veamos un ejemplo. Aquí hay una tabla con tres columnas. Uno tiene 10 valores, otros 1,000, los últimos 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     

    Estos son números que quedan rellenados con ceros. Esto ayudará a aclarar más adelante la compresión.

    Entonces tienes tres consultas comunes:

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

    ¿Qué indizas?

    Un índice en solo unos pocos valores es solo marginalmente mejor que una exploración de tabla completa:

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

    Por lo tanto, es poco probable que valga la pena indexarlo solo. Las consultas en lots_vals devuelven algunas filas (solo 1 en este caso). Así que definitivamente vale la pena indexarlo.

    Pero, ¿qué pasa con las consultas en ambas columnas?

    ¿Debería indexar:

    ( few_vals, lots_vals )

    O

    ( lots_vals, few_vals )

    ¡Pregunta capciosa!

    La respuesta es ninguno.

    Claro que few_vals es una cadena larga. Para que pueda obtener una buena compresión. Y (podría) obtener una exploración de omisión de índice para las consultas usando (few_vals, lots_vals) que solo tienen predicados en lots_vals. Pero no lo hago aquí, a pesar de que funciona notablemente mejor que un escaneo completo:

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

    ¿Te gustan los juegos de azar? (2)

    Por lo tanto, aún necesita un índice con lots_vals como columna principal. Y al menos en este caso, el índice compuesto (pocos, lotes) realiza la misma cantidad de trabajo que uno solo (lotes)

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

    Habrá casos en los que el índice compuesto le ahorrará 1-2 IO. ¿Pero vale la pena tener dos índices para este ahorro?

    Y hay otro problema con el índice compuesto. Compare el factor de agrupación para los tres índices, incluidos 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    

    ¡Tenga en cuenta que el factor de agrupación para few_lots es 10 veces mayor que para lots y lots_few! Y esto está en una tabla de demostración con una agrupación perfecta para empezar. En las bases de datos del mundo real, es probable que el efecto sea peor.

    Entonces, ¿qué tiene de malo eso?

    El factor de agrupamiento es uno de los factores clave que determina cuán "atractivo" es un índice. Cuanto más alto sea, menos probable es que el optimizador lo elija. Particularmente si lots_vals no son realmente únicos, pero normalmente tienen pocas filas por valor. Si no tiene suerte, esto podría ser suficiente para hacer que el optimizador piense que un análisis completo es más barato ...

    De acuerdo, los índices compuestos con pocos_valos y lotes_valor solo tienen ventajas de caso límite.

    ¿Qué pasa con las consultas que filtran unos pocos valores y muchos valores?

    Los índices de columnas individuales solo ofrecen pequeños beneficios. Pero combinados devuelven pocos valores. Entonces, un índice compuesto es una buena idea. ¿Pero en qué sentido?

    Si coloca unos pocos primero, al comprimir la columna principal se reducirá

    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      

    Con menos valores diferentes en la columna inicial, se comprime mejor. Entonces, hay marginalmente menos trabajo para leer este índice. Pero solo un poco. Y ambos ya son una buena porción más pequeña que la original (disminución del tamaño del 25%).

    ¡Y puedes ir más allá y comprimir todo el índice!

    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   

    Ahora ambos índices vuelven al mismo tamaño. Tenga en cuenta que esto aprovecha el hecho de que hay una relación entre pocos y muchos. De nuevo, es poco probable que veas este tipo de beneficio en el mundo real.

    Hasta ahora solo hemos hablado de los controles de igualdad. A menudo, con índices compuestos, tendrá una desigualdad con respecto a una de las columnas. por ejemplo, consultas como "obtener los pedidos / envíos / facturas de un cliente en los últimos N días".

    Si tiene este tipo de consultas, desea la igualdad con la primera columna del índice:

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

    Observe que están usando el índice opuesto.

    TL; DR

    • Las columnas con condiciones de igualdad deben ir primero en el índice.
    • Si tiene varias columnas con igualdades en su consulta, colocar primero la que tenga la menor cantidad de valores diferentes le dará la mejor ventaja de compresión
    • Si bien los escaneos de omisión de índice son posibles, debe estar seguro de que seguirá siendo una opción viable en el futuro previsible.
    • Los índices compuestos que incluyen columnas casi únicas brindan beneficios mínimos. Asegúrese de que realmente necesita guardar los 1-2 IO

    1: En algunos casos, puede valer la pena incluir una columna en un índice si esto significa que todas las columnas de su consulta están en el índice. Esto permite un escaneo de índice solamente, por lo que no necesita acceder a la tabla.

    2: Si tiene licencia para Diagnóstico y Ajuste, puede forzar el plan a omitir el escaneo con SQL Plan Management

    ADDEDNDA

    PD: los documentos que ha citado son de 9i. Eso es realmente viejo. Me quedaría con algo más reciente


    ¿Es una consulta con select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )realmente común? ¿Oracle no permite la sintaxis select count (distinct few_vals, many_vals, lots_vals ), que no hace ninguna concatenación de cadenas, no necesita que las columnas sean tipos de texto y no se basa en la ausencia de :caracteres?
    ypercubeᵀᴹ

    @ ypercubeᵀᴹ no puedes hacer nada count ( distinct x, y, z )en Oracle. Por lo tanto, debe hacer una subconsulta distinta y contar los resultados o una concatenación como la anterior. Simplemente lo hice aquí para forzar un acceso a la tabla (en lugar de escanear solo el índice) y solo tengo una fila en el resultado
    Chris Saxon

    1

    Hay más elementos de consulta que contribuyen a la decisión final sobre con qué debe comenzar y / o contener un índice compuesto además de la selectividad de la columna.

    por ejemplo:

    1. qué tipo de operador de consulta se está utilizando: si las consultas tienen operadores como
      ">,> =, <, <="
    2. Cuántas filas reales se esperan como resultado de la consulta: ¿El resultado de la consulta será la mayoría de las filas de la tabla?
    3. ¿Se está utilizando alguna función en la columna de la tabla durante la cláusula Where ?: si la consulta tiene alguna función UPPER, LOWER, TRIM, SUBSTRING utilizada en la columna que se utiliza en la condición WHERE.

    aún para mantener la conversación relevante, mi respuesta a continuación se aplica a la siguiente situación:

    1. "El 90% del tipo de consultas en una tabla dada tiene cláusula WHERE con operador ="
    2. "como máximo la consulta devuelve el 10% del total de filas en la tabla como resultado"
    3. "no se está utilizando ningún tipo de funciones en la columna de la tabla en la cláusula WHERE"
    4. "la mayoría de las columnas de tiempo en la cláusula WHERE utilizada son principalmente de tipo número,
      cadena"

    En mi experiencia, DBA debe tener en cuenta ambas cosas.

    Imaginemos que se aplica la única regla:

    1) Si creo un índice con la columna más selectiva en primer lugar, pero esa columna no es realmente utilizada por la mayoría de las consultas en esa tabla, entonces no sirve para db engine.

    2) Si creo un índice con la columna más utilizada en una consulta siendo la primera en el índice pero la columna tiene baja selectividad, entonces el rendimiento de mi consulta no será bueno.

    Enumeraré las columnas que se usan principalmente en el 90% de las consultas de la tabla. Luego ponga esos solo en el orden de mayor cardinalidad a menor cardinalidad.

    Usamos índices para mejorar el rendimiento de la consulta de lectura y ese flujo de trabajo (tipos de consulta de lectura) solo debería impulsar la creación del índice. De hecho, a medida que los datos crecen (miles de millones de filas), el índice comprimido puede ahorrar almacenamiento, pero seguro perjudica el rendimiento de la consulta de lectura.


    1

    En teoría, la columna más selectiva produce la búsqueda más rápida. Pero en el trabajo me topé con una situación en la que tenemos un índice compuesto de 3 partes con la parte más selectiva primero. (fecha, autor, compañía editorial, digamos, en ese orden, la tabla monitorea el pulgar hacia arriba en las publicaciones) y tengo una consulta que usa las 3 partes. Mysql usa de manera predeterminada el autor solo para omitir el índice compuesto que contiene la compañía y la fecha a pesar de estar presentes en mi consulta. Usé el índice de fuerza para usar el compuesto y la consulta en realidad fue más lenta. ¿Por qué sucedió eso? Te diré:

    Estaba seleccionando un rango en la fecha, por lo que a pesar de que la fecha es muy selectiva, el hecho de que lo estemos usando para escaneos de rango (aunque el rango es relativamente corto, 6 meses de 6 años de datos) hizo que el compuesto fuera dañino para mysql. Para usar el compuesto en ese caso particular, mysql tiene que tomar todos los artículos escritos desde los nuevos años y luego sumergirse en quién es el autor, y dado que el autor no ha escrito tantos artículos en comparación con otros autores, mysql prefirió encontrarlo. .

    En otro caso, la consulta se ejecutó mucho más rápido en el compuesto, el caso fue cuando un autor era muy popular y poseía la mayoría de los registros, la clasificación por fecha tenía sentido. Pero mysql no detectó automáticamente ese caso, tuve que forzar el índice ... Entonces, ya sabes, varía. Los escaneos de rango podrían inutilizar su columna selectiva. La distribución de los datos podría hacer que las columnas sean más selectivas para diferentes registros ...

    Lo que haría de manera diferente es cambiar la fecha (que de nuevo, en teoría es la más selectiva) a la derecha, ya que sé que realizaré un escaneo de rango ahora y eso marca la diferencia.


    1
    Si su consulta tuviera algo así WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p), un índice activado (author, publishing_company, date)o (publishing_company, author, date)activado sería mejor y se utilizaría, sin forzarlo.
    ypercubeᵀᴹ

    -2

    Diferentes casos para diferentes situaciones. Conoce tu objetivo; luego cree sus índices y ejecute planes de explicación para cada uno y tendrá la mejor respuesta para su situación.


    -2

    Del orden de las columnas en el índice en Ask Tom:

    Entonces, el orden de las columnas en su índice depende de CÓMO se escriben sus CONSULTAS. Desea poder usar el índice para tantas consultas como sea posible (para reducir el número total de índices que tiene), lo que impulsará el orden de las columnas. Nada más (la selectividad de aob no cuenta en absoluto).

    De acuerdo, que tenemos que ordenar las columnas en función de la cláusula where, pero la afirmación "(la selectividad de aob no cuenta en absoluto)" no es correcta. "". ("dónde cláusula")

    Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
    Licensed under cc by-sa 3.0 with attribution required.