oracle sql語句優化

一般的SQL優化技巧

1.避免使用“*”代替所有列

在使用select語句查詢一個表的所有信息時,用戶會經常選擇使用“*”來代替所有列,尤其是在表中的列比較多,而且列名又難記的時候。而使用“*”代替列名,會給SQL語句的編寫帶來方便,卻會給SQL語句的執行帶來麻煩。首先了解SQL語句的執行步驟:
1.語法分析與編譯
在語法分析與編譯階段,oracle會使用哈希函數爲該SQL語句在庫緩存中分配一個SQL區,然後在該SQL區搜索該語句是否存在,如果已經存在則查詢數據字典,檢查必須的存取權限等;如果不存在,則分析、編譯用戶的SQL語句。
2.執行
對SQL語句進行分析與編譯,瞭解SQL語句的內容後,執行該SQL語句。
3.存取數據
將數據從數據文件中讀取出來並存放到數據緩衝區中,或者將數據緩衝區中的數據寫入數據文件中。
小結:用“*”查詢的時候oracle系統需要首先通過數據字典來將語句中的“*”轉換成相應表的所有列名,這自然要比直接使用列名花費更多的時間。

2.用truncate代替delete

在使用delete語句刪除表中的所有數據行時,oracle會對這些行進行逐行的刪除,並且使用回滾段來記錄刪除操作。
提示:通過上面的介紹可以發現兩個問題,比較明顯的問題是,在使用delete刪除時,oracle需要花費時間去記錄刪除操作;而另一個問題則不是那麼明顯,那就是delete刪除採取的是逐行刪除的形式,也就是說所有的數據行並不是同一時間被刪除的,這就要求在數據行的刪除過程中,oracle系統不能出現什麼意外情況導致刪除操作中止。
如果確定要刪除表中的所有行,建議使用truncate語句。使用truncate語句刪除表中的所有數據行時,oracle不會在撤銷表空間中記錄刪除操作。這就提高了語句的執行速度,而且這種刪除時一次性的,也就是所有的數據行是在同一時間被刪除。

3.在確保完整性的情況下多用commit語句

在前面的內容中已經介紹過,當用戶執行DML操作後,如果不使用commit命令進行提交,則oracle會在回滾段中記錄DML操作,以便用戶在使用rollback命令時對數據進行恢復。oracle實現這種數據回滾功能,自然要花費相應的時間與空間。
所以,在確保數據完整性的情況下,儘量及時的使用commit命令對DML操作進行提交。
提示:使用commit命令後,系統將釋放回滾段上的記錄DML操作信息、被程序語句獲得的鎖、redo log buffer中的空間以及oracle系統管理前面3種資源所需要的其他花費。

4.減少表的查詢次數

儘量減少表的查詢次數,主要是指能使用一次查詢獲得的數據儘量不要去通過兩次或更多次的查詢獲得。
例如,有兩個表:學生表student和教師表teacher,現在想知道某個學生(sname)的教師名稱(tname),可以通過兩種方式實現,一種是先從學生表中查詢出這個學生所對應的教師編號(tid),然後從教師表中查詢出這個教師編號所對應的教師名稱,如下:
select tname from teacher
where tid=(select tid from student where sname='羊羊');
這個過程,先從student表中查出tid,再從teacher表中查出相對應的數據,共使用了兩次查詢。
另一種查詢方式是使用表的連接方式,如下:
select tname from teacher t full join student s on t.tid=s.tid where s.sname='羊羊';

5.用exists替代in

select distinct tname from teacher t left join student s on t.tid=s.tid where t.tid in(select tid from student);
上例是判斷某名教師是否教了學生時,採用的in操作符,這時oracle會遍歷查詢學生表中所有學生的TID,判斷該教師的編號是否在這個TID列表中。而如果採用exist操作符,示例如下:
select distinct tname from teacher t left join student s on t.tid=s.tid where exists (select tid from teacher where t.tid=s.tid);
1.in
確定給定的值是否與子查詢或列表中的值相匹配。使用in時,子查詢先產生結果集,然後主查詢再去結果集中尋找符合要求的字段列表,符合要求的輸出,反之則不輸出。
2.exists
指定一個子查詢,檢測行的存在。它不返回列表的值,只返回一個true或false。其運行方式是先運行主查詢一次,再去子查詢中查詢與其對應的結果,如果子查詢返回true則輸出,反之則不輸出。再根據主查詢中的每一行去子查詢中查詢。
提示:由於in操作符需要進行確切的比較,而exists只需要驗證存不存在,所以使用in將會比使用exists花費更多的查詢成本,因此能使用exists代替in的地方,應該儘量使用exists。另外,儘量使用not exists替代not in。

6.用where替代having

統計student表中除“java 1班”以外的每個班級的學生人數,如下:
select sclass,count(*) from student where sclass !='java 1班' group by sclass;
上面按sclass列對行進行分組,並使用where子句對行進行過濾,不統計"java 1班"的學生人數。而使用having子句同樣可以實現這種過濾,如下:
select sclass,count(*) from student group by sclass having sclass !='java 1班';
雖然where子句與having子句都可以用來過濾數據行,但having子句只會在檢索出所有記錄後纔對結果集進行過濾,這種處理需要排序、總計等操作,而使用where子句就會減少這方面的開銷。因此,那些簡單的過濾條件應該讓where實現,而儘量讓having子句對統計函數的結果進行過濾。

7.使用“<=”替代“<”

如果想查詢不及格的學生,where條件中可以寫出score<60或者score<=59。這兩條語句的區別就在於如果使用“<60”,則oracle會首先定位到60,然後再去尋找比60小的數,而如果使用“<=59”,則oracle會直接定位到第一個比60小的數,也就是59。雖然這種優化顯得差別不大,但是在查詢量大較大,尤其是在循環語句中使用這兩個比較操作符時,區別還是很明顯的。

表的連接

1.選擇from表的順序

所謂from表的順序,是指select語句的from子句中的表與表之間的先後順序。例如查詢某名學生的教師名稱,需要連接查詢student和teacher表,語句如下:
select tname from student s,teacher t where s.tid=t.tid and s.sid=1;
上述示例中,將teacher表放在student表之前。如果單從查詢結果來說,哪個表放在前面都一樣,但是如果從查詢效率這個角度來考慮,表之間的順序就不能是隨意的。
一般來說,oracle的解析器在處理from子句中的表時,是按照從右到左的順序,也就是說from子句中最後指定的表將被oracle首先處理,oracle將它作爲驅動表(Driving Table),並對該表的順序進行排序,之後再掃描倒數第二個表,最後將所有從第二個表中檢查出來的記錄與第一個表中的合適記錄進行合併。
因此,建議在使用表的連接查詢時,選擇記錄條數最少的表作爲驅動表,也就是將它作爲from子句中的最後一個表。

2.where子句的連接順序

在查詢語句中,經常需要使用where子句爲查詢添加條件,對結果進行篩選,很多時候條件不僅僅只有一個,甚至還需要在where子句中使用子查詢。例如,查詢student表中學生成績大於60,且教師姓名不是“周建明”的學生的信息。如下:
select * from student s where s.sscore>60 and '周建明' !=(select t.tname from teacher t where t.tid=s.tid);
當where子句中所指的條件不止一個時,應該將表之間的連接子查詢放在其他條件的前面。因爲oracle解析where子句的順序是從下至上,所以應該將那些可以過濾掉最大數量記錄的條件放在where子句的末尾。因此上述的SQL語句需要進行調整,修改後的語句如下:
select * from student s where '周建明' !=(select t.tname from teacher t where t.tid=s.tid) and s.sscore>60;

3.使用表的別名

上述示例中的tid列,student表與teacher表中都有它,所以必須指定該列所屬的表名,否則oracle會出現解析錯誤。雖然有些列只屬於一個表,例如sid列只存在於student表中,但是如果在SQL語句中不指明它所屬的表,那麼這部分工作將會由oracle自己去完成,這顯然會增加oracle的負擔。所以能夠使用表的別名時就儘量使用。

有效使用索引

1.使用索引的基本事項

通過索引查詢要比全表掃描快得多,當oracle找出執行查詢的最佳路徑時,oracle優化器就會使用索引。但是,在認識到索引的優點的同時,也要注意使用索引所需要付出的代價。索引需要佔據存儲空間,需要定期進行維護,每當表中有記錄增減或索引列被修改時,索引本身也會被修改,這就意味着每條記錄的insert、delete、update操作都要使用更多的磁盤I/O。而且很多已經不必要的索引,甚至會影響查詢效率。
所以在使用索引時,應該注意什麼情況下使用它,一般創建索引有如下幾個基本原則:
1.對於經常以查詢關鍵字爲基礎的表,並且該表中的數據行是均勻分佈的。
2.以查詢關鍵字爲基礎,表中的數據行隨機排序。
3.表中包含的列數相對比較少。
4.表中的大多數查詢都包含相對簡單的where子句。
除了需要知道什麼情況下使用索引以外,還需要知道在創建索引時選擇表中的哪些列作爲索引列。一般選擇索引列有如下幾個原則:
1.經常在where子句中使用的列。
2.經常在表連接查詢中用於表之間連接的列。
3.不宜將經常修改的列作爲索引列。
4.不宜將經常在where子句中使用,但與函數或操作符相結合的列作爲索引列。
5.對於取值較少的列,應考慮建立位圖索引,而不應該採用B樹索引。
提示:除了所查詢的表沒有索引,或者需要返回表中的所有行時,oracle會進行全表掃描以外,如果查詢語句中帶like關鍵字,並使用了通配符“%”,或者對索引列使用了函數,oracle同樣會對全表掃描。

2.避免對索引列使用NOT關鍵字

在索引列上使用not關鍵字,與在索引列上使用函數一樣,都會導致oracle進行全表掃描。實際上索引的作用是快速地告訴用戶在表中有什麼數據,而不能用來告訴用戶在表中沒有什麼數據。所以,類似的“!=”等操作符也應該避免在索引列上使用。

3.避免對唯一索引列使用is(not) null

使用unique關鍵字可以爲列添加唯一性索引,也就是說列的值不允許有重複值,但是,多個null值卻可以同時存在,同時oracle認爲兩個空值是不相等的。
所以,在向表中的唯一索引列添加數據時,可以添加無數條null記錄,但是由於這些記錄都是空值,所以在索引中並不存在這些記錄。因此,當在where子句中使用is null或is not null對唯一索引列進行空值比較時,oracle將會停止使用該列上的唯一索引,這就會導致oracle進行全表掃描。

4.選擇複合索引主列

索引不僅僅可以基於單獨的列,還可以基於多個列,在多個列上創建的索引叫複合索引。例如爲student表中的sname列與sscore列建立複合索引,如下:
create index name_score_index on student(sname,sscore);
在創建複合索引時,不可避免的要面對多個列的前後順序,這個順序並不是隨意的,它會影響索引的使用效率。一般在創建複合索引時,應該注意如下幾個原則:
1.選擇經常在where子句中使用且由and操作符連接的列作爲複合索引列。
2.選擇where子句中使用頻率相對較高的列作爲複合索引的主列。
例如,如果student表中的sscore列在where子句中的使用頻率比sname高,則創建索引時應該將sscore列放在sname前面。
注意:只有當複合索引的第一列,也就是主鍵列,被where子句使用時,oracle纔會使用該複合索引。例如,如果在where子句中只使用了sscore列,則oracle不會使用上面創建的複合索引name_score_index。
另外,在使用複合索引時,where子句中列的順序應該與複合索引中索引列的順序保持一致,例如有如下兩條select語句:
select * from student where sname='羊羊' and sscore=94;
select * from student where sscore=94 and sname='羊羊';
合理的where子句應該與上述第一條select語句中的where子句相似,也就是保持where子句中列的順序與複合索引中列的順序一致。

5.監視索引是否被使用

因爲不必要的索引會對錶的查詢效率起副作用,所以在實際應用中應該經常檢查索引是否被使用,這需要用到索引的監視功能。
對name_score_index索引創建監視:
alter index name_score_index monitoring usage;
上述語句添加了對name_score_index索引的監視,然後就可以通過V$OBJECT_USAGE視圖來了解該索引的使用狀態,如下:
select table_name,index_name,monitoring from v$object_usage;
上面table_name字段描述索引所在表,index_name字段描述索引名稱,monitoring字段表示索引是否處於激活狀態。













發佈了28 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章