索引使用簡介 一、 關於索引的知識 要寫出運行效率高的sql,需要對索引的機制有一定了解,下面對索引的基本知識做一介紹。 1、 索引的優點和局限 索引可以提高查詢的效率,但會降低dml操作的效率。 所以建立索引時需要權衡。對於dml操作比較頻繁的表,索引的個數不宜太多。 2、 什么樣的列需要建索引? 經常用於查詢、排序和分組的列(即經常在where、order或group by子句中出現的列)。 3、 主鍵索引和復合索引 對於一張表的主鍵,系統會自動為其建立索引。 如果一張表的幾列經常同時作為查詢條件,可為其建立復合索引。 4、 建立索引的語句 create index i_staff on staff (empno); create index i_agent on agent (empno, start_date); 5、 刪除索引的語句 drop index I_staff; drop index I_agent; 6、 查詢索引的語句 法一:利用數據字典 表一:all_indexes 查看一張表有哪些索引以及索引狀態是否有效 主要字段: index_name, table_name, status 例如:select index_name, status from all_indexes where table_name=’STAFF_INFO’; INDEX_NAME STATUS --------------------- ----------- I_STAFF VALID 表二:all_ind_columns 查看一張表在哪些字段上建了索引 主要字段: table_name, index_name, column_name, column_position 例如: select index_name, column_name, column_position from all_ind_columns where table_name=’AGENT’ INDEX_NAME COLUMN_NAME COLUMN_POSITON --------------------- ----------------------- -------------------------- I_AGENT EMPNO 1 I_AGENT START_DATE 2 由此可見,agent表中有一個復合索引(empno, start_date ) 法二:利用toad工具 toad用戶界面比sql*plus友好,並且功能強大。你可以在toad編輯器中鍵入表名,按F4,便可見到這張表的表結構以及所有索引列等基本信息。 7、 索引的一些特點 1): 不同值較多的列上可建立檢索,不同值少的列上則不要建。比如在雇員表的“性別”列上只有“男”與“女”兩個不同值,因此就沒必要建立索引。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。 2): 如果在索引列上加表達式,則索引不能正常使用 例如:b1,c1分別是表b,c的索引列 select * from b where b1/30< 1000 ; select * from c where to_char(c1,’YYYYMMDD HH24:MI:SS’) = ‘200203 14:01:01’; 以上都是不正確的寫法 3): where子句中如果使用in、or、like、!=,均會導致索引不能正常使用 例如:select * from b where b1=30 or b1=40; 4): 使用復合索引進行查詢時必須使用前置列 例如表a上有一個復合索引(c1,c2,c3),則c1為其前置列 如果用c1或c1+c2或c1+c2+c3為條件進行查詢,則該復合索引可以發揮作用,反之,用c2或c3或c2+c3進行查詢,則該索引不能起作用。 二. 書寫sql注意事項: 1、 避免給sql語句中引用的索引列添加表達式: 典型實例: b1,c1分別是表b,c的索引列: 1) select * from b where b1/30< 1000 ; 2) select * from c where to_char(c1,’YYYYMMDD HH24:MI:SS’) = ‘200203 14:01:01’; 替代方案: 1) select * from b where b1 < 30000; 2) select * from c where c1 = to_date(‘20020301 14:01:01’, ‘YYYYMMDD HH24:MI:SS’); 注:在lbs中有兩個重要字段,pol_info中的undwrt_date和prem_info中的payment_date,這兩個日期是帶時分秒的,所以經常有同事用to_char 來查詢某一時間段的數據。 例如:select count(*) from pol_info where to_char(undwrt_date,’YYYYMMDD’)=’20020416’; select count(*) from prem_info where to_char(undwrt_date,’YYYYMM’)=’200203’; 替代方案: select count(*) from pol_info where undwrt_date>=to_date(’20020416’,’YYYYMMDD’) and undwrt_date select count(*) from prem_info where payment_date>=to_date(’20020301’,’YYYYMMDD’) and payment_date 2、 避免在where子句中使用in、or、like、!= 典型實例: a1是a表上的索引列: 1) select * from a where ( a1 = ‘0’ and ...) or (a1 = ‘1’ and ...); 2) select count(*) from a where a1 in (‘0’,’1’) ; 替代方案: 1) select * from a where a1 = ‘0’ and ... union select * from a where a1 = ‘1’ and ... 2) select count(*) from a where a1 = ‘0’; select count(*) from a where a1 = ‘1’; 然后做一次加法運算;或者直接用存儲過程來實現; 小結: 對字段使用了 ‘in,or,like’ 做條件、對字段使用了不等號 ‘!=’,均會使索引失效;如果不產生大量重復值,可以考慮把子句拆開;拆開的子句中應該包含索引,或者使用union連結符代替。另一種方式是使用存儲過程,它使SQL變得更加靈活和高效。 3、 建立適當的索引 曾經接過開發的一個統計sql, select … from tablea where cola=… and … 運行效率非常慢,經查tablea數據量巨大,再查all_ind_columns,發現cola是tablea的一個復合索引中的一列,但不是前置列。象這種情況,就需要與開發商量,是否針對cola建一個索引。 4、 like和substr 對於‘like’和‘substr’,其效率並沒有多大分別。但是,當所搜索的值不存在時,使用‘like’的速度明顯大於‘substr’。 所以:select * from a where substr(a1,1,4) = '5378' 可以用like替代 select * from a where a1 like ‘5378%’; 5、 寫where條件時,有索引字段的判斷在前,其它字段的判斷在后;如果where條件中用到復合索引,按照索引列在復合索引中出現的順序來依次寫where條件; 6、使用多表連接時,在from子句中,將記錄數少的表放在后面,可提高執行效率; 7、避免使用not in not in 是效率極低的寫法,盡量使用minus或外連接加以替代 典型實例: 1) select col1 from tab1 where col1 not in (select col1 from tab2); 2) select sum(col2) from tab1 where col1 not in (select col1 from tab2); 替代方案 select col1 from tab1 minus select col1 from tab2; select sum(a.col2) from tab1 a, tab2 b where a.col1=b.col2(+) and b.col1 is null; 8、多表查詢時,如果其中一個表的記錄數量明顯大於其他表,則可以先對此表進行查詢后,再與其他小表進行表連接。 典型實例: select a.plan_code, b.dno, c,tno, sum(a.tot_modal_prem), from prem_info a, dept_ref b, plan_type c where substr(a.deptno,1,7) = substr(b.deptno,1,7) and a.plan_code = c.plan_code group by b.dno, c.tno, a.plan_code; 替代方案: select b.dno, c.tno, a.plan_code, a.tot_amount from (select plan_code, deptno, sum(tot_modal_prem) tot_amount from prem_info group by deptno, plan_code) a dept_ref b, plan_type c where substr(a.deptno,1,7) = substr(b.deptno,1,7) and a.plan_code = c.plan_code group by b.dno, c.tno, a.plan_code; 小結: 由於prem_info表的記錄數遠遠大於dept_ref表和plan_type表中的記錄數, 所以首先從prem_info表中查詢需要的記錄,此時記錄數已經被大量縮小,然后再和其他兩個表連接,速度會得到很大改善! 9、查詢數量較大時,使用表連接代替IN,EXISTS,NOT IN,NOT EXISTS等。 典型實例: a、使用IN: select sum(col2) from tab1 where col1 in (select col1 from tab2); 使用EXISTS:: select sum(col2) from tab1 a where exists ( select * from tab2 where col1=a.col1); b、使用NOT IN: select sum(col2) from tab1 where col1 not in (select col1 from tab2); 使用NOT EXISTS: select sum(col2) from tab1 a where not exists ( select * from tab2 where col1=a.col1); 替代方案: a、使用連接: select sum(a.col2) from tab1 a,tab2 b where a.col1=b.col2; b、使用外連接: select sum(a.col2) from tab1 a,tab2 b where a.col1=b.col2(+) and b.col1 is null; \•索引是建立在表的一列或多個列上的輔助對象,它有利於快速訪問表的數據。 •索引由於其內在的結構,它具有某些內在的開銷,這些開銷依賴於為了檢索由索引中ROWID指定的行所訪問的表中的塊數,並且這個開銷可能會超過進行全表掃描的成本。 聚集索引確定表中數據的物理順序。聚集索引類似於電話簿,按姓氏排列數據。由於聚集索引規定數據在表中的物理存儲順序,因此一個表只能包含一個聚集索引。但該索引可以包含多個列(組合索引),就像電話簿按姓氏和名字進行組織一樣。 聚集索引對於那些經常要搜索范圍值的列特別有效。使用聚集索引找到包含第一個值的行后,便可以確保包含后續索引值的行在物理相鄰。例如,如果應用程序執行的一個查詢經常檢索某一日期范圍內的記錄,則使用聚集索引可以迅速找到包含開始日期的行,然后檢索表中所有相鄰的行,直到到達結束日期。這樣有助於提高此類查詢的性能。同樣,如果對從表中檢索的數據進行排序時經常要用到某一列,則可以將該表在該列上聚集(物理排序),避免每次查詢該列時都進行排序,從而節省成本。 當索引值唯一時,使用聚集索引查找特定的行也很有效率。例如,使用唯一雇員 ID 列 emp_id 查找特定雇員的最快速的方法,是在 emp_id 列上創建聚集索引或 PRIMARY KEY 約束。 非聚集索引與聚集索引一樣有 B 樹結構,但是有兩個重大差別: 數據行不按非聚集索引鍵的順序排序和存儲。非聚集索引的葉層不包含數據頁。 相反,葉節點包含索引行。每個索引行包含非聚集鍵值以及一個或多個行定位器,這些行定位器指向有該鍵值的數據行(如果索引不唯一,則可能是多行)。 非聚集索引可以在有聚集索引的表、堆集或索引視圖上定義。在 Microsoft® SQL Server™ 2000 中,非聚集索引中的行定位器有兩種形式: 如果表是堆集(沒有聚集索引),行定位器就是指向行的指針。該指針用文件標識符 (ID)、頁碼和頁上的行數生成。整個指針稱為行 ID。如果表沒有聚集索引,或者索引在索引視圖上,則行定位器就是行的聚集索引鍵。如果聚集索引不是唯一的索引,SQL Server 2000 將添加在內部生成的值以使重復的鍵唯一。用戶看不到這個值,它用於使非聚集索引內的鍵唯一。SQL Server 通過使用聚集索引鍵搜索聚集索引來檢索數據行,而聚集索引鍵存儲在非聚集索引的葉行內。 由於非聚集索引將聚集索引鍵作為其行指針存儲,因此使聚集索引鍵盡可能小很重要。如果表還有非聚集索引,請不要選擇大的列作為聚集索引的鍵。 關於索引(轉) 索引的三個問題 索引( Index )是常見的數Database 的性能。雖然有許多,還是有不少的人對它存在誤解Oracle 8.1.7 OPS on HP N se使用不同的方法后,數據的比較明白事情的關鍵。 據庫對象,它的設置好壞、使用是否資料講索引的用法, DBA 和 Develo,因此針對使用中的常見問題,講三ries ,示例全部是真實數據,讀者不。本文所講基本都是陳詞濫調,但是 得當,極大地影響數據庫應用程序和per 們也經常與它打交道,但筆者發現個問題。此文所有示例所用的數據庫是需要注意具體的數據大小,而應注意在筆者試圖通過實際的例子,來真正讓您 第一講、索引並非總是最佳選擇 如果發現Oracle 在有索引,Oracle 確實會選擇全表掃描 的情況下,沒有使用索引,這並不是(Full Table Scan),而非索引掃描 Oracle 的優化器出錯。在有些情況下(Index Scan)。這些情況通常有: 1. 表未做statistics, 或 者 statistics 陳舊,導致 Oracle 判斷失誤。 2. 根據該表擁有的記錄數和數據塊數,實際上全表掃描要比索引掃描更快。 對第1種情況,最常見的例子,是以下這句sql 語句: select count(*) from mytable; 在未作statistics 之前,它使用全表掃描,statistics 之后,使用的是 INDEX (FAST FULL S得不好,也會導致Oracle 不使用索引。 需要讀取6000多個數據塊(一個數據塊是8k), 做了CAN) ,只需要讀取450個數據塊。但是,statistics 做 第2種情況就要復雜得多。一般概念上都認為掃描快。為了講清楚這個問題,這里先介紹一下Or:CF(Clustering factor) 和 FF(Filtering fact 索引比表快,比較難以理解什么情況下全表掃描要比索引acle 在評估使用索引的代價(cost)時兩個重要的數據or). CF: 所謂 CF, 通俗地講,就是每讀入一個索引塊,要對應讀入多少個數據塊。 FF: 所謂 FF, 就是該sql 語句所選擇的結果集,占總的數據量的百分比。 大約的計算公式是:FF * (要讀入的數據塊塊數。需要讀入全表掃描需要讀入的數據塊數等 CF + 索引塊個數) ,由此估計出,的數據塊越多,則 cost 越大,Orac於該表的實際數據塊數) 一個查詢, 如果使用某個索引,會需le 也就越可能不選擇使用 index. ( 其核心就是, CF 可能會比實際的數據塊數量建立時,索引中的記錄與表中的記錄有良好的對應對應關系越來越亂,CF 也越來越大。此時需要 DB 大。CF 受到索引中數據的排列方式影響,通常在索引剛關系,CF 都很小;在表經過大量的插入、修改后,這種A 重新建立或者組織該索引。 如果某個sql 語句以前一直重新整理該索引了。 使用某索引,較長時間后不再使用, 一種可能就是 CF 已經變得太大,需要 FF 則是Oracle 根據 stati,最大值是409654,考慮以下sq stics 所做的估計。比如, mytablesl 語句: 表有32萬行,其主鍵myid的最小值是1 Select * from mytables where myid>=1; 和 Select * from mytables where myid>=400000 這兩句看似差不多的 sql 者的 FF 可能只有 1%。如果它實際上,在我們的數據庫上的測 語句,對Oracle 而言,卻有巨大的的CF 大於實際的數據塊數,則Oracl試驗證了我們的預測. 以下是在HP 差別。因為前者的 FF 是100%, 而后e 可能會選擇完全不同的優化方式。而 第二講、索引也有好壞 索引有 B tree 索引, Bit全稱是Balanced , 其意義是,有一個字段(Single column),Function-based index. 許多de map 索引, Reverse b tree 索引,從 tree 的 root 到任何一個leaf 也可以有多個字段(Composite),veloper 都傾向於使用單列B 樹索引 等。最常用的是 B tree 索引。 B 的,要經過同樣多的 level. 索引可以只最多32個字段,8I 還支持 。 所謂索引的好壞是指: 1,索引不是越多越好。特別是大量從來或者個索引即會降低性能,而且在一個sql 中, Oracl 幾乎不用的索引,對系統只有損害。OLTP系統每表超過5e 從不能使用超過 5個索引。 2,很多時候,單列索引不如復合索引有效率。 3,用於多表連結的字段,加上索引會很有作用。 那么,在什么情況下單列索所查詢的列,全部都出現在復合使用多個單列索引要快得多。( 引不如復合索引有效率呢?有一種情索引中時,此時由於 Oracle 只需要此時,這種優化方式被稱為 Index o 況是顯而易見的,那就是,當sql 語句查詢索引塊即可獲得所有數據,當然比nly access path) 第三講、索引再好,不用也是白搭 拋開前面所說的,假設你設不用,那么,需要做的第一件事 置了一個非常好的索引,任何傻瓜都情,是審視你的 sql 語句。 知道應該使用它,但是Oracle 卻偏偏 Oracle 要使用一個索引,有一些最基本的條件: 1, where 子句中的這個字段,必須是復合索引的第一個字段; 2, where 子句中的這個字段,不應該參與任何形式的計算 具體來講,假設一個索引是按 f1, f2, f3的= : var2, 則因為 f2 不是索引的第1個字段,無 次序建立的,現在有一個 sql 語句, where 子句是 f2 法使用該索引。 第2個問題,則在我們之中非常嚴重。以下是從 實際系統上面抓到的幾個例子: Select jobid from mytabs where isReq='0' and to_date (updatedate) >= to_Date ( '2001-7-18', 'YYYY-MM-DD'); 以上的例子能很容易地進行和 內存資源。 改進。請注意這樣的語句每天都在我 們的系統中運行,消耗我們有限的cpu 除了1,2這兩個我們必須牢記於心的原則外,。這里我只講哪些操作或者操作符會顯式(explic 還應盡量熟悉各種操作符對 Oracle 是否使用索引的影響itly)地阻止 Oracle 使用索引。以下是一些基本規則: 1, 如果 f1 和 f2 是同一個表的兩個字段,則 f1>f2, f1>=f2, f1 2, f1 is null, f1 is no t null, f1 not in, f1 !=, f1 lik e ‘%pattern%’; 3, Not exist 4, 某些情況下,f1 in 也會不用索引; 對於這些操作,別無辦法,許可以將 in 操作改成 比較操 只有盡量避免。比如,如果發現你的作 + union all。筆者在實踐中發現 sql 中的 in 操作沒有使用索引,也很多時候這很有效。 但是,Oracle 是否真正使用索引,使用索引,對所寫的復雜的 sql, 在將它寫入應用程序之前Oracle 對該 sql 的解析(plan),可以明確地看是否真正有效,還是必須進行實地的測驗。合理的做法是,先在產品數據庫上做一次explain . explain 會獲得到 Oracle 是如何優化該 sql 的。 如果經常做 explain, 就會划往往不盡如人意。事實上,將然這已經是題外話了。發現,喜愛寫復雜的 sql 並不是個復雜的 sql 拆開,有時候會極大地好習慣,因為過分復雜的sql 其解析計提高效率,因為能獲得很好的優化。當沒有對name建索引之前,oracle使用全表掃描。時間長短視數據庫參數db_file_multiblock_read_count的設置,一次讀取db_file_multiblock_read_count*db_block_size(db_cache_dize)的數據. 建立索引之后,oracle使用index range scan,一次讀取一條索引。