Indici compositi: prima la colonna più selettiva?


17

Ho letto composite indexese sono un po 'confuso riguardo all'ordinazione. Questa documentazione (poco meno della metà) indica

In generale, è necessario inserire prima la colonna che si prevede verrà utilizzata per prima nell'indice.

Tuttavia, poco dopo dice

creare un indice composito mettendo prima la colonna più selettiva; cioè, la colonna con il maggior numero di valori.

Oracle lo dice anche qui in altre parole

Se tutte le chiavi sono usate ugualmente spesso nelle clausole WHERE, ordinare queste chiavi dalla più selettiva alla meno selettiva nell'istruzione CREATE INDEX migliora al meglio le prestazioni della query.

Tuttavia, ho trovato una risposta SO che dice diversamente. Dice

Disporre le colonne con la colonna meno selettiva per prima e l'ultima colonna selettiva per ultima. Nel caso di un pareggio di piombo con la colonna che è più probabile che venga utilizzato da solo.

La prima documentazione a cui ho fatto riferimento dice che dovresti prima andare dal più spesso usato, mentre la risposta SO dice che dovrebbe essere solo per il pareggio. Quindi differiscono anche per l'ordinazione.

Questa documentazione parla anche skip scanninge dice

La scansione con salto è vantaggiosa se ci sono pochi valori distinti nella colonna principale dell'indice composito e molti valori distinti nella chiave non leader dell'indice.

Un altro articolo dice

La colonna del prefisso dovrebbe essere la più discriminante e la più utilizzata nelle query

che ritengo più discriminante significherebbe più distintivo.

Tutta questa ricerca mi porta ancora alla stessa domanda; dovrebbe La colonna più selettiva deve essere la prima o l'ultima? La prima colonna dovrebbe essere la più utilizzata e solo la più selettiva in caso di pareggio?

Questi articoli sembrano contraddirsi a vicenda, ma offrono alcuni esempi. Da quello che ho raccolto, sembra essere più efficiente per least selective columnessere il primo nell'ordine se stai anticipando Index Skip Scans. Ma non sono davvero sicuro che sia corretto.


Risposte:


8

Da AskTom

(in 9i, c'è una nuova "scansione di salto dell'indice" - cerca quello lì per leggere su questo. Rende l'indice (a, b) O (b, a) utile in entrambi i casi di cui sopra a volte!)

Quindi, l'ordine delle colonne nel tuo indice dipende da come sono scritte le tue domande. Vuoi essere in grado di utilizzare l'indice per il maggior numero di query possibile (in modo da ridurre il numero complessivo di indici che hai) - ciò determinerà l'ordine delle colonne. Nient'altro (la selettività di a o b non conta affatto).

Uno degli argomenti per disporre le colonne nell'indice composito in ordine dai valori meno discriminanti (valori meno distinti) ai più discriminanti (valori più distinti) è per la compressione della chiave dell'indice.

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

Secondo le statistiche dell'indice, il primo indice è più comprimibile.

Un altro è il modo in cui l'indice viene utilizzato nelle tue query. Se le tue query utilizzano principalmente col1,

Ad esempio, se hai domande come-

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

    -quindi index(col1,col2)avrebbe funzionato meglio.

    Se le tue query utilizzano principalmente col2,

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

    -poi index(col2,col1) avrebbe funzionato meglio. Se tutte le tue query specificano sempre entrambe le colonne, non importa quale colonna viene prima nell'indice composito.

    In conclusione, le considerazioni chiave nell'ordinamento delle colonne dell'indice composito sono la compressione della chiave dell'indice e il modo in cui verrà utilizzato questo indice nelle query.

    Riferimenti:

  • Ordine delle colonne in indice
  • È meno efficiente avere colonne cardinali a bassa cardinalità in un indice (giusto)?
  • Index Skip Scan - L'ordine delle colonne dell'indice conta di più? (Segnale di pericolo)


  • 3

    Il più selettivo per primo è utile solo quando questa colonna è nella clausola WHERE effettiva.

    Quando SELEZIONA è di un gruppo più grande (meno selettivo), e quindi eventualmente di altri valori non indicizzati, un indice con colonne meno selettive può ancora essere utile (se c'è un motivo per non crearne un altro).

    Se c'è un tavolo INDIRIZZO, con

    STRADA DELLA CITTÀ DI PAESE, qualcos'altro ...

    indicizzazione STREET, CITY, COUNTRY produrrà le query più veloci con un nome di strada. Ma interrogando tutte le strade di una città, l'indice sarà inutile e la query probabilmente eseguirà una scansione completa della tabella.

    L'indicizzazione di COUNTRY, CITY, STREET potrebbe essere un po 'più lenta per le singole strade, ma l'indice può essere utilizzato per altre query, selezionando solo per paese e / o città.


    3

    Quando si sceglie l'ordine delle colonne dell'indice, la preoccupazione principale è:

    Ci sono predicati (uguaglianza) contro questa colonna nelle mie query?

    Se una colonna non viene mai visualizzata in una clausola where, non vale la pena indicizzarla (1)

    OK, quindi hai una tabella e query su ogni colonna. A volte più di uno.

    Come decidete cosa indicizzare?

    Diamo un'occhiata a un esempio. Ecco una tabella con tre colonne. Uno contiene 10 valori, un altro 1.000, gli ultimi 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     

    Questi sono numeri lasciati riempiti di zeri. Ciò contribuirà a chiarire in seguito la compressione.

    Quindi hai tre domande comuni:

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

    Cosa indicizzi?

    Un indice su just_vals è solo leggermente migliore di una scansione completa della tabella:

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

    Quindi è improbabile che valga la pena indicizzarlo da solo. Le query su lots_vals restituiscono poche righe (solo 1 in questo caso). Quindi vale sicuramente la pena indicizzarlo.

    Ma per quanto riguarda le query su entrambe le colonne?

    Dovresti indicizzare:

    ( few_vals, lots_vals )

    O

    ( lots_vals, few_vals )

    Domanda a trabocchetto!

    La risposta è nessuna delle due.

    Certo few_vals è una stringa lunga. Quindi puoi ottenere una buona compressione da esso. E potresti (potresti) ottenere una scansione di salto indice per le query usando (few_vals, lots_vals) che hanno solo predicati su lots_vals. Ma io non qui, anche se funziona notevolmente meglio di una scansione completa:

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

    Ti piace il gioco d'azzardo? (2)

    Quindi hai ancora bisogno di un indice con lots_vals come colonna principale. E almeno in questo caso l'indice composto (pochi, lotti) fa la stessa quantità di lavoro di uno solo su (lotti)

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

    Ci saranno casi in cui l'indice composto salva 1-2 IO. Ma vale la pena avere due indici per questo risparmio?

    E c'è un altro problema con l'indice composito. Confronta il fattore di clustering per i tre indici tra cui 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    

    Si noti che il fattore di clustering per pochi_lotti è 10 volte maggiore rispetto a lotti e lotti_few! E questo è in una tabella demo con un cluster perfetto per cominciare. Nei database del mondo reale è probabile che l'effetto sia peggiore.

    Quindi cosa c'è di male in questo?

    Il fattore di clustering è uno dei driver chiave che determinano quanto "attraente" sia un indice. Più è alto, meno è probabile che l'ottimizzatore lo scelga. Soprattutto se lots_vals non è in realtà univoco, ma normalmente ha ancora poche righe per valore. Se sei sfortunato, questo potrebbe essere sufficiente per far pensare all'ottimizzatore che una scansione completa è più economica ...

    OK, quindi gli indici compositi con pochi_vals e lots_vals hanno solo vantaggi di edge case.

    Che dire delle query che filtrano few_vals e many_vals?

    Gli indici a colonna singola offrono solo piccoli vantaggi. Ma combinati restituiscono pochi valori. Quindi un indice composito è una buona idea. Ma in che modo?

    Se ne posizioni alcuni per primi, comprimere la colonna iniziale renderà più piccola

    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 meno valori diversi nella colonna principale si comprime meglio. Quindi c'è un po 'meno lavoro per leggere questo indice. Ma solo leggermente. Ed entrambi sono già un buon pezzo più piccolo dell'originale (riduzione del 25% delle dimensioni).

    E puoi andare oltre e comprimere l'intero indice!

    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   

    Ora entrambi gli indici sono tornati alla stessa dimensione. Nota che questo sfrutta il fatto che esiste una relazione tra pochi e molti. Ancora una volta è improbabile che vedrai questo tipo di beneficio nel mondo reale.

    Finora abbiamo parlato solo di controlli di uguaglianza. Spesso con indici compositi avrai una disuguaglianza rispetto a una delle colonne. ad esempio query come "ottenere gli ordini / spedizioni / fatture per un cliente negli ultimi N giorni".

    Se si dispone di questo tipo di query, si desidera l'uguaglianza rispetto alla prima colonna dell'indice:

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

    Si noti che stanno utilizzando l'indice opposto.

    TL; DR

    • Le colonne con condizioni di uguaglianza dovrebbero andare per prime nell'indice.
    • Se hai più colonne con uguaglianze nella tua query, posizionando prima quella con il minor numero di valori diversi otterrai il miglior vantaggio di compressione
    • Mentre sono possibili scansioni di salto indice, devi essere sicuro che rimarrà un'opzione praticabile per il prossimo futuro
    • Gli indici compositi, comprese le colonne quasi uniche, offrono vantaggi minimi. Assicurati di aver davvero bisogno di salvare gli 1-2 IO

    1: In alcuni casi può valere la pena includere una colonna in un indice se ciò significa che tutte le colonne nella query sono nell'indice. Ciò consente una scansione solo dell'indice, quindi non è necessario accedere alla tabella.

    2: se si dispone di una licenza per Diagnostica e ottimizzazione, è possibile forzare il piano a saltare la scansione con SQL Plan Management

    ADDEDNDA

    PS: i documenti che hai citato lì sono di 9i. È davvero vecchio. Continuerei con qualcosa di più recente


    Una query con è select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )davvero comune? Oracle non consente la sintassi select count (distinct few_vals, many_vals, lots_vals )- che non fa alcuna concatenazione di stringhe, non ha bisogno che le colonne siano tipi di testo e non si basa sull'assenza di :carattere?
    ypercubeᵀᴹ

    @ ypercubeᵀᴹ non puoi farlo count ( distinct x, y, z )in Oracle. Quindi è necessario eseguire una subquery distinta e contare i risultati o una concatenazione come sopra. L'ho fatto qui per forzare un accesso al tavolo (piuttosto che scansionare solo l'indice) e ho solo una riga nel risultato
    Chris Saxon,

    1

    Ci sono più elementi di query che contribuiscono alla decisione finale su cosa dovrebbe iniziare e / o contenere un indice composito oltre alla selettività della colonna.

    per esempio:

    1. quale tipo di operatore di query viene utilizzato: se le query hanno operatori come
      ">,> =, <, <="
    2. Quante righe effettive sono previste a seguito della query: il risultato della query sarà la maggior parte delle righe della tabella.
    3. Qualche funzione viene utilizzata nella colonna della tabella durante la clausola Where: se la query ha una funzione UPPER, LOWER, TRIM, SUBSTRING utilizzata nella colonna utilizzata nella condizione WHERE?

    ma per mantenere la conversazione pertinente la mia risposta di seguito si applica alla seguente situazione:

    1. "Il 90% del tipo di query su una determinata tabella ha la clausola WHERE con operatore ="
    2. "al massimo la query restituisce il 10% delle righe totali nella tabella come risultato"
    3. "nessun tipo di funzione viene utilizzato nella colonna della tabella nella clausola WHERE"
    4. "la maggior parte delle colonne di tempo nella clausola WHERE utilizzata sono principalmente di tipo numero,
      stringa"

    Nella mia esperienza, sia DBA che dovrebbe essere consapevole.

    Immaginiamo che venga applicata l'unica regola:

    1) Se creo un indice con la colonna più selettiva prima, ma quella colonna non è effettivamente utilizzata dalla maggior parte delle query su quella tabella di quanto non sia utile per il motore db.

    2) Se creo un indice con la colonna più utilizzata in una query, essendo la prima nell'indice, ma la colonna ha una selettività bassa rispetto alle prestazioni della mia query non sarà buona.

    Elencherò le colonne utilizzate principalmente nel 90% delle query della tabella. Quindi metti quelli solo nell'ordine della maggior cardinalità alla minima cardinalità.

    Utilizziamo gli indici per migliorare le prestazioni della query di lettura e quel flusso di lavoro (tipi di query di lettura) dovrebbe guidare solo la creazione dell'indice. Infatti, con l'aumentare dei dati (miliardi di righe) l'indice compresso può risparmiare spazio di archiviazione, ma comprometterebbe sicuramente le prestazioni della query di lettura.


    1

    In teoria la colonna più selettiva produce la ricerca più veloce. Ma al lavoro mi sono appena imbattuto in una situazione in cui abbiamo un indice composito di 3 parti con la parte più selettiva per prima. (data, autore, casa editrice diciamo, in questo ordine, la tabella controlla il pollice in alto sui post) e ho una query che utilizza tutte e 3 le parti. Per impostazione predefinita, Mysql utilizza l'autore sull'indice ignorando l'indice composito contenente la società e la data nonostante siano presenti nella mia query. Ho usato l'indice di forza per usare il composito e la query è stata effettivamente eseguita più lentamente. Perché è successo? Ti dirò:

    Stavo selezionando un intervallo alla data, quindi nonostante la data sia altamente selettiva, il fatto che lo stiamo usando per le scansioni dell'intervallo (anche se l'intervallo è relativamente breve, 6 mesi su 6 anni di dati) ha reso dannoso il composito per mysql. Per usare il composito in quel caso particolare, mysql deve prendere tutti gli articoli scritti da nuovi anni e poi tuffarsi in chi è l'autore, e dato che l'autore non ha scritto molti articoli rispetto ad altri autori, mysql ha preferito trovare quell'autore .

    In un altro caso la query ha funzionato molto più velocemente sul composito, il caso era quando un autore era estremamente popolare e possedeva la maggior parte dei record, l'ordinamento per data aveva senso. Ma mysql non ha rilevato automaticamente quel caso, ho dovuto forzare l'indice ... Quindi, sai, varia. Le scansioni dell'intervallo potrebbero rendere inutilizzabile la colonna selettiva. La distribuzione dei dati potrebbe rendere i casi in cui le colonne sono più selettive per diversi record ...

    Quello che farei diversamente è spostare la data (che di nuovo, in teoria è la più selettiva) a destra, poiché so che eseguirò una scansione di intervallo su di essa ora e questo fa la differenza.


    1
    Se la tua query avesse qualcosa del genere, WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)un indice su (author, publishing_company, date)o su (publishing_company, author, date)sarebbe meglio e verrebbe usato - senza forzarlo.
    ypercubeᵀᴹ

    -2

    Casi diversi per situazioni diverse. Conosci il tuo obiettivo; quindi crea i tuoi indici ed esegui i piani di spiegazione per ciascuno e avrai la migliore risposta per la tua situazione.


    -2

    Dalla fine della colonna in Index su Ask Tom:

    Quindi, l'ordine delle colonne nel tuo indice dipende da come sono scritte le tue domande. Vuoi essere in grado di utilizzare l'indice per il maggior numero di query possibile (in modo da ridurre il numero complessivo di indici che hai) - ciò determinerà l'ordine delle colonne. Nient'altro (la selettività di a o b non conta affatto).

    D'accordo, dobbiamo ordinare le colonne in base alla clausola where, ma l'istruzione "(la selettività di a o b non conta affatto)" non è corretta.) ". Le colonne più selettive dovrebbero essere in testa se è soddisfatto il primo ruolo ("dove la clausola")

    Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
    Licensed under cc by-sa 3.0 with attribution required.