Oracle_高級功能(2) 索引
1.oracle優化器
優化目標分為4種:
choose (選擇性)
rule (基於規則)
first rows(第一行)
all rows(所有行)
Description:描述sql的執行計劃
Object owner:對象模式
Object name:對象名
Cost:花費(的時間)
Cardinality:基數,約等於行數
Bytes:空間(訪問的存儲空間)
2.table的訪問方式
2.1全表掃描(table access full)
全表掃描就是順序地訪問表中每條記錄。
oracle采用一次讀入多個數據塊(database block)的方式優化全表掃描。
select * from emp;
2.2通過rowid訪問表(table access by user/index rowid)
可以采用基於rowid的訪問方式情況,提高訪問表的效率。
rowid包含了表中記錄的物理位置信息。
oracle采用索引(index)實現了數據和存放數據的物理位置(rowid)之間的聯系。
通常索引提供了快速訪問rowid的方法,因此那些基於索引列的查詢就可以得到性能上的提高。
舉例:
select rowid,emp.* from emp where rowid=‘AAAR3sAAEAAAACXAAA‘;
select * from emp where empno=7788;
--清除數據庫內存緩沖區
alter system flush buffer_cache;
索引(index)
1.索引定義
索引是一種與表相關聯的可選結構,有時可以提高數據訪問速度。
通過在表中的一個或多個列上創建索引,
通常情況下能夠(快速地)從隨機分布的表行中檢索一小部分行。
索引是減少磁盤 i/o 的許多手段之一。
如果一個表沒有索引,數據庫必須執行全表掃描來查找值。
測試:
select * from all_objects;
create table tab_all_objects as select * from all_objects;
select * from tab_all_objects;
select * from tab_all_objects where object_name=‘STUDENT‘;
0.531s-->0.015s
create index idx_object_name on tab_all_objects (object_name);
練習:
select owner,view_name,text_length,editioning_view,read_only from all_views;
create table tab_all_views as select owner,view_name,text_length,editioning_view,read_only from all_views;
select * from tab_all_views;
0.515s-->0.498s
select * from tab_all_views where view_name=‘VIEW_EMP‘;
0.499s-->0.062s
create index idx_view_name on tab_all_views (view_name);
select * from emp,dept where emp.deptno=dept.deptno;
0.109s-->0.031s
create index idx_emp_deptno on emp (deptno);
通常,在下列情況下可以考慮在某列上創建索引:
某個列經常被作為查詢條件,並且查詢結果只返回了表中的一小部分行。5% >20%
列或列集上存在引用完整性約束(是外鍵列)。
二、語法
2.1 創建索引
create [unique | bitmap] index 索引名 on 表名 { ([<expr>] <col> [asc | desc] [, …])
舉例:
create index idx_emp_deptno on emp (deptno); 普通索引
create unique index idx_emp_ename on emp (ename); 唯一索引
create index idx_emp_job_sal on emp (job,sal); 復合索引
create index idx_emp_sal on emp (sal*12); 函數索引
select * from emp where deptno=20;
select * from emp where ename=‘FORD‘;
select * from emp where job=‘MANAGER‘ and sal>=2500;
select * from emp where sal*12>=20000;
select * from tab_all_objects where object_id=74975;
create index idx_object_id on tab_all_objects (object_id);
--select * from tab_all_objects where object_type=‘SYNONYM‘;
--create index idx_object_type on tab_all_objects (object_type);
2.2 修改索引
alter index <ind> {enable | disable};
alter index <ind> rename to <new>;
舉例:
alter index IDX_EMP_SAL disable;
alter index idx_emp_deptno disable;
alter index IDX_EMP_SAL rename to IDX_EMP_SAL1;
alter index IDX_EMP_SAL1 rename to IDX_EMP_SAL;
2.3 刪除索引
drop index <ind>;
舉例:
--drop index IDX_EMP_SAL;
2.4 分析索引
analyze index <ind> validate structure online|offline;
舉例:
analyze index IDX_EMP_SAL validate structure online;
2.5 重建索引
alter index <ind> rebuild
舉例:
alter index IDX_EMP_SAL rebuild;
--授權
connect sys/123 as sysdba;
grant create any index,drop any index,alter any index to scott;
三、索引特征
索引是一種模式對象,它在邏輯上和物理上都與其相關聯表對象的數據保持獨立。
因此,可以刪除或創建索引而不會實際影響相關的表。
如果刪除一個索引,應用程序將仍然可以工作。不過,訪問之前通過索引訪問的數據可能會變慢。
索引的存在與否,不需要改變任何sql語句的寫法。
索引是到單一行數據的快速訪問路徑。它只影響執行的速度。
對於一個已被索引的給定的數據值,索引直接指向包含該值的行的位置。
在表上存在過多的索引,會降低dml性能,因為數據庫還必須更新索引。
主鍵和唯一鍵會自動生成索引,但需要手動在外鍵上創建索引。
create index ind_emp_deptno on emp_test (deptno);
select * from emp,dept where emp.deptno=dept.deptno;
四、復合索引
復合索引,也稱為連接索引,是在某個表中的多個列上的索引。
復合索引中的列應該以在檢索數據的查詢中最有意義的順序出現,但在表中不必是相鄰的。
若 where 子句引用了復合索引中的所有列或前導列,
復合索引可以加快 select 語句的數據檢索速度。
所以,在定義中所使用的列順序很重要。一般地,最常被訪問的列放在前面。
五、唯一索引和非唯一索引
索引可以是唯一的或非唯一的。
唯一索引保證在表的鍵列或鍵列集上沒有具有重復的值的行。
在一個唯一索引中,對每個數據值都存在一個 rowid。葉塊中的數據僅根據鍵排序。
非唯一索引允許在索引的列或列集中有重復的值。
對於非唯一索引,rowid 被包含在鍵中且已排序,
因此非唯一索引按索引鍵和 rowid (升序) 進行排序。
六、索引類型
索引類型分為:b-樹索引、位圖索引、基於函數的索引
6.1 b-樹索引
這是索引的標準類型。對於主鍵和高選擇性索引非常適合。
平衡樹,簡稱b-樹,是最常見的數據庫索引類型。
一個 b-樹索引是被劃分為多個範圍的已排序的值列表。
通過將鍵與一行或行範圍關聯起來 ,b-樹可以對多種類型的查詢提供優秀的檢索性能,
包括精確匹配和範圍搜索等。
二分查找算法是在有序數組中用到的較為頻繁的一種算法。
在未接觸二分查找算法時,
最通用的一種做法是,對數組進行遍歷,跟每個元素進行比較,其時間為O(n).
但二分查找算法則更優,因為其查找時間為O(lgn)。
比如數組{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},查找元素6,
用二分查找的算法執行的話,其順序為:
1.找中位數,用中位數和6比較,如果中位數比6大,則在中位數左側數組中查找;如果中位數比6小,則在中位數右側數組中查找;
2.循環步驟1,直到等於要查找的值,並返回;或者找不到要查找的值,則返回空。
select index_type,count(1) from dba_indexes where 1=1 group by index_type order by count(1) desc;
1 NORMAL 3681
2 LOB 901 BLOB、CLOB列產生的,非單獨索引(view PLAN_TABLE$;)
3 IOT - TOP 158 索引組織表的主鍵索引(view SQLLOG$;)
4 FUNCTION-BASED NORMAL 40 基於函數的索引(view DBFS$_MOUNTS;)
5 BITMAP 16 位圖索引
6 CLUSTER 10
7 FUNCTION-BASED DOMAIN 4
8 DOMAIN 1
create bitmap index SH.SALES_PROD_BIX on SH.SALES (PROD_ID);
6.2 位圖索引
在位圖索引中,索引條目使用位圖來指向多個行。
create index idx_emp_deptno on emp (deptno);
drop index idx_emp_deptno;
create bitmap index idx_emp_deptno on emp (deptno);
6.3 基於函數的索引
這種類型的索引包括經過一個函數(如upper函數)轉換過的列,或包括在表達式中的列。
create index idx_emp_ename_lower on emp (lower(ename));
七、索引掃描
在索引掃描中,數據庫使用語句指定的索引列,通過遍歷索引來檢索行。
數據庫掃描索引,將使用n個i/o就能找到其要查找的值,其中 n 即是b-樹索引的高度。
這是數據庫索引背後的基本原理。
如果 sql 語句僅訪問被索引的列,那麽數據庫只需直接從索引中讀取值,而不用讀取表。
如果該語句同時還需要訪問除索引列之外的列,那麽數據庫會使用 rowid 來查找表中的行。
通常,為檢索表數據,數據庫以交替方式先讀取索引塊,然後讀取相應的表塊。
7.1.完全索引掃描 INDEX FULL SCAN
select emp.* from emp,dept where emp.deptno=dept.deptno;
在完全索引掃描中,數據庫順序讀取整個索引。
如果在 sql 語句中的謂詞 (where 子句) 引用了一個索引列,
或者在某些情況下未不指定任何謂詞,此時可能使用完全索引掃描。
完全掃描可以消除排序,因為數據本身就是基於索引鍵排過序的。
7.2快速完全索引掃描 INDEX FAST FULL SCAN
select view_name from tab_all_views;
--all rows
快速完全索引掃描是一種完全索引掃描,數據庫並不按特定的順序讀取索引塊。
數據庫僅訪問索引本身中的數據,而無需訪問表。
當索引包含了查詢所需的所有列,且索引鍵中至少一列具有 not null 約束時,快速完全索引掃描可以替代全表掃描。
7.3 索引範圍掃描 INDEX RANGE SCAN
create index idx_emp_deptno on emp (deptno);
select * from emp where deptno=10;
索引範圍掃描是對索引的有序掃描,具有以下特點:
在條件中指定了一個或多個索引前導列。
條件指定一個或多個表達式和邏輯 (布爾) 運算符的組合,並返回一個值( true、 false,或unknown)。
一個索引鍵可能對應0個、1個或更多個值。
通常,數據庫使用索引範圍掃描來訪問選擇性的數據。
選擇性是查詢所選擇的數據占總行數的百分比, 0 意味著沒有任何行,1 表示所有行。
選擇性與一個(或多個)查詢謂詞相關,比如where last_name like ‘a%‘。
值越接近 0的謂詞越具有選擇性,相反,越接近1的謂詞則越不具有選擇性。
7.4 唯一索引掃描 INDEX UNIQUE SCAN
select * from emp where empno=7788;
相對於索引範圍掃描,唯一索引掃描必須是 有0個 或 1個 rowid 與索引鍵相關聯。
當一個謂詞使用相等運算符引用了唯一索引鍵的所有列時,數據庫將執行唯一掃描。
只要找到了第一個記錄,唯一索引掃描就停止處理,因為不可能有第二個記錄滿足條件。
7.5 索引跳躍掃描 INDEX SKIP SCAN
如果在復合索引前導鍵列中有少量不同值,而在非前導鍵列中有大量不同值,此時使用跳躍掃描是有益的。
--建表
create table stu
(
sex varchar2(1) not null,
sid number(8)
);
--建復合索引
create index idx_stu_sexandsid on stu(sex,sid);
--插數據
declare
begin
for i in 1..1000 loop
insert into stu values(‘M‘,i);
end loop;
for i in 1001..2000 loop
insert into stu values(‘F‘,i);
end loop;
end;
--分析表
analyze table stu compute statistics;
--分析SQL執行計劃
select * from stu where sid=100;
索引跳躍掃描使用復合索引的邏輯子索引。
數據庫“跳躍地”通過單個索引,好像它在多個單獨的索引中搜索一樣。
當在查詢謂詞中未指定組合索引的前導列時,數據庫可能選擇索引跳躍掃描。
索引是根據指定的數據庫表列建立起來的順序。
它提供了快速訪問數據的途徑,並且可監督表的數據,使其索引所指向的列中的數據不重復。
分為單列索引和組合索引。
主鍵、唯一鍵系統自動創建索引,外鍵系統不會自動創建索引。
外鍵不創建索引時將導致父子表連接查詢時出現子表的全表掃描。
偽列rowid
1.定義:
oracle數據庫的表中的每一行數據都有一個唯一的標識符,或者稱為rowid,
在oracle內部通常就是使用它來訪問數據的。
rowid需要 10個字節的存儲空間,並用18個字符來顯示。
該值表明了該行在oracle數據庫中的物理具體位置。
可以在一個查詢中使用rowid來表明查詢結果中包含該值。
select rowid,emp.* from emp where rowid=‘AAAR3sAAEAAAACXAAA‘;
73196
2.存儲
保存rowid需要10個字節或者是80個位二進制位。
這80個二進制位分別是:
1. 數據對象編號,表明此行所屬的數據庫對象的編號,每個數據對象在數據庫建立的時候
都被唯一分配一個編號,並且此編號唯一。數據對象編號占用大約32位。
2. 對應文件編號,表明該行所在文件的編號,表空間的每一個文件標號都是唯一的。
文件編號所占用的位置是10位。
3. 塊編號,表明改行所在文件的塊的位置塊編號需要22位。
4. 行編號,表明該行在行目錄中的具體位置行編號需要16位。
這樣加起來就是80位。
3.顯示
Oracle的物理擴展ROWID有18位,每位采用64位編碼,分別用A~Z、a~z、0~9、+、/共64個字符表示。
A表示0,B表示1,……Z表示25,a表示26,……z表示51,0表示52,……,9表示61,+表示62,/表示63。
64位編碼表示的ROWID有18位,其中:
數據對象編號占6位;
文件編號占3位;
塊編號占6位;
行編號占3位。
select log(2,64),log(2,64)*18 from dual;
4.舉例:
select rowid,empno,ename from emp;
將會得到結果:
AAAR3sAAEAAAACXAAA
說明:
AAAR3s是數據庫對象編號,AAE是文件標號,AAAACX是塊編號,最後三位AAA(SMITH)是行編號。
5.驗證
5.1 驗證行編號
SMITH --> AAA
ALLEN --> AAB
不同人名 行號是遞增的。
5.2 驗證數文件標號
select FILE_ID as fid,FILE_NAME from dba_data_files where TABLESPACE_NAME=‘USERS‘ ;
FID FILE_NAME
---------- ---------------------------------------------
4 D:\APP\WANGXUWEI\ORADATA\ORCL\USERS01.DBF
FILE_ID=4,就是ROWID中AAE。
5.3 驗證數據庫對象編號
select * from dba_objects where object_name=‘EMP‘;
objectid
73196
73196 = AAAR3s ???
select ascii(‘R‘)-ascii(‘A‘) from dual;
R=17=17×64×64
3=26+26+3=55
select ascii(‘s‘)-ascii(‘a‘)+26 from dual;
s=44
AAAR3s=17×64×64+55×64+44
select 17*64*64+55*64+44 from dual;
73196
5.4 驗證塊編號
select * from dba_extents where segment_name=‘EMP‘;
extent_id =0
file_id =4
block_id =144
bytes =65536
blocks =8
144 = AAAACX ???
select ascii(‘X‘)-ascii(‘A‘) from dual;
23
AAAACX=2×64+23=
select 2*64+23 from dual;
151
AAAACX=151<>144 ???
6.dbms_rowid包
通過dbms_rowid包,可以直接得到具體的rowid包含的信息:
select dbms_rowid.rowid_object(rowid) object_id,
dbms_rowid.rowid_relative_fno(rowid) file_id,
dbms_rowid.rowid_block_number(rowid) block_id,
dbms_rowid.rowid_row_number(rowid) row_number,
rowid,emp.*
from emp;
通過dbms_rowid包,還可以查詢到表或記錄所在的文件
select file_id,file_name from dba_data_files
where file_id in (select distinct dbms_rowid.rowid_relative_fno(rowid) from scott.emp);
file_id file_name
4 D:\APP\ORADATA\ORCL\USERS01.DBF
Oracle_高級功能(2) 索引