1.分區索引
索引也可以像表分區結構那樣來進行分區。有多種方法可以對索引進行分區。在分區表上可以創建局部或者全局索引。並且,有多種分區方案可選,如範圍分區,散列分區,列表分區以及混合分區方案。自oracle數據庫10g版本以來,也可以在非分區表上建立分區索引。
1.1 局部索引
局部分區索引使用LOCAL關鍵字來建立,其分區邊界與表相同。簡單來說,與每個表分區相聯結的有一個索引分區。因爲維護操作可以在獨立分區區級進行,表的可用性更好。對索引分區的維護操作僅需要鎖定相應的表分區而不是整張表。
如果局部索引分區鍵列並且如果SQL語句聲明瞭分區鍵列上的謂語,執行計劃就僅需要訪問一個或者很少的索引分區。這個概念通常被稱爲分區消除。如果執行計劃在最小的分區由進行搜索,性能就會得到提升。
create table sales_fact_part
partition by range(year)
(partition p_1997 values less than (1998),
partition p_1998 values less than (1999),
partition p_1999 values less than (2000),
partition p_2000 values less than (2001),
partition p_max values less than (maxvalue)
) as select * from sales_fact;
SQL> set lines 120 pages 100
SQL> set serveroutput off
SELECT *
FROM (SELECT * FROM sales_fact_part WHERE product = 'Xtend Memory')
3 WHERE rownum < 21;
SQL> @x.sql
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID c726kzar01w0h, child number 0
-------------------------------------
SELECT * FROM (SELECT * FROM sales_fact_part WHERE product = 'Xtend
Memory') WHERE rownum < 21
Plan hash value: 2438495830
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name| Rows | Bytes | Cost (%CPU)| Time
| Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT ||
| | 16 (100)|
||
|
|* 1 | COUNT STOPKEY ||
| |
| ||
|
| 2 | PARTITION RANGE ALL |
| 1895 | 218K| 16 (0)| 00:00:01 | 1 | 5 |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES_FACT_PART| 1895 | 218K| 16 (0)| 00:00:01 | 1 | 5 |
|* 4 | INDEX RANGE SCAN | SALES_FACT_PART_N1 ||
| 6 (0)| 00:00:01 | 1 | 5 |
--------------------------------------------------------------------------------------------------------------------------
SELECT *
FROM (SELECT *
FROM sales_fact_part
WHERE product = 'Xtend Memory'
AND YEAR = 1998)
WHERE rownum < 21;
SQL> @x.sql
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 2r316z9t211gw, child number 0
-------------------------------------
SELECT * FROM (SELECT * FROM sales_fact_part WHERE
product = 'Xtend Memory' AND YEAR = 1998) WHERE rownum < 21
Plan hash value: 3853015538
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name| Rows | Bytes | Cost (%CPU)| Time
| Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT ||
| | 15 (100)|
||
|
|* 1 | COUNT STOPKEY ||
| |
| ||
|
| 2 | PARTITION RANGE SINGLE |
| 453 | 53454 | 15 (0)| 00:00:01 | 2 | 2 |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES_FACT_PART| 453 | 53454 | 15 (0)| 00:00:01 | 2 | 2 |
|* 4 | INDEX RANGE SCAN | SALES_FACT_PART_N1 ||
| 3 (0)| 00:00:01 | 2 | 2 |
--------------------------------------------------------------------------------------------------------------------------
儘管表的可用性很重要,應該仍然要考慮另外一點:如果謂語沒有聲明分區鍵列,那麼局部索引中必須訪問所有的分區以識別候選的數據行。如果分區數非常多,到達幾千的量級的話,這可能會導致性能問題的出現。即使這樣,也需要衡量創建局部索引而不是全局索引帶來的影響。
1.2 全局索引
全局索引通過關鍵字GLOBAL來創建。在全局索引中,索引的分區邊界與表分區邊界不一定要匹配,並且表和索引的分區鍵也可以不一樣。
create index sales_index_fact_part_n1 on sales_fact_part(year)
global partition by range(year)
(partition p_1998 values less than(1999),
partition p_2000 values less than(2001),
partition p_max values less than(maxvalue));
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name| Rows | Bytes | Cost (%CPU)| Time
| Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT ||
| | 3 (100)|
||
|
|* 1 | COUNT STOPKEY ||
| |
| ||
|
| 2 | PARTITION RANGE SINGLE || 180 | 21240 | 3 (0)| 00:00:01 | 2 | 2 |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES_FACT_PART| 180 | 21240 | 3 (0)| 00:00:01 | 2 | 2 |
|* 4 | INDEX RANGE SCAN | SALES_FACT_PART_N1 | 180 || 1 (0)| 00:00:01 | 2 | 2 |
--------------------------------------------------------------------------------------------------------------------------
唯一索引可以建立爲不包含分區列的全局索引。但如果建立唯一局部索引,則表的分區鍵必須包含在局部索引中
2 散列分區與範圍分區;
在散列分區方案中,分區鍵列的值使用散列算法進行散列化來確定存儲數據行的分區.這種類型的分區方案適合於分區列使用人造鍵值填充的情況,例如分區列由順序生成的值填充。如果列值得分別是均勻的,那麼每個分區將會存儲幾乎相等數目的數據行。
散列分區方案還有幾個額外的優點。範圍分區方案有一些管理開支,因爲在將來想放入新的數據行就需要定期加入新的分區。例如,如果分區鍵是order_date列,那麼就必須增加新分區來放入將來的日期的數據。在散列分區中,這個開支就避免了,因爲數據行在使用散列算法進行分區的各個分區是均勻分佈的。如果列值是均勻分佈的則所有分區將保持近似相等數量的數據行,也就沒有理由定期增加更多新的分區了。
散列分區表和索引在應對由唯一索引和主鍵索引引起的與併發相關的性能問題時時非常有效的。典型的主鍵可能會使用生成的順序列值來填充。因爲索引按照排序後的順序存儲列值,新的數據行的列值將會進入到索引最右側的葉子塊。在該葉子塊滿了以後,接下來插入的數據行就會進入新的最右側葉子塊中,資源爭奪點也就從一個葉子塊轉移到另一葉子塊。隨着表的插入併發性增加,會話將會大幅修改索引的最右側塊。基本上,索引的當前最右側葉子塊將會是最主要的資源爭奪點。將會看到會話等待事件如緩存區繁忙等待。在RAC中,由於全局緩存通信的成本導致這個問題放大,gc緩衝繁忙將會排在第一爲的等待事件。這種類型的最右側索引的快速增長被稱爲右側增長索引。
create sequence sfseq cache 200;
drop table sales_fact_part;
create table sales_fact_part
partition by hash(id)
partitions 32
as select sfseq.nextval id ,f.* from sales_fact f;
create unique index sales_fact_part_n1 on sales_fact_part(id) local;
SQL> select * from sales_fact_part where id=1000;
ID COUNTRY
REGION PRODUCT YEAR WEEK
SALERECEIPTS
---------- ---------------------------------------- ------------------------------ -------------------------------------------------- ---------- ---------- ---------- ----------
1000 United States of America Northern America Martial Arts Champions
1999 6
288.99 307.633
SQL> @x.sql
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID bfy3qhpjprucg, child number 0
-------------------------------------
select * from sales_fact_part where id=1000
Plan hash value: 1603644595
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name| Rows
| Bytes | Cost (%CPU)| Time| Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT ||
| | 2 (100)|
||
|
| 1 | PARTITION HASH SINGLE || 1 | 131 | 2 (0)| 00:00:01 | 25 | 25 |
| 2 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES_FACT_PART| 1 | 131 | 2 (0)| 00:00:01 | 25 | 25 |
|* 3 | INDEX UNIQUE SCAN | SALES_FACT_PART_N1 | 1 || 1 (0)| 00:00:01 | 25 | 25 |
-------------------------------------------------------------------------------------------------------------------------
如果分區鍵上的數據分佈式均勻的,如有序列生成的情況,那麼數據行在所有分區上均勻分佈。你可以使用dbms_rowid包來衡量散列分區表上的數據分佈。
SQL> 1 SELECT dbms_rowid.rowid_object(ROWID) obj_id, COUNT(*)
2 FROM sales_fact_part
3* GROUP BY dbms_rowid.rowid_object(ROWID)
SELECT dbms_rowid.rowid_object(ROWID) obj_id, COUNT(*)
FROM sales_fact_part
3 GROUP BY dbms_rowid.rowid_object(ROWID);
OBJ_ID COUNT(*)
---------- ----------
89172 3478
89175 3544
89177 3542
89182 3470
89159 3481
89164 3571
89169 3376
89178 3402
89180 3420
89181 3424
89173 3467
89179 3484
89156 3426
散列分區算法:如果提供了分區鍵值,就可以使用ora_hash函數獲得分區ID,ORA_HASH 函數的第2個參數問分區數減1.
SELECT dbms_rowid.rowid_object(ROWID) obj_id,
ora_hash(id, 31.0) part_id,
COUNT(*)
FROM sales_fact_part
GROUP BY dbms_rowid.rowid_object(ROWID), ora_hash(id, 31.0)
6 ORDER BY 1;
OBJ_ID PART_ID COUNT(*)
---------- ---------- ----------
89153 0 3505
89154 1 3492
89155 2 3572
89156 3 3426
89157 4 3480
89158 5 3427
89159 6 3481
89160 7 3457
3. 基於函數的索引:
如果一個謂語在索引列上應用了函數,則優化器不會選用該列上的索引。例如,對於謂語to_char(i)=‘1000’,不會選用id列上的索引,因爲索引列上應用了to_char函數,這個限制可以通過表達是to_char(id)上創建基於函數的索引來克服。基於函數的索引預存函數的結果。謂語中所聲明的表達是必須與基於函數的索引所聲明表示相匹配。
基於函數的索引也可以建立在用戶自定義函數上,但這個函數必須定義爲確定性函數,也就是說對於這個函數的每一次執行必須返回一致的值。
SQL> select * from sales_fact_part where to_char(id)='1000';
select * from sales_fact_part where to_char(id)='1000'
Plan hash value: 893548352
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | |
|478 (100)|
| |
|
| 1 | PARTITION HASH ALL| | 1 |
131 |478 (1)| 00:00:01 |
1 | 32 |
|* 2 | TABLE ACCESS FULL| SALES_FACT_PART |
1 | 131 |
478 (1)| 00:00:01 | 1 | 32 |
create index sales_fact_part_fbi1 on sales_fact_part(to_char(id));
SQL> @analyze_table_sfp.sql
PL/SQL procedure successfully completed.
SQL> @x.sql
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 50hdw2f955xpj, child number 0
-------------------------------------
select * from sales_fact_part where to_char(id)='1000'
Plan hash value: 3799936162
---------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
| Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | |
| |
2 (100)| |
| |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| SALES_FACT_PART |
1 | 77 |2 (0)| 00:00:01 | ROWID | ROWID |
|* 2 | INDEX RANGE SCAN | SALES_FACT_PART_FBI1 |1 |
| 1 (0)| 00:00:01 |
| |
---------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SALES_FACT_PART"."SYS_NC00009$"='1000')
SELECT data_default, hidden_column, virtual_column
FROM dba_tab_cols
WHERE table_name = 'SALES_FACT_PART'
4 AND virtual_column = 'YES';
DATA_DEFAULT HID VIR
-------------------------------------------------------------------------------- --- ---
TO_CHAR("ID") YES YES
SQL> col INDEX_NAME for a20
SQL> col COLUMN_NAME for a20
SELECT index_name, column_name
FROM dba_ind_columns
3 WHERE index_name = 'SALES_FACT_PART_FBI1';
INDEX_NAME
COLUMN_NAME
-------------------- --------------------
SALES_FACT_PART_FBI1 SYS_NC00009$
說明:在增加了基於函數的索引後收集表的統計信息很重要,如果不收集,新的虛擬列就沒有統計信息。以下腳本用於收集統計信息(要設置cascade=>true):
begin
dbms_stats.gather_table_stats (
ownname =>user,
tabname=>'SALES_FACT_PART',
estimate_percent=>30,
cascade=>true);
end;
4.反鍵索引:
反鍵索引中,列值按照逐個字符的反向順序存儲。因爲列值是按照反向順序存儲的,連續的列值將會存儲在不同的索引葉子塊中,從而避免了右側增長索引所帶來的資源爭奪問題。但是,在表的數據塊中,這些列值還是存儲的爲順序的。
反鍵索引有兩個問題。
(1).反轉鍵索引的範圍掃描不能使用範圍運算符如between,<,>等,因爲索引範圍掃描的基本假設就是列值按照邏輯升序或者降序存儲,反鍵索引由於列值按照反轉順序存儲,沒有安裝邏輯鍵的順序來維護,違反了這個假設,
(2).反鍵索引可能會人爲增加物理讀取的次數,因爲列值被存儲在很多葉子塊中,而這些葉子塊可能需要讀取到緩衝區緩存中來修改塊。但是,這個I/O成本的增加需要與右側增長索引所引起的併發性問題相對照來衡量。
drop index sales_fact_part_n1;
create unique index sales_fact_part_n1 on sales_fact_part(id) global reverse;
SQL> select * from sales_fact_part where id=1000;
SQL> @x.sql
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
SQL_ID bfy3qhpjprucg, child number 0
-------------------------------------
select * from sales_fact_part where id=1000
Plan hash value: 1855157343
--------------------------------------------------------------------------
| Id | Operation | Name
| E-Rows |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| SALES_FACT_PART
| 1 |
|* 2 | INDEX UNIQUE SCAN | SALES_FACT_PART_N1 | 31 |
--------------------------------------------------------------------------
select * from sales_fact_part where id between 1000 and 1001
Plan hash value: 893548352
------------------------------------------------------
| Id | Operation
| Name | E-Rows |
-------------------------------------------------------
| 0 | SELECT STATEMENT |
| |
| 1 | PARTITION HASH ALL|
| 3 |
|* 2 | TABLE ACCESS FULL| SALES_FACT_PART |
3 |
-------------------------------------------------------
尤其在RAC中,右側增長索引可能會引起不能容忍的性能問題,反鍵索引被引入來解決性能問題,但有的時候可能應該考慮散列分區索引而不是反轉鍵索引。
5.降序索引
索引默認按照升序存儲列值,但可以通過使用降序索引來切換爲降序存儲,如果你的應用按照特定的順序來獲取數據,則在數據行被髮送給應用之前需要進行排序。通過降序索引可以避免這個排序。如果應用按照某個特定的順序上百萬次地獲取數據,則這類索引是非常有用的。從oracle 11gR2開始,降序索引實現基於函數的索引
SQL> drop index sales_fact_c1;
Index dropped.
SQL> create index sales_fact_c1 on sales_fact(product desc, year desc,week desc);
Index created.
set lines 120 pages 100
begin
dbms_stats.gather_table_stats (
ownname =>user,
tabname=>'SALES_FACT',
estimate_percent=>30,
cascade=>true);
end;
8 /
PL/SQL procedure successfully completed.
SQL> set termout off
SELECT YEAR, week FROM sales_fact s WHERE YEAR IN (1998, 1999, 2000)
AND week < 5 AND product = 'Xtend Memory' ORDER BY product DESC,
YEAR DESC, week DESC
Plan hash value: 2514767912
---------------------------------------------------
| Id | Operation | Name
| E-Rows |
---------------------------------------------------
| 0 | SELECT STATEMENT | |
|
|* 1 | INDEX RANGE SCAN| SALES_FACT_C1 | 107 |--語句執行過程中沒有排序步驟
---------------------------------------------------
5 .不可見索引
在某些場景下,你可能需要增加一個索引來對SQL語句的性能進行調優,但是你不太確定索引帶來的負面影響,不可見索引以較小的風險來衡量新索引所帶來的影響方面非常有用,一個索引可以加入到數據庫中並被標記爲不可見,這樣優化器就不會選用這個索引,可以確定某個索引沒有負面影響或對執行計劃沒有負面影響後將它標記爲可見。
SELECT * FROM (SELECT *
FROM sales_fact WHERE
product = 'Xtend Memory' AND YEAR = 1998
AND week
= 1) WHERE rownum < 21
Plan hash value: 3021657266
---------------------------------------------------------------
| Id | Operation | Name
| E-Rows |
---------------------------------------------------------------
| 0 | SELECT STATEMENT |
| |
|* 1 | COUNT STOPKEY |
| |
| 2 | TABLE ACCESS BY INDEX ROWID| SALES_FACT |
7 |
|* 3 | INDEX RANGE SCAN | SALES_FACT_C1 |
9 |
---------------------------------------------------------------
SQL> alter index sales_fact_c1 invisible;
Index altered.
SELECT * FROM (SELECT * FROM sales_fact
WHERE
product = 'Xtend Memory' AND YEAR = 1998
AND week
= 1) WHERE rownum < 21
Plan hash value: 2622531735
--------------------------------------------------
| Id | Operation | Name
| E-Rows |
--------------------------------------------------
| 0 | SELECT STATEMENT | |
|
|* 1 | COUNT STOPKEY |
| |
|* 2 | TABLE ACCESS FULL| SALES_FACT | 7 |
--------------------------------------------------
在會話級別設置爲TRUE後,優化器又選擇了索引:
SQL> alter session set optimizer_use_invisible_indexes=true;
Session altered.
SELECT * FROM (SELECT * FROM sales_fact
WHERE
product = 'Xtend Memory' AND YEAR = 1998
AND week
= 1) WHERE rownum < 21
Plan hash value: 3021657266
---------------------------------------------------------------
| Id | Operation | Name
| E-Rows |
---------------------------------------------------------------
| 0 | SELECT STATEMENT |
| |
|* 1 | COUNT STOPKEY |
| |
| 2 | TABLE ACCESS BY INDEX ROWID| SALES_FACT |
7 |
|* 3 | INDEX RANGE SCAN | SALES_FACT_C1 |
9 |
---------------------------------------------------------------
不可見索引還有另一個應用場景,這種索引有助於在刪除不使用的索引時用來降低風險。從生產數據庫中刪除不使用的索引並不是不常見,可能之後卻意外地認識到刪除索引在一個很重要的報表中用到了,即使經過充分分析後 ,也可能被刪除的索引在某個商務過程中必須得,而重建索引可能會導致應用停機。從oracle 11g以來,你可以將索引標記爲不可見,等過了幾周以後,如果沒有任何進程要用到這個索引,則可以較爲安全地將其刪掉。如果索引被標記爲不可見後發現是需要的,則可以很快使用一個SQL御姐將索引還原爲可見狀態。
6.虛擬索引
虛擬索引對於查看索引的有效性是很有用的,虛擬索引不會分配存儲空間,因此可以很快建立。虛擬索引與不可見索引的不同之處在於不可見索引是有與之相關的存儲的,只是優化器不能選擇它們,而虛擬索引沒有與之關聯的存儲空間,由於這個 原因,虛擬索引也被稱爲無段索引。
會話可修改一個劃線參數_USE_NOSEGMENT_INDEXES控制了優化器是否可以考慮選擇虛擬索引。這個參數默認值是False,應用不會選擇虛擬索引。使用nosegment子句創建虛擬索引。
SQL> create index sales_virt on sales(cust_id,promo_id) nosegment;
Index created.
SQL> alter session set "_use_nosegment_indexes"=true;
Session altered.
SQL> explain plan for select * from sales where cust_id=:b1 and promo_id=:b2;
Explained.
SQL> select * from table(dbms_xplan.display(null,'','all'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 2769445196
----------------------------------------------------------------------------------------------------
| Id | Operation | Name
| Rows | Bytes | Cost (%CPU)| Time
| Ps
tart| Pstop |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| 33 | 957 | 9 (0)| 00:00:01 |
| |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| SALES
| 33 | 957 | 9 (0)| 00:00:01 | RO
WID | ROWID |
|* 2 | INDEX RANGE SCAN | SALES_VIRT | 9188 |
| 1 (0)| 00:00:01 |
| |
----------------------------------------------------------------------------------------------------
6.位圖聯結索引
位圖聯結索引對於數據倉庫應用中物化事實表和緯度表之間的聯結是很有用的,在數據倉庫表中,一般來說,事實表比緯度表要大得多,並且緯度和事實表使用主鍵進行聯結--它們之間存在外鍵關係。這種聯結的成本由於事實表很大而更高。如果能夠預先存儲聯結結果則這些查詢的性能就會得到提高。物化視圖是預先存儲計算聯結結果的可選項之一。位圖聯結索引是另一個可選項。
典型的DW查詢:
SELECT SUM(s.quantity_sold), SUM(s.amount_sold) FROM sales s,
products p, customers c, channels ch WHERE s.prod_id = p.prod_id
AND s.cust_id = c.cust_id AND s.channel_id = ch.channel_id
AND
p.prod_name = 'Y box' AND c.cust_first_name = 'Abigail' AND
ch.channel_desc = 'Direct_sales'
Plan hash value: 2309889988
------------------------------------------------------------------------------------------------------
| Id | Operation | Name
| E-Rows | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| |
| | |
| 1 | SORT AGGREGATE |
| 1 |
| | |
|* 2 | HASH JOIN |
| 20 | 2293K| 2293K| 1536K (0)|
|* 3 | TABLE ACCESS FULL | CUSTOMERS
| 43 | |
| |
| 4 | NESTED LOOPS |
| 3235 | |
| |
| 5 | NESTED LOOPS |
| 3235 | |
| |
| 6 | MERGE JOIN CARTESIAN |
| 1 |
| | |
|* 7 | TABLE ACCESS FULL | CHANNELS
| 1 |
| |
|
| 8 | BUFFER SORT |
| 1 | 73728 | 73728 |
|
|* 9 | TABLE ACCESS FULL | PRODUCTS
| 1 |
| |
|
| 10 | PARTITION RANGE ALL |
| |
| | |
| 11 | BITMAP CONVERSION TO ROWIDS |
| | |
| |
| 12 | BITMAP AND |
| |
| | |
|* 13 | BITMAP INDEX SINGLE VALUE | SALES_PROD_BIX
| |
| | |
|* 14 | BITMAP INDEX SINGLE VALUE | SALES_CHANNEL_BIX |
| |
| |
| 15 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES
| 3190 | |
| |
------------------------------------------------------------------------------------------------------
利用位圖聯結索引:
SQL> alter table products modify primary key validate;
Table altered.
SQL> alter table customers modify primary key validate;
Table altered.
SQL> alter table channels modify primary key validate;
Table altered.
create bitmap index sales_bji1 on sales(p.prod_name,c.cust_first_name,
ch.channel_desc)
from sales s,products p,customers c,channels ch
where s.prod_id=p.prod_id
and s.cust_id=c.cust_id
6 and s.channel_id=ch.channel_id local;
Index created.
SELECT SUM(s.quantity_sold), SUM(s.amount_sold) FROM sales s,
products p, customers c, channels ch WHERE s.prod_id = p.prod_id
AND s.cust_id = c.cust_id AND s.channel_id = ch.channel_id
AND
p.prod_name = 'Y box' AND c.cust_first_name = 'Abigail' AND
ch.channel_desc = 'Direct_sales'
-------------------------------------------------------------------
| Id | Operation | Name
| E-Rows |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| |
| 1 | SORT AGGREGATE |
| 1 |
| 2 | PARTITION RANGE ALL |
| 19 |
| 3 | TABLE ACCESS BY LOCAL INDEX ROWID| SALES
| 19 |
| 4 | BITMAP CONVERSION TO ROWIDS |
| |
|* 5 | BITMAP INDEX SINGLE VALUE | SALES_BJI1 |
|
-------------------------------------------------------------------
位圖聯結索引通過虛擬列的索引物化了結果集,從而避免了成本較高的聯結操作,但是位圖聯結索引有侷限性,索引維度都需要定義有經過驗證的主鍵或者唯一鍵約束,索引必須是局部的。