Index composites: la colonne la plus sélective en premier?


17

J'ai lu composite indexeset je suis un peu confus au sujet de la commande. Cette documentation (un peu moins de la moitié) indique

En général, vous devez d'abord placer la colonne devant être utilisée le plus souvent dans l'index.

Cependant, peu de temps après

créer un index composite mettant la colonne la plus sélective en premier; c'est-à-dire la colonne avec le plus de valeurs.

Oracle le dit aussi ici en d'autres termes

Si toutes les clés sont utilisées de la même manière dans les clauses WHERE, le classement de ces clés de la plus sélective à la moins sélective dans l'instruction CREATE INDEX améliore au mieux les performances de la requête.

Cependant, j'ai trouvé une réponse SO qui dit différemment. Ça dit

Disposez les colonnes avec la colonne la moins sélective en premier et la colonne la plus sélective en dernier. Dans le cas d'un lien avec la colonne qui est plus susceptible d'être utilisé seul.

La première documentation que j'ai référencée dit que vous devez d'abord passer par le plus souvent utilisé alors que la réponse SO dit que cela ne devrait être que pour le bris d'égalité. Ensuite, ils diffèrent également sur la commande.

Cette documentation parle également skip scanninget dit

Le saut de balayage est avantageux s'il y a peu de valeurs distinctes dans la colonne de tête de l'index composite et de nombreuses valeurs distinctes dans la clé non-leader de l'index.

Un autre article dit

La colonne de préfixe doit être la plus discriminante et la plus utilisée dans les requêtes

qui, selon moi, est le plus discriminant et le plus distinctif.

Toutes ces recherches m'amènent toujours à la même question; La colonne la plus sélective doit-elle être la première ou la dernière? La première colonne doit-elle être la plus utilisée et la plus sélective lors d'un bris d'égalité?

Ces articles semblent se contredire, mais ils offrent quelques exemples. D'après ce que j'ai rassemblé, il semble plus efficace least selective columnd'être le premier dans la commande si vous prévoyez Index Skip Scans. Mais je ne suis pas vraiment sûr que ce soit correct.


Réponses:


8

De AskTom

(dans 9i, il y a un nouveau "saut d'index" - recherchez-le pour en savoir plus. Il rend parfois l'index (a, b) OU (b, a) utile dans les deux cas ci-dessus!)

Ainsi, l'ordre des colonnes dans votre index dépend de la façon dont vos requêtes sont écrites. Vous voulez être en mesure d'utiliser l'index pour autant de requêtes que vous le pouvez (afin de réduire le nombre total d'index que vous avez) - cela déterminera l'ordre des colonnes. Rien d'autre (la sélectivité de a ou b ne compte pas du tout).

L'un des arguments pour organiser les colonnes dans l'index composite dans l'ordre des moins discriminantes (valeurs moins distinctes) aux plus discriminantes (valeurs plus distinctes) est la compression des clés d'index.

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

Selon les statistiques de l'indice, le premier indice est plus compressible.

Un autre est la façon dont l'index est utilisé dans vos requêtes. Si vos requêtes utilisent principalement col1,

Par exemple, si vous avez des requêtes comme-

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

    -Alors, index(col1,col2)cela fonctionnerait mieux.

    Si vos requêtes utilisent principalement col2,

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

    -Alors, index(col2,col1)cela fonctionnerait mieux. Si toutes vos requêtes spécifient toujours les deux colonnes, peu importe la colonne qui arrive en premier dans l'index composite.

    En conclusion, les considérations clés dans l'ordre des colonnes de l'index composite sont la compression des clés d'index et la façon dont vous allez utiliser cet index dans vos requêtes.

    Les références:

  • Ordre des colonnes dans l'index
  • Il est moins efficace d'avoir des colonnes de tête à faible cardinalité dans un index (à droite)?
  • Index Skip Scan - L'ordre des colonnes d'index importe-t-il encore? (Panneau d'avertissement)


  • 3

    La plus sélective en premier n'est utile que lorsque cette colonne se trouve dans la clause WHERE réelle.

    Lorsque le SELECT est par un groupe plus grand (moins sélectif), puis éventuellement par d'autres valeurs non indexées, un index avec des colonnes moins sélectives peut toujours être utile (s'il y a une raison de ne pas en créer un autre).

    S'il y a une ADRESSE de table, avec

    COUNTRY CITY STREET, autre chose ...

    l'indexation de STREET, CITY, COUNTRY produira les requêtes les plus rapides avec un nom de rue. Mais en interrogeant toutes les rues d'une ville, l'index sera inutile et la requête effectuera probablement une analyse complète de la table.

    L'indexation COUNTRY, CITY, STREET peut être un peu plus lente pour les rues individuelles, mais l'index peut être utilisé pour d'autres requêtes, en sélectionnant uniquement par pays et / ou ville.


    3

    Lors du choix de l'ordre des colonnes d'index, la préoccupation principale est:

    Y a-t-il des prédicats (égalité) contre cette colonne dans mes requêtes?

    Si une colonne n'apparaît jamais dans une clause where, cela ne vaut pas l'indexation (1)

    OK, vous avez donc un tableau et des requêtes pour chaque colonne. Parfois plus d'un.

    Comment décidez-vous quoi indexer?

    Regardons un exemple. Voici un tableau à trois colonnes. L'une contient 10 valeurs, 1000 autres, les 10 000 dernières:

    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     

    Ce sont des chiffres remplis de zéros. Cela aidera à faire le point sur la compression plus tard.

    Vous avez donc trois requêtes courantes:

    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'indexez-vous?

    Un index sur seulement few_vals n'est que légèrement meilleur qu'un scan de table complet:

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

    Il ne vaut donc probablement pas la peine d'être indexé seul. Les requêtes sur lots_vals renvoient quelques lignes (seulement 1 dans ce cas). Donc, cela vaut vraiment la peine d'être indexé.

    Mais qu'en est-il des requêtes sur les deux colonnes?

    Devriez-vous indexer:

    ( few_vals, lots_vals )

    OU

    ( lots_vals, few_vals )

    Question piège!

    La réponse n'est ni l'un ni l'autre.

    Bien sûr, few_vals est une longue chaîne. Vous pouvez donc en tirer une bonne compression. Et vous (pourriez) obtenir une analyse de saut d'index pour les requêtes en utilisant (few_vals, lots_vals) qui n'ont que des prédicats sur lots_vals. Mais je ne le fais pas ici, même s'il fonctionne nettement mieux qu'une analyse complète:

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

    Aimez-vous le jeu? (2)

    Vous avez donc toujours besoin d'un index avec lots_vals comme colonne de tête. Et au moins dans ce cas, l'indice composé (peu, beaucoup) fait la même quantité de travail qu'un seul sur (lots)

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

    Il y aura des cas où l'indice composé vous fera économiser 1 à 2 E / S. Mais vaut-il la peine d'avoir deux index pour cette économie?

    Et il y a un autre problème avec l'index composite. Comparez le facteur de regroupement pour les trois index, y compris 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    

    Notez que le facteur de clustering pour few_lots est 10 fois plus élevé que pour lots et lots_few! Et ceci est dans une table de démonstration avec un clustering parfait pour commencer. Dans les bases de données du monde réel, l'effet est probablement pire.

    Alors qu'est-ce qui est si mauvais à ce sujet?

    Le facteur de regroupement est l'un des principaux facteurs déterminant le caractère «attrayant» d'un indice. Plus il est élevé, moins l'optimiseur est susceptible de le choisir. Surtout si les lots_vals ne sont pas réellement uniques, mais ont normalement quelques lignes par valeur. Si vous n'avez pas de chance, cela pourrait suffire à faire croire à l'optimiseur qu'une analyse complète est moins chère ...

    OK, donc les index composites avec few_vals et lots_vals n'ont que des avantages de cas de bord.

    Qu'en est-il des requêtes filtrant plusieurs-valeurs et plusieurs-valeurs?

    Les index à colonnes uniques n'offrent que de petits avantages. Mais combinés, ils renvoient peu de valeurs. Un indice composite est donc une bonne idée. Mais dans quel sens?

    Si vous en placez quelques-uns en premier, la compression de la colonne de tête la réduira

    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      

    Avec moins de valeurs différentes dans la première colonne, la compression est meilleure. Il y a donc un peu moins de travail pour lire cet index. Mais seulement légèrement. Et les deux sont déjà un bon morceau plus petit que l'original (diminution de 25% de la taille).

    Et vous pouvez aller plus loin et compresser tout l'index!

    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   

    Maintenant, les deux index sont de retour à la même taille. Notez que cela profite du fait qu'il existe une relation entre peu et beaucoup. Encore une fois, il est peu probable que vous constatiez ce genre d'avantages dans le monde réel.

    Jusqu'à présent, nous n'avons parlé que des contrôles d'égalité. Souvent, avec des index composites, vous aurez une inégalité par rapport à l'une des colonnes. par exemple des requêtes telles que "obtenir les commandes / expéditions / factures pour un client au cours des N derniers jours".

    Si vous avez ces types de requêtes, vous voulez l'égalité par rapport à la première colonne de l'index:

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

    Notez qu'ils utilisent l'index opposé.

    TL; DR

    • Les colonnes avec des conditions d'égalité doivent aller en premier dans l'index.
    • Si vous avez plusieurs colonnes avec des égalités dans votre requête, placer d'abord celle avec le moins de valeurs différentes donnera le meilleur avantage de compression
    • Bien que les sauts d'index soient possibles, vous devez être sûr que cela restera une option viable dans un avenir prévisible.
    • Les index composites comprenant des colonnes quasi uniques offrent des avantages minimes. Assurez-vous que vous avez vraiment besoin de sauvegarder les 1-2 IO

    1: Dans certains cas, il peut être utile d'inclure une colonne dans un index si cela signifie que toutes les colonnes de votre requête se trouvent dans l'index. Cela permet une analyse d'index uniquement, vous n'avez donc pas besoin d'accéder à la table.

    2: Si vous avez une licence pour les diagnostics et l'optimisation, vous pouvez forcer le plan à un saut d'analyse avec SQL Plan Management

    ADDEDNDA

    PS - les documents que vous avez cités proviennent de 9i. C'est vraiment vieux. Je m'en tiendrai à quelque chose de plus récent


    Une requête est-elle select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )vraiment courante? Oracle n'autorise-t-il pas la syntaxe select count (distinct few_vals, many_vals, lots_vals )- qui ne fait aucune concaténation de chaîne, n'a pas besoin que les colonnes soient des types de texte et ne repose pas sur l'absence de :caractère?
    ypercubeᵀᴹ

    @ ypercubeᵀᴹ vous ne pouvez pas faire un count ( distinct x, y, z )dans Oracle. Vous devez donc effectuer une sous-requête distincte et compter les résultats ou une concaténation comme ci-dessus. Je viens de le faire ici pour forcer un accès à la table (plutôt que pour un scan d'index uniquement) et n'avoir qu'une ligne dans le résultat
    Chris Saxon

    1

    Il y a plus d'éléments de requête qui contribuent à la décision finale sur ce qu'un index composite doit commencer et / ou contenir en plus de la sélectivité de la colonne.

    par exemple:

    1. quel type d'opérateur de requête est utilisé: si les requêtes ont des opérateurs comme
      ">,> =, <, <="
    2. Combien de lignes réelles attendues à la suite de la requête: le résultat de la requête sera-t-il la plupart des lignes de la table?
    3. Des fonctions sont-elles utilisées sur la colonne de la table pendant la clause Where: si la requête a une fonction UPPER, LOWER, TRIM, SUBSTRING utilisée sur la colonne utilisée dans la condition WHERE.

    pour garder la conversation pertinente, ma réponse ci-dessous s'applique à la situation suivante:

    1. "Le type de requêtes à 90% sur une table donnée a une clause WHERE avec l'opérateur ="
    2. "au maximum, la requête renvoie les 10% du nombre total de lignes du tableau"
    3. "aucun type de fonction n'est utilisé sur la colonne de table dans la clause WHERE"
    4. "la plupart des colonnes de temps dans la clause WHERE utilisée sont principalement de type nombre,
      chaîne"

    D'après mon expérience, c'est à la fois que DBA devrait être attentif.

    Imaginons que la seule règle soit appliquée:

    1) Si je crée un index avec la colonne la plus sélective en premier mais que cette colonne n'est pas réellement utilisée par la plupart des requêtes sur cette table, cela ne sert à rien pour le moteur db.

    2) Si je crée un index avec la colonne la plus utilisée dans une requête en premier dans l'index mais que la colonne a une faible sélectivité, les performances de ma requête ne seront pas bonnes.

    Je vais lister les colonnes qui sont principalement utilisées dans 90% des requêtes de table. Ensuite, mettez ceux-ci uniquement dans l'ordre de la plus grande cardinalité à la moindre cardinalité.

    Nous utilisons des index pour améliorer les performances de la requête de lecture et ce flux de travail (types de requête de lecture) doit uniquement conduire la création de l'index. En fait, au fur et à mesure que les données augmentent (des milliards de lignes), l'index compressé peut économiser du stockage, mais nuire à la performance des requêtes de lecture.


    1

    En théorie, la colonne la plus sélective donne la recherche la plus rapide. Mais au travail, je suis juste tombé sur une situation où nous avons un indice composite de 3 parties avec la partie la plus sélective en premier. (date, auteur, maison d'édition, disons, dans cet ordre, le tableau surveille les pouces vers le haut sur les messages) et j'ai une requête qui utilise les 3 parties. Mysql utilise par défaut l'index onlny de l'auteur en sautant l'index composite contenant la société et la date bien qu'ils soient présents dans ma requête. J'ai utilisé force index pour utiliser le composite et la requête s'est en fait déroulée plus lentement. Pourquoi est-ce arrivé? Je vais vous dire:

    Je sélectionnais une plage à la date, donc malgré une date très sélective, le fait que nous l'utilisions pour des analyses de plage (même si la plage est relativement courte, 6 mois sur 6 ans de données) a rendu le composite nocif pour mysql. Pour utiliser le composite dans ce cas particulier, mysql doit récupérer tous les articles écrits depuis les nouvelles années, puis plonger dans qui est l'auteur, et étant donné que l'auteur n'a pas écrit autant d'articles par rapport à d'autres auteurs, mysql a préféré simplement trouver cet auteur .

    Dans un autre cas, la requête a été exécutée beaucoup plus rapidement sur le composite, dans le cas où un auteur était extrêmement populaire et possédait la plupart des enregistrements, le tri par date étant logique. Mais mysql n'a pas détecté automatiquement ce cas, j'ai dû forcer l'index ... Donc vous savez, ça varie. Les analyses de plage peuvent rendre votre colonne sélective inutile. La distribution des données pourrait rendre les cas où les colonnes sont plus sélectives pour différents enregistrements ...

    Ce que je ferais différemment, c'est de déplacer la date (qui encore une fois, en théorie, est la plus sélective) vers la droite, car je sais que je vais effectuer un balayage de plage maintenant et cela fait une différence.


    1
    Si votre requête avait quelque chose comme ça, WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)alors un index on (author, publishing_company, date)ou on (publishing_company, author, date)serait mieux et serait utilisé - sans le forcer.
    ypercubeᵀᴹ

    -2

    Différents cas pour différentes situations. Connaissez votre objectif; puis créez vos index et exécutez des plans pour chacun et vous aurez votre meilleure réponse à votre situation.


    -2

    De l' ordre des colonnes dans l'index sur Ask Tom:

    Ainsi, l'ordre des colonnes dans votre index dépend de la façon dont vos requêtes sont écrites. Vous voulez être en mesure d'utiliser l'index pour autant de requêtes que vous le pouvez (afin de réduire le nombre total d'index que vous avez) - cela déterminera l'ordre des colonnes. Rien d'autre (la sélectivité de a ou b ne compte pas du tout).

    D'accord, que nous devons ordonner les colonnes en fonction de la clause where, mais la déclaration "(la sélectivité de a ou b ne compte pas du tout)" n'est pas correcte.) ". Les colonnes les plus sélectives doivent être en tête si elles sont satisfaites premier rôle ("clause where")

    En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
    Licensed under cc by-sa 3.0 with attribution required.