Indeks komposit: Kolom paling selektif dulu?

17

Saya sudah membaca tentang composite indexesdan saya sedikit bingung tentang memesan. Dokumentasi ini (sedikit kurang dari setengah jalan) mengatakan

Secara umum, Anda harus meletakkan kolom yang diharapkan paling sering digunakan pertama kali dalam indeks.

Namun, tak lama setelah itu dikatakan

buat indeks komposit dengan menempatkan kolom paling selektif terlebih dahulu; yaitu kolom dengan nilai terbanyak.

Oracle juga mengatakannya di sini dengan kata lain

Jika semua kunci digunakan dalam klausa WHERE sama seringnya, maka memesan kunci ini dari yang paling selektif ke yang paling selektif dalam pernyataan CREATE INDEX terbaik meningkatkan kinerja permintaan.

Namun, saya telah menemukan jawaban SO yang mengatakan berbeda. Ia mengatakan

Atur kolom dengan kolom paling selektif terlebih dahulu dan kolom paling selektif terakhir. Dalam kasus dasi timbal dengan kolom yang lebih mungkin digunakan sendiri.

Dokumentasi pertama yang saya referensikan mengatakan bahwa Anda harus pergi dulu dengan yang paling sering digunakan sedangkan jawaban SO mengatakan bahwa seharusnya hanya untuk melanggar dasi. Kemudian mereka juga berbeda dalam pemesanan.

Dokumentasi ini juga berbicara tentang skip scanningdan mengatakan

Melewati pemindaian menguntungkan jika ada beberapa nilai berbeda di kolom terkemuka indeks komposit dan banyak nilai berbeda dalam kunci nonleading indeks.

Artikel lain mengatakan

Kolom awalan harus menjadi yang paling diskriminatif dan paling banyak digunakan dalam kueri

yang saya percaya paling diskriminatif berarti paling khas.

Semua penelitian ini masih mengarahkan saya ke pertanyaan yang sama; haruskah kolom paling selektif menjadi yang pertama atau terakhir? Haruskah kolom pertama menjadi yang paling banyak digunakan dan hanya yang paling selektif pada tie-break?

Artikel-artikel ini tampaknya saling bertentangan, tetapi mereka menawarkan beberapa contoh. Dari apa yang telah saya kumpulkan, tampaknya lebih efisien untuk least selective columnmenjadi yang pertama dalam memesan jika Anda mengantisipasi Index Skip Scans. Tetapi saya tidak begitu yakin apakah itu benar.

Eric
sumber

Jawaban:

8

Dari AskTom

(dalam 9i, ada "indeks lewati pemindaian" baru - cari yang ada di sana untuk membaca tentang itu. Itu membuat indeks (a, b) ATAU (b, a) berguna dalam kedua kasus di atas kadang-kadang!)

Jadi, urutan kolom dalam indeks Anda tergantung pada BAGAIMANA QUERIES ANDA ditulis. Anda ingin dapat menggunakan indeks untuk sebanyak mungkin pertanyaan (sehingga dapat mengurangi seluruh jumlah indeks yang Anda miliki) - yang akan menggerakkan urutan kolom. Tidak ada yang lain (selektivitas a atau b tidak dihitung sama sekali).

Salah satu argumen untuk mengatur kolom dalam indeks komposit dalam urutan dari yang paling tidak diskriminatif (nilai yang berbeda) ke yang paling diskriminatif (nilai yang lebih berbeda) adalah untuk kompresi kunci indeks.

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

Menurut statistik indeks, indeks pertama lebih kompresif.

Lain adalah bagaimana indeks digunakan dalam permintaan Anda. Jika sebagian besar pertanyaan Anda menggunakan col1,

Misalnya, jika Anda memiliki pertanyaan seperti-

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

    -Kemudian index(col1,col2)akan tampil lebih baik.

    Jika sebagian besar pertanyaan Anda menggunakan col2,

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

    -Kemudian index(col2,col1)akan tampil lebih baik. Jika semua pertanyaan Anda selalu menentukan kedua kolom, maka tidak masalah kolom mana yang lebih dulu berada di indeks komposit.

    Sebagai kesimpulan, pertimbangan utama dalam pemesanan kolom indeks komposit adalah kompresi kunci indeks dan bagaimana Anda akan menggunakan indeks ini dalam permintaan Anda.

    Referensi:

  • Urutan kolom dalam Indeks
  • Kurang Efisien Untuk Memiliki Kardinalitas Rendah Memimpin Kolom Dalam Suatu Indeks (Kanan)?
  • Indeks Lewati Pemindaian - Apakah Kolom Indeks Memesan Lagi? (Tanda peringatan)

  • JSapkota
    sumber
    3

    Paling selektif pertama hanya berguna ketika kolom ini berada di klausa WHERE yang sebenarnya.

    Ketika SELECT adalah dengan kelompok yang lebih besar (kurang selektif), dan kemudian mungkin dengan nilai lain yang tidak diindeks, indeks dengan kolom yang kurang selektif mungkin masih berguna (jika ada alasan untuk tidak membuat yang lain).

    Jika ada ALAMAT tabel, dengan

    JALAN KOTA NEGARA, sesuatu yang lain ...

    pengindeksan STREET, CITY, COUNTRY akan menghasilkan kueri tercepat dengan nama jalan. Tetapi menanyakan semua jalan kota, indeks tidak akan berguna, dan kueri kemungkinan akan melakukan pemindaian tabel penuh.

    Pengindeksan NEGARA, KOTA, JALAN mungkin sedikit lebih lambat untuk setiap jalan, tetapi indeks dapat digunakan untuk kueri lain, hanya memilih menurut negara dan / atau kota.

    Erik Hart
    sumber
    3

    Saat memilih urutan kolom indeks, perhatian utama adalah:

    Apakah ada (kesetaraan) predikat terhadap kolom ini di kueri saya?

    Jika sebuah kolom tidak pernah muncul dalam klausa where, itu tidak layak diindeks (1)

    OK, jadi Anda punya tabel dan kueri terhadap setiap kolom. Terkadang lebih dari satu.

    Bagaimana Anda memutuskan apa yang akan diindeks?

    Mari kita lihat sebuah contoh. Berikut adalah tabel dengan tiga kolom. Satu memiliki 10 nilai, 1.000 lainnya, 10.000 terakhir:

    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     

    Ini adalah angka yang dibiarkan penuh dengan nol. Ini akan membantu menjelaskan tentang kompresi nanti.

    Jadi, Anda memiliki tiga pertanyaan umum:

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

    Apa yang Anda indeks?

    Indeks hanya pada few_vals hanya sedikit lebih baik daripada pemindaian tabel lengkap:

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

    Jadi tidak mungkin untuk mengindeks sendiri. Kueri pada lots_vals mengembalikan beberapa baris (hanya 1 dalam kasus ini). Jadi ini pasti layak diindeks.

    Tetapi bagaimana dengan pertanyaan terhadap kedua kolom?

    Haruskah Anda mengindeks:

    ( few_vals, lots_vals )

    ATAU

    ( lots_vals, few_vals )

    Pertanyaan jebakan!

    Jawabannya adalah tidak.

    Tentu, few_vals adalah string yang panjang. Jadi Anda bisa mendapatkan kompresi yang baik dari itu. Dan Anda (mungkin) mendapatkan pemindaian lompatan indeks untuk kueri menggunakan (few_vals, lots_vals) yang hanya memiliki predikat pada lots_vals. Tapi saya tidak di sini, meskipun kinerjanya jauh lebih baik daripada pemindaian penuh:

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

    Apakah Anda suka judi? (2)

    Jadi, Anda masih memerlukan indeks dengan lots_vals sebagai kolom utama. Dan setidaknya dalam hal ini indeks gabungan (beberapa, lot) melakukan jumlah pekerjaan yang sama dengan satu pada hanya (banyak)

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

    Akan ada kasus di mana indeks gabungan menghemat 1-2 IOs. Tetapi apakah layak memiliki dua indeks untuk penghematan ini?

    Dan ada masalah lain dengan indeks komposit. Bandingkan faktor pengelompokan untuk tiga indeks termasuk 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    

    Perhatikan bahwa faktor pengelompokan untuk few_lots adalah 10x lebih tinggi daripada untuk banyak dan banyak_few! Dan ini ada di tabel demo dengan pengelompokan yang sempurna untuk memulai. Dalam database dunia nyata pengaruhnya mungkin lebih buruk.

    Jadi apa yang buruk tentang itu?

    Faktor pengelompokan adalah salah satu pendorong utama yang menentukan seberapa "menarik" suatu indeks. Semakin tinggi, semakin kecil kemungkinan pengoptimal untuk memilihnya. Terutama jika lots_vals sebenarnya tidak unik, tetapi biasanya masih memiliki beberapa baris per nilai. Jika Anda kurang beruntung ini bisa cukup untuk membuat pengoptimal berpikir pemindaian lengkap lebih murah ...

    OK, jadi indeks gabungan dengan few_vals dan lots_vals hanya memiliki manfaat tepi kasus.

    Bagaimana dengan kueri yang memfilter few_vals dan many_vals?

    Indeks kolom tunggal hanya memberikan manfaat kecil. Tetapi gabungan mereka mengembalikan beberapa nilai. Jadi indeks komposit adalah ide yang bagus. Tapi ke arah mana?

    Jika Anda menempatkan sedikit lebih dulu, mengompresi kolom terkemuka akan membuatnya lebih kecil

    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      

    Dengan nilai yang berbeda lebih sedikit di kolom terkemuka kompres lebih baik. Jadi ada sedikit pekerjaan untuk membaca indeks ini. Namun hanya sedikit. Dan keduanya sudah merupakan bagian yang baik lebih kecil dari yang asli (ukuran 25% berkurang).

    Dan Anda dapat melangkah lebih jauh dan kompres seluruh indeks!

    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   

    Sekarang kedua indeks kembali ke ukuran yang sama. Perhatikan ini mengambil keuntungan dari fakta ada hubungan antara sedikit dan banyak. Sekali lagi tidak mungkin Anda akan melihat manfaat semacam ini di dunia nyata.

    Sejauh ini kami hanya berbicara tentang pemeriksaan kesetaraan. Seringkali dengan indeks komposit Anda akan memiliki ketimpangan terhadap salah satu kolom. mis. pertanyaan seperti "dapatkan pesanan / pengiriman / faktur untuk pelanggan dalam N hari terakhir".

    Jika Anda memiliki pertanyaan seperti ini, Anda ingin persamaan terhadap kolom pertama dari indeks:

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

    Perhatikan mereka menggunakan indeks yang berlawanan.

    TL; DR

    • Kolom dengan kondisi persamaan harus lebih dulu dalam indeks.
    • Jika Anda memiliki beberapa kolom dengan persamaan dalam kueri Anda, menempatkan yang dengan nilai berbeda paling sedikit terlebih dahulu akan memberikan keuntungan kompresi terbaik
    • Meskipun pemindaian lompatan indeks dimungkinkan, Anda harus yakin ini akan tetap menjadi opsi yang layak untuk masa mendatang
    • Indeks komposit termasuk kolom hampir unik memberikan manfaat minimal. Pastikan Anda benar-benar perlu menyelamatkan 1-2 IO

    1: Dalam beberapa kasus mungkin ada baiknya menyertakan kolom dalam indeks jika ini berarti semua kolom dalam kueri Anda ada dalam indeks. Ini memungkinkan hanya pemindaian indeks, sehingga Anda tidak perlu mengakses tabel.

    2: Jika Anda memiliki lisensi untuk Diagnostik dan Tuning, Anda bisa memaksa rencana untuk melewati pemindaian dengan SQL Plan Management

    ADDEDNDA

    PS - dokumen yang Anda kutip dari 9i. Itu benar-benar tua. Saya akan tetap dengan sesuatu yang lebih baru

    Chris Saxon
    sumber
    Apakah pertanyaan dengan select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )benar - benar umum? Tidakkah Oracle mengizinkan sintaksis select count (distinct few_vals, many_vals, lots_vals )- yang tidak melakukan penggabungan string, tidak memerlukan kolom untuk menjadi tipe teks dan tidak bergantung pada ketiadaan :karakter?
    ypercubeᵀᴹ
    @ ypercubeᵀᴹ Anda tidak dapat melakukannya count ( distinct x, y, z )di Oracle. Jadi, Anda perlu melakukan subquery yang berbeda dan menghitung hasil atau gabungan seperti di atas. Saya hanya melakukannya di sini untuk memaksa akses tabel (daripada hanya memindai indeks) dan hanya memiliki satu baris dalam hasilnya
    Chris Saxon
    1

    Ada lebih banyak elemen kueri yang berkontribusi pada keputusan akhir tentang apa yang harus dimulai dengan Indeks Komposit dan / atau mengandung selain selektivitas kolom.

    sebagai contoh:

    1. apa jenis operator permintaan yang digunakan: Jika permintaan memiliki operator seperti
      ">,> =, <, <="
    2. Berapa banyak baris aktual yang diharapkan sebagai hasil dari kueri: Apakah hasil kueri akan menjadi sebagian besar baris dari tabel.
    3. Apakah ada fungsi yang digunakan pada kolom tabel selama klausa mana: Jika kueri memiliki fungsi UPPER, RENDAH, TRIM, SUBSTRING digunakan pada kolom yang digunakan dalam kondisi WHERE.

    namun untuk menjaga agar percakapan tetap relevan, jawaban saya di bawah ini berlaku untuk situasi berikut:

    1. "90% jenis kueri pada tabel yang diberikan memiliki WHERE Clause with operator ="
    2. "paling banyak kueri mengembalikan 10% total baris dalam tabel sebagai hasilnya"
    3. "tidak ada fungsi yang digunakan pada kolom tabel dalam klausa WHERE"
    4. "Sebagian besar kolom waktu di WHERE Klausa yang digunakan sebagian besar adalah tipe nomor,
      string"

    Dalam pengalaman saya, itu adalah DBA yang harus diperhatikan.

    Mari kita bayangkan satu-satunya aturan yang diterapkan:

    1) Jika saya membuat indeks dengan kolom paling selektif menjadi yang pertama tetapi kolom itu tidak benar-benar digunakan oleh sebagian besar pertanyaan pada tabel itu daripada tidak digunakan untuk mesin db.

    2) Jika saya membuat indeks dengan kolom yang paling banyak digunakan dalam kueri menjadi yang pertama dalam indeks tetapi kolom memiliki selektivitas yang rendah daripada juga kinerja permintaan saya tidak akan baik.

    Saya akan mencantumkan kolom yang sebagian besar digunakan dalam 90% kueri tabel. Kemudian, letakkan semua itu hanya dalam urutan kardinalitas paling rendah hingga kardinalitas terendah.

    Kami menggunakan indeks untuk meningkatkan kinerja kueri baca dan alur kerja itu (jenis kueri baca) hanya akan mendorong pembuatan indeks. Bahkan ketika data tumbuh (miliaran baris) indeks terkompresi dapat menghemat penyimpanan tetapi tentu saja merusak kinerja permintaan baca.

    Anup Shah
    sumber
    1

    Secara teori, kolom paling selektif menghasilkan pencarian tercepat. Tetapi di tempat kerja saya hanya tersandung pada situasi di mana kita memiliki indeks komposit 3 bagian dengan bagian paling selektif terlebih dahulu. (tanggal, penulis, perusahaan penerbitan katakanlah, dalam urutan itu, monitor meja diacungi jempol pada posting) dan saya memiliki permintaan yang menggunakan semua 3 bagian. Mysql default untuk menggunakan indeks penulis hanya melewatkan indeks komposit yang mengandung perusahaan dan tanggal meskipun mereka hadir dalam permintaan saya. Saya menggunakan indeks paksa untuk menggunakan komposit dan permintaan benar-benar berjalan lebih lambat. Mengapa itu terjadi? Saya akan memberi tahu Anda:

    Saya memilih rentang pada tanggal tersebut, jadi meskipun tanggal tersebut sangat selektif, fakta bahwa kami menggunakannya untuk pemindaian rentang (meskipun rentangnya relatif pendek, 6 bulan dari 6 tahun data) membuat komposit berbahaya untuk mysql. Untuk menggunakan komposit dalam kasus tertentu, mysql harus mengambil semua artikel yang ditulis sejak tahun-tahun baru kemudian masuk ke dalam siapa penulisnya, dan mengingat bahwa penulis belum menulis banyak artikel dibandingkan dengan penulis lain, mysql lebih suka untuk hanya menemukan penulis itu .

    Dalam kasus lain, kueri berjalan jauh lebih cepat pada komposit, kasusnya adalah ketika seorang penulis sangat populer dan memiliki sebagian besar catatan, mengurutkan berdasarkan tanggal masuk akal. Tetapi mysql tidak secara otomatis mendeteksi kasing itu, saya harus memaksa indeks ... Jadi, Anda tahu, ini bervariasi. Pemindaian rentang dapat membuat kolom selektif Anda tidak berguna. Distribusi data dapat membuat kasus di mana kolom lebih selektif untuk catatan yang berbeda ...

    Apa yang akan saya lakukan secara berbeda adalah menggeser tanggal (yang lagi-lagi, dalam teori adalah yang paling selektif) ke kanan, karena saya tahu saya akan melakukan pemindaian jarak di atasnya sekarang dan itu membuat perbedaan.

    Joe Yahchouchi
    sumber
    1
    Jika kueri Anda memiliki sesuatu seperti itu WHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)maka indeks pada (author, publishing_company, date)atau pada (publishing_company, author, date)akan lebih baik dan akan digunakan - tanpa memaksanya.
    ypercubeᵀᴹ
    -2

    Kasus berbeda untuk situasi berbeda. Kenali tujuan Anda; kemudian buat indeks Anda dan jalankan menjelaskan rencana untuk masing-masing dan Anda akan memiliki jawaban terbaik untuk situasi Anda.

    RMPJ
    sumber
    -2

    Dari Urutan kolom dalam Indeks pada Tanya Tom:

    Jadi, urutan kolom dalam indeks Anda tergantung pada BAGAIMANA QUERIES ANDA ditulis. Anda ingin dapat menggunakan indeks untuk sebanyak mungkin pertanyaan (sehingga dapat mengurangi seluruh jumlah indeks yang Anda miliki) - yang akan menggerakkan urutan kolom. Tidak ada yang lain (selektivitas a atau b tidak dihitung sama sekali).

    Setuju, bahwa kita harus memesan kolom berdasarkan klausa where, tetapi pernyataan "(selektivitas a atau b tidak dihitung sama sekali)" tidak benar.) ". Kolom yang paling selektif harus memimpin jika memenuhi peran pertama ("di mana klausa")

    Andjelko Miovcic
    sumber