##第四. 索引sql
###4.1 定義bash
索引是與表關聯的可選結構。經過建立索引可提升數據更新和檢索的性能。Oracle 索引提供到數據行的直接訪問路徑。服務器
能夠對錶的一個或多個列建立索引。建立索引後,Oracle 服務器會自動維護和使用索引。表數據的更新(如添加新行、更新行或刪除行)會自動傳播到全部相關的索引,這些對用戶來講是徹底透明的。ide
索引還能夠提升實施主鍵和惟一鍵約束條件時的性能。若是沒有索引,則每次對錶執行DML 操做時都會掃描整個表(全表掃描)。性能
###4.2 類型ui
有多種類型的索引結構,能夠根據須要使用。最經常使用的兩種 類型是:code
####4.2.1 B 樹索引orm
默認的索引類型; 採用平衡樹的形式. B 樹索引的鍵值存儲在平衡樹(B 樹)中,這樣能夠快速執行二進制搜索。排序
B 樹索引的結構索引
索引的頂層爲根,它包含指向索引中下一層次的條目。下一層次爲分支塊,它又指向位於索引中下一層次的塊。最底層是葉節點,它包含指向錶行的索引條目。葉塊是雙向關聯的,這便於按鍵值升序或降序掃描索引。
索引葉條目的格式
- 條目頭:存儲列數和鎖定信息
- 鍵列長度/值對:用於定義鍵中的列大小,後面跟隨列值(此類長度/值對的數目就是索引中的最大列數)。
- ROWID:包含鍵值的行的行ID
索引葉條目的特性
在非分區表的B 樹索引中:
- 當多個行具備相同的鍵值時,若是不壓縮索引,鍵值會出現重複
- 當某行包含的全部鍵列爲NULL 時,該行沒有對應的索引條目。所以,當WHERE 子句指定了NULL 時,將始終執行全表掃描
- 由於全部行屬於同一個段,因此要使用受限的ROWID 指向錶行
對索引執行DML 操做的效果
對錶執行DML 操做時,Oracle 服務器會維護全部索引。下面說明對索引執行DML 命令產生的效果:
- 執行插入操做致使在相應塊中插入索引條目。
- 刪除一行只致使對索引條目進行邏輯刪除。已刪除行所佔用的空間不可供後面新的葉條目使用。
- 更新鍵列致使對索引進行邏輯刪除和插入。PCTFREE設置對索引沒有影響,但建立時除外。即便索引塊的空間少於PCTFREE指定的空間,也能夠向索引塊添加新條目。
###4.3 建立
#建立索引 create index emp3_name_ix on emp3(emp3_name); #查看索引的信息 select index_name, index_type, table_name, table_type, uniqueness, status from user_indexes where table_name = 'EMP3'; INDEX_NAME INDEX_TYPE TABLE_NAME TABLE_TYPE UNIQUENES STATUS --------------- --------------- --------------- ----------- --------- -------- EMP3_ID_PK NORMAL EMP3 TABLE UNIQUE VALID EMP3_NAME_IX NORMAL EMP3 TABLE NONUNIQUE VALID #查看索引對應的列 SQL> select * from user_ind_columns where table_name = 'EMP3'; INDEX_NAME TABLE_NAME COLUMN_NAME COLUMN_POSITION COLUMN_LENGTH CHAR_LENGTH DESC --------------- --------------- --------------- --------------- ------------- ----------- ---- EMP3_ID_PK EMP3 EMP3_ID 1 22 0 ASC EMP3_NAME_IX EMP3 EMP3_NAME 1 10 10 ASC SQL> select * from emp3 where emp3_name = 'qa1'; EMP3_ID EMP3_NAME DEP_ID ---------- ---------- ---------- 2 qa1 2 Execution Plan ---------------------------------------------------------- Plan hash value: 215206995 -------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 33 | 1 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP3 | 1 | 33 | 1 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP3_NAME_IX | 1 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMP3_NAME"='qa1') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 3 consistent gets 0 physical reads 0 redo size 675 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
###4.4 決定使用全表掃描仍是使用索引
在大多數狀況下,全表掃描可能會致使更多的物理磁盤輸入輸出,可是全表掃描有時又可能會由於高度並行化的存在而執行的更快。
索引範圍掃描的整體原則是:
- 對於原始排序的表僅讀取少於表記錄數
40%
的查詢應該使用索引範圍掃描。 反之,讀取記錄數目多於表記錄數的40%
的查詢應該使用全表掃描。
- 對於未排序的表僅讀取少於表記錄數
7%
的查詢應該使用索引範圍掃描。 反之,讀取記錄數目多於表記錄數的7%
的查詢應該使用全表掃描。
###決定使用全表掃描仍是使用索引 SQL> select index_name, index_type, table_name, uniqueness, status from user_indexes where table_name = 'EMP3'; INDEX_NAME INDEX_TYPE TABLE_NAME UNIQUENESS STATUS --------------- ---------- --------------- ---------- -------- EMP3_ID_PK NORMAL EMP3 UNIQUE VALID EMP3_NAME_IX NORMAL EMP3 NONUNIQUE VALID SQL> select count(*) from emp3; COUNT(*) ---------- 19 #雖然有索引,可是此時是全表掃描 SQL> select * from emp3 where emp3_name = 'qa8'; EMP3_ID EMP3_NAME DEP_ID ---------- ---------- ---------- 16 qa8 2 Execution Plan ---------------------------------------------------------- Plan hash value: 2425169977 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 11 | 2 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP3 | 1 | 11 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("EMP3_NAME"='qa8') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 3 consistent gets 0 physical reads 0 redo size 671 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed #往emp3表添加數據 [root@hzvscmdb sql]# more insert_data.sql #!/bin/bash i=$1; while [ $i -le $2 ] do sqlplus hr/CCM%lab123@tony1521 <<EOF insert into emp3 values($i,'$3',$4); commit; quit; EOF let i=i+1 done echo "inset into emp3 table" [root@hzvscmdb sql]# ./insert_data.sql 90 100 dev 1 SQL> select max(emp3_id) from emp3; MAX(EMP3_ID) ------------ 100000 SQL> analyze table emp3 estimate statistics; Table analyzed. SQL> select blocks, empty_blocks, num_rows from user_tables where table_name = 'EMP3'; BLOCKS EMPTY_BLOCKS NUM_ROWS ---------- ------------ ---------- 374 10 101081 #查看一條數據 SQL> select * from emp3 where emp3_name = 'qa33333'; EMP3_ID EMP3_NAME DEP_ID ---------- ---------- ---------- 33333 qa33333 2 Execution Plan ---------------------------------------------------------- Plan hash value: 215206995 -------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 14 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP3 | 1 | 14 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP3_NAME_IX | 1 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMP3_NAME"='qa33333') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 4 consistent gets 0 physical reads 0 redo size 681 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed #不加條件的查詢,索引掃描 SQL> select count(*) from emp3; COUNT(*) ---------- 100000 Execution Plan ---------------------------------------------------------- Plan hash value: 2418373429 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 70 (2)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| EMP3_ID_PK | 101K| 70 (2)| 00:00:01 | ---------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 2 recursive calls 2 db block gets 262 consistent gets 37 physical reads 176 redo size 526 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed #沒有建立索引的,查詢全表掃描 SQL> select count(*) from emp3 where dep_id = 2; COUNT(*) ---------- 85726 Execution Plan ---------------------------------------------------------- Plan hash value: 1396384608 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2 | 104 (1)| 00:00:02 | | 1 | SORT AGGREGATE | | 1 | 2 | | | |* 2 | TABLE ACCESS FULL| EMP3 | 50541 | 98K| 104 (1)| 00:00:02 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("DEP_ID"=2) Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 373 consistent gets 0 physical reads 0 redo size 528 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed SQL> select count(*) from emp3 where emp3_name like 'qa%'; COUNT(*) ---------- 85726 Execution Plan ---------------------------------------------------------- Plan hash value: 3884997069 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | INDEX RANGE SCAN| EMP3_NAME_IX | 7 | 56 | 2 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMP3_NAME" LIKE 'qa%') filter("EMP3_NAME" LIKE 'qa%') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 395 consistent gets 0 physical reads 0 redo size 528 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed