เมื่อเลือกลำดับคอลัมน์ดัชนีข้อกังวลที่สำคัญคือ:
จะมี (ความเสมอภาค) เปรียบเทียบกับคอลัมน์นี้ในข้อความค้นหาของฉันหรือไม่?
หากคอลัมน์ไม่เคยปรากฏในส่วนคำสั่งที่ไหนก็ไม่คุ้มค่าการจัดทำดัชนี (1)
ตกลงดังนั้นคุณจะมีตารางและคิวรีสำหรับแต่ละคอลัมน์ บางครั้งมากกว่าหนึ่ง
คุณตัดสินใจเลือกสิ่งที่จะจัดทำดัชนีอย่างไร
ลองดูตัวอย่าง นี่คือตารางที่มีสามคอลัมน์ หนึ่งค่ามี 10 ค่าอีก 1,000 ค่าล่าสุด 10,000:
create table t(
  few_vals  varchar2(10),
  many_vals varchar2(10),
  lots_vals varchar2(10)
);
insert into t 
with rws as (
  select lpad(mod(rownum, 10), 10, '0'), 
         lpad(mod(rownum, 1000), 10, '0'), 
         lpad(rownum, 10, '0') 
  from dual connect by level <= 10000
)
  select * from rws;
commit;
select count(distinct few_vals),
       count(distinct many_vals) ,
       count(distinct lots_vals) 
from   t;
COUNT(DISTINCTFEW_VALS)  COUNT(DISTINCTMANY_VALS)  COUNT(DISTINCTLOTS_VALS)  
10                       1,000                     10,000     
ตัวเลขเหล่านี้จะถูกเติมด้วยเลขศูนย์ สิ่งนี้จะช่วยให้ประเด็นเกี่ยวกับการบีบอัดในภายหลัง
ดังนั้นคุณจึงมีคำถามทั่วไปสามข้อ:
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  few_vals = '0000000001';
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  lots_vals = '0000000001';
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  lots_vals = '0000000001'
and    few_vals = '0000000001';
คุณทำดัชนีอะไร 
ดัชนีเพียงเล็กน้อย __ จะดีกว่าการสแกนแบบเต็มตาราง:
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 |  
-------------------------------------------------------------------------------------------------------------
ดังนั้นจึงไม่น่าจะมีค่าการจัดทำดัชนีด้วยตัวเอง ข้อความค้นหาบน lots_vals คืนค่าสองสามแถว (เพียง 1 ในกรณีนี้) ดังนั้นนี่คือการจัดทำดัชนีที่คุ้มค่าแน่นอน
แต่สิ่งที่เกี่ยวกับการค้นหากับทั้งสองคอลัมน์?
คุณควรจัดทำดัชนี:
( few_vals, lots_vals )
หรือ
( lots_vals, few_vals )
คำถามซ่อนเงื่อน!
คำตอบคือไม่ใช่
แน่ใจว่าสามมิติเป็นสตริงที่มีความยาว ดังนั้นคุณสามารถบีบอัดได้ดี และคุณ (อาจ) ได้รับการข้ามดัชนีสแกนเพื่อค้นหาโดยใช้ (2-3_vals, lots_vals) ที่มีเพรดิเคตเฉพาะใน lots_vals เท่านั้น แต่ฉันไม่ได้อยู่ที่นี่แม้ว่าจะได้ผลดีกว่าการสแกนเต็มรูปแบบ:
create index few_lots on t(few_vals, lots_vals);
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  lots_vals = '0000000001';
select * 
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
-------------------------------------------------------------------------------------------  
| Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
-------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
|   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
|   2 |   VIEW               | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      61 |  
|   3 |    HASH GROUP BY     |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
|   4 |     TABLE ACCESS FULL| T        |      1 |      1 |      1 |00:00:00.01 |      61 |  
-------------------------------------------------------------------------------------------  
select /*+ index_ss (t few_lots) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  lots_vals = '0000000001';
select * 
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
----------------------------------------------------------------------------------------------------------------------  
| Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
----------------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      13 |     11 |  
|   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
|   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
|   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
|   5 |      INDEX SKIP SCAN                   | FEW_LOTS |      1 |     40 |      1 |00:00:00.01 |      12 |     11 |  
----------------------------------------------------------------------------------------------------------------------
คุณชอบเล่นการพนันหรือไม่? (2)
ดังนั้นคุณยังต้องการดัชนีที่มี lots_vals เป็นคอลัมน์นำ และอย่างน้อยในกรณีนี้ดัชนีผสม (ไม่กี่ล็อต) จะทำงานในปริมาณเดียวกันกับที่ใช้เพียงแค่ (ล็อต)
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  lots_vals = '0000000001'
and    few_vals = '0000000001';
select * 
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
-------------------------------------------------------------------------------------------------------------  
| Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
-------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |  
|   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
|   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |  
|   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |  
|   5 |      INDEX RANGE SCAN                  | FEW_LOTS |      1 |      1 |      1 |00:00:00.01 |       2 |  
-------------------------------------------------------------------------------------------------------------  
create index lots on t(lots_vals);
select /*+ index (t (lots_vals)) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  lots_vals = '0000000001'
and    few_vals = '0000000001';
select * 
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
----------------------------------------------------------------------------------------------------------------------  
| Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
----------------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |      1 |  
|   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
|   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
|   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
|   5 |      INDEX RANGE SCAN                  | LOTS     |      1 |      1 |      1 |00:00:00.01 |       2 |      1 |  
----------------------------------------------------------------------------------------------------------------------  
จะมีหลายกรณีที่ดัชนีผสมช่วยคุณประหยัด 1-2 IOs แต่มันก็คุ้มค่าถ้ามีดัชนีสองตัวสำหรับการออมนี้?
และยังมีปัญหาอีกอย่างกับดัชนีคอมโพสิต เปรียบเทียบปัจจัยการจัดกลุ่มสำหรับดัชนีสามรายการซึ่งรวมถึง 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    
โปรดสังเกตว่าปัจจัยการจัดกลุ่มสำหรับ few_lots นั้นสูงกว่า10xและ lots_few มากกว่า10 เท่า ! และนี่คือตารางสาธิตที่มีการจัดกลุ่มที่สมบูรณ์แบบเริ่มต้น ในฐานข้อมูลโลกแห่งความเป็นจริงผลกระทบมีแนวโน้มที่จะแย่ลง
ดังนั้นสิ่งที่ไม่ดีเกี่ยวกับที่? 
ปัจจัยการจัดกลุ่มเป็นหนึ่งในตัวขับเคลื่อนหลักที่กำหนดว่าดัชนี "น่าสนใจ" เป็นอย่างไร ยิ่งมีโอกาสสูงที่เครื่องมือเพิ่มประสิทธิภาพก็จะเลือกน้อยลง โดยเฉพาะอย่างยิ่งถ้า lots_vals ไม่ได้มีลักษณะเฉพาะจริง ๆ แต่โดยปกติแล้วจะมีจำนวนแถวต่อค่าไม่มาก หากคุณโชคร้ายนี่อาจเพียงพอที่จะทำให้เครื่องมือเพิ่มประสิทธิภาพคิดว่าการสแกนแบบเต็มนั้นราคาถูกกว่า ...
ตกลงดังนั้นดัชนีคอมโพสิตที่มีจำนวนน้อยมากและจำนวนมากจะได้รับสิทธิประโยชน์ขอบ 
สิ่งที่เกี่ยวกับการกรองการกรองสามมิติและหลายช่วง
ดัชนีคอลัมน์เดี่ยวให้ประโยชน์เพียงเล็กน้อยเท่านั้น แต่เมื่อรวมมันจะคืนค่าน้อย ดังนั้นดัชนีคอมโพสิตจึงเป็นความคิดที่ดี แต่ทางไหนบ้าง
หากคุณวางไม่กี่อันดับแรกการบีบอัดคอลัมน์นำจะทำให้ขนาดเล็กลง
create index few_many on t(many_vals, few_vals);
create index many_few on t(few_vals, many_vals);
select index_name, leaf_blocks, distinct_keys, clustering_factor 
from   user_indexes
where  index_name in ('FEW_MANY', 'MANY_FEW');
INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
FEW_MANY    47           1,000          10,000             
MANY_FEW    47           1,000          10,000   
alter index few_many rebuild compress 1;
alter index many_few rebuild compress 1;
select index_name, leaf_blocks, distinct_keys, clustering_factor 
from   user_indexes
where  index_name in ('FEW_MANY', 'MANY_FEW');
INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
MANY_FEW    31           1,000          10,000             
FEW_MANY    34           1,000          10,000      
ด้วยค่าที่แตกต่างกันน้อยลงในคอลัมน์นำจะบีบอัดให้ดีขึ้น ดังนั้นจึงมีขอบเขตน้อยกว่าในการอ่านดัชนีนี้ แต่เพียงเล็กน้อยเท่านั้น และทั้งคู่ก็มีขนาดเล็กกว่าเดิมอยู่แล้ว (ลดขนาดลง 25%)
และคุณสามารถไปต่อและบีบอัดดัชนีทั้งหมด!
alter index few_many rebuild compress 2;
alter index many_few rebuild compress 2;
select index_name, leaf_blocks, distinct_keys, clustering_factor 
from   user_indexes
where  index_name in ('FEW_MANY', 'MANY_FEW');
INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
FEW_MANY    20           1,000          10,000             
MANY_FEW    20           1,000          10,000   
ตอนนี้ดัชนีทั้งสองกลับมามีขนาดเท่าเดิม หมายเหตุสิ่งนี้ใช้ประโยชน์จากความจริงที่ว่ามีความสัมพันธ์ระหว่างคนไม่มากและคนจำนวนมาก ไม่น่าที่คุณจะเห็นประโยชน์เช่นนี้ในโลกแห่งความเป็นจริงอีกครั้ง
จนถึงตอนนี้เราได้พูดคุยเกี่ยวกับการตรวจสอบความเท่าเทียมกันเท่านั้น บ่อยครั้งที่มีดัชนีคอมโพสิตคุณจะมีความไม่เท่าเทียมกับหนึ่งในคอลัมน์ เช่นข้อความค้นหาเช่น "รับคำสั่งซื้อ / การจัดส่ง / ใบแจ้งหนี้สำหรับลูกค้าในช่วง N วันที่ผ่านมา"
หากคุณมีข้อความค้นหาประเภทนี้คุณต้องการความเท่าเทียมกันกับคอลัมน์แรกของดัชนี:
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  few_vals < '0000000002'
and    many_vals = '0000000001';
select * 
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
-------------------------------------------------------------------------------------------------------------  
| Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
-------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |  
|   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |  
|   2 |   VIEW                                 | VW_DAG_0 |      1 |     10 |     10 |00:00:00.01 |      12 |  
|   3 |    HASH GROUP BY                       |          |      1 |     10 |     10 |00:00:00.01 |      12 |  
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |     10 |     10 |00:00:00.01 |      12 |  
|   5 |      INDEX RANGE SCAN                  | FEW_MANY |      1 |     10 |     10 |00:00:00.01 |       2 |  
-------------------------------------------------------------------------------------------------------------  
select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
from   t
where  few_vals = '0000000001'
and    many_vals < '0000000002';
select * 
from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
----------------------------------------------------------------------------------------------------------------------  
| Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
----------------------------------------------------------------------------------------------------------------------  
|   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |      1 |  
|   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |      1 |  
|   2 |   VIEW                                 | VW_DAG_0 |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
|   3 |    HASH GROUP BY                       |          |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
|   5 |      INDEX RANGE SCAN                  | MANY_FEW |      1 |      1 |     10 |00:00:00.01 |       2 |      1 |  
----------------------------------------------------------------------------------------------------------------------  
สังเกตว่าพวกเขากำลังใช้ดัชนีตรงกันข้าม
TL; DR
- คอลัมน์ที่มีเงื่อนไขความเท่าเทียมกันควรเป็นอันดับแรกในดัชนี 
- หากคุณมีหลายคอลัมน์ที่มีความเสมอภาคในแบบสอบถามของคุณการวางคอลัมน์ที่มีค่าต่างกันน้อยที่สุดก่อนจะให้ประโยชน์ในการบีบอัดที่ดีที่สุด
- ในขณะที่การสแกนดัชนีข้ามเป็นไปได้คุณต้องมั่นใจว่านี่จะเป็นตัวเลือกที่มีศักยภาพสำหรับอนาคตอันใกล้
- ดัชนีคอมโพสิตรวมถึงคอลัมน์ใกล้เคียงมีประโยชน์น้อยที่สุด ต้องแน่ใจว่าคุณต้องบันทึก 1-2 IOs จริงๆ
1: ในบางกรณีอาจมีค่ารวมถึงคอลัมน์ในดัชนีถ้านี่หมายความว่าคอลัมน์ทั้งหมดในแบบสอบถามของคุณอยู่ในดัชนี สิ่งนี้เปิดใช้งานการสแกนดัชนีเท่านั้นดังนั้นคุณไม่จำเป็นต้องเข้าถึงตาราง
2: หากคุณได้รับใบอนุญาตสำหรับการวินิจฉัยและการปรับแต่งคุณสามารถบังคับให้มีการข้ามการสแกนด้วย SQL Plan Management
ADDEDNDA
PS - เอกสารที่คุณยกมามีตั้งแต่ 9i นั่นคืออายุ reeeeeeally ฉันจะยึดติดกับบางสิ่งที่ใหม่กว่า