1、問題的提出java
在應用系統開發初期,因爲開發數據庫數據比較少,對於查詢SQL語句,複雜視圖的的編寫等體會不出SQL語句各類寫法的性能優劣,可是若是將應用系統提交實際應用後,隨着數據庫中數據的增長,系統的響應速度就成爲目前系統須要解決的最主要的問題之一。系統優化中一個很重要的方面就是SQL語句的優化。對於海量數據,劣質SQL語句和優質SQL語句之間的速度差異能夠達到上百倍,可見對於一個系統不是簡單地能實現其功能就可,而是要寫出高質量的SQL語句,提升系統的可用性。sql
在多數狀況下,Oracle使用索引來更快地遍歷表,優化器主要根據定義的索引來提升性能。可是,若是在SQL語句的where子句中寫的SQL代碼不合理,就會形成優化器刪去索引而使用全表掃描,通常就這種SQL語句就是所謂的劣質SQL語句。在編寫SQL語句時咱們應清楚優化器根據何種原則來刪除索引,這有助於寫出高性能的SQL語句。數據庫
2、SQL語句編寫注意問題oracle
下面就某些SQL語句的where子句編寫中須要注意的問題做詳細介紹。在這些where子句中,即便某些列存在索引,可是因爲編寫了劣質的SQL,系統在運行該SQL語句時也不能使用該索引,而一樣使用全表掃描,這就形成了響應速度的極大下降。函數
1. IS NULL 與 IS NOT NULL工具
不能用null做索引,任何包含null值的列都將不會被包含在索引中。即便索引有多列這樣的狀況下,只要這些列中有一列含有null,該列就會從索引中排除。也就是說若是某列存在空值,即便對該列建索引也不會提升性能。性能
任何在where子句中使用is null或is not null的語句優化器是不容許使用索引的。學習
2. 聯接列優化
對於有聯接的列,即便最後的聯接值爲一個靜態值,優化器是不會使用索引的。咱們一塊兒來看一個例子,假定有一個職工表(employee),對於一個職工的姓和名分紅兩列存放(FIRST_NAME和LAST_NAME),如今要查詢一個叫比爾.克林頓(Bill Cliton)的職工。spa
下面是一個採用聯接查詢的SQL語句,
select * from employss where first_name||''||last_name ='Beill Cliton';
上面這條語句徹底能夠查詢出是否有Bill Cliton這個員工,可是這裏須要注意,系統優化器對基於last_name建立的索引沒有使用。
當採用下面這種SQL語句的編寫,Oracle系統就能夠採用基於last_name建立的索引。
*** where first_name ='Beill' and last_name ='Cliton';
. 帶通配符(%)的like語句
一樣以上面的例子來看這種狀況。目前的需求是這樣的,要求在職工表中查詢名字中包含cliton的人。能夠採用以下的查詢SQL語句:
select * from employee where last_name like '%cliton%';
這裏因爲通配符(%)在搜尋詞首出現,因此Oracle系統不使用last_name的索引。在不少狀況下可能沒法避免這種狀況,可是必定要心中有底,通配符如此使用會下降查詢速度。然而當通配符出如今字符串其餘位置時,優化器就能利用索引。在下面的查詢中索引獲得了使用:
select * from employee where last_name like 'c%';
4. Order by語句
ORDER BY語句決定了Oracle如何將返回的查詢結果排序。Order by語句對要排序的列沒有什麼特別的限制,也能夠將函數加入列中(象聯接或者附加等)。任何在Order by語句的非索引項或者有計算表達式都將下降查詢速度。
仔細檢查order by語句以找出非索引項或者表達式,它們會下降性能。解決這個問題的辦法就是重寫order by語句以使用索引,也能夠爲所使用的列創建另一個索引,同時應絕對避免在order by子句中使用表達式。
5. NOT
咱們在查詢時常常在where子句使用一些邏輯表達式,如大於、小於、等於以及不等於等等,也可使用and(與)、or(或)以及not(非)。NOT可用來對任何邏輯運算符號取反。下面是一個NOT子句的例子:
... where not (status ='VALID')
若是要使用NOT,則應在取反的短語前面加上括號,並在短語前面加上NOT運算符。NOT運算符包含在另一個邏輯運算符中,這就是不等於(<>)運算符。換句話說,即便不在查詢where子句中顯式地加入NOT詞,NOT仍在運算符中,見下例:
... where status <>'INVALID';
對這個查詢,能夠改寫爲不使用NOT:
select * from employee where salary<3000 or salary>3000;
雖然這兩種查詢的結果同樣,可是第二種查詢方案會比第一種查詢方案更快些。第二種查詢容許Oracle對salary列使用索引,而第一種查詢則不能使用索引。
雖然這兩種查詢的結果同樣,可是第二種查詢方案會比第一種查詢方案更快些。第二種查詢容許Oracle對salary列使用索引,而第一種查詢則不能使用索引。
===============================================================================================
咱們要作到不但會寫SQL,還要作到寫出性能優良的SQL,如下爲筆者學習、摘錄、並彙總部分資料與你們分享!
(1) 選擇最有效率的表名順序(只在基於規則的優化器中有效): ORACLE 的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎表 driving table)將被最早處理,在FROM子句中包含多個表的狀況下,你必須選擇記錄條數最少的表做爲基礎表。若是有3個以上的錶鏈接查詢, 那就須要選擇交叉表(intersection table)做爲基礎表, 交叉表是指那個被其餘表所引用的表. (2) WHERE子句中的鏈接順序.: ORACLE採用自下而上的順序解析WHERE子句,根據這個原理,表之間的鏈接必須寫在其餘WHERE條件以前, 那些能夠過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾.
(3) SELECT子句中避免使用 ‘ * ‘: ORACLE在解析的過程當中, 會將'*' 依次轉換成全部的列名, 這個工做是經過查詢數據字典完成的, 這意味着將耗費更多的時間
(4) 減小訪問數據庫的次數: ORACLE在內部執行了許多工做: 解析SQL語句, 估算索引的利用率, 綁定變量 , 讀數據塊等;
(5) 在SQL*Plus , SQL*Forms和Pro*C中從新設置ARRAYSIZE參數, 能夠增長每次數據庫訪問的檢索數據量 ,建議值爲200
(6) 使用DECODE函數來減小處理時間: 使用DECODE函數能夠避免重複掃描相同記錄或重複鏈接相同的表.
(7) 整合簡單,無關聯的數據庫訪問: 若是你有幾個簡單的數據庫查詢語句,你能夠把它們整合到一個查詢中(即便它們之間沒有關係)
(8) 刪除重複記錄: 最高效的刪除重複記錄方法 ( 由於使用了ROWID)例子: DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO);
(9) 用TRUNCATE替代DELETE: 當刪除表中的記錄時,在一般狀況下, 回滾段(rollback segments ) 用來存放能夠被恢復的信息.
若是你沒有COMMIT事務,ORACLE會將數據恢復到刪除以前的狀態(準確地說是恢復到執行刪除命令以前的情況) 而當運用TRUNCATE時, 回滾段再也不存聽任何可被恢復的信息.當命令運行後,數據不能被恢復.
所以不多的資源被調用,執行時間也會很短. (譯者按: TRUNCATE只在刪除全表適用,TRUNCATE是DDL不是DML)
(10) 儘可能多使用COMMIT: 只要有可能,在程序中儘可能多使用COMMIT, 這樣程序的性能獲得提升,需求也會由於COMMIT所釋放的資源而減小: COMMIT所釋放的資源:
a. 回滾段上用於恢復數據的信息. b. 被程序語句得到的鎖 c. redo log buffer 中的空間 d. ORACLE爲管理上述3種資源中的內部花費
(11) 用Where子句替換HAVING子句: 避免使用HAVING子句, HAVING 只會在檢索出全部記錄以後纔對結果集進行過濾. 這個處理須要排序,總計等操做.
若是能經過WHERE子句限制記錄的數目,那就能減小這方面的開銷. (非oracle中)on、where、having這三個均可以加條件的子句中,on是最早執行,where次之,having最後,
由於on是先把不 符合條件的記錄過濾後才進行統計,它就能夠減小中間運算要處理的數據,按理說應該速度是最快的,where也應該比having快點的,
由於它過濾數據後 才進行sum,在兩個表聯接時才用on的,因此在一個表的時候,就剩下where跟having比較了。
在這單表查詢統計的狀況下,若是要過濾的條件沒有涉及到要計算字段,那它們的結果是同樣的,只是where可使用rushmore技術,而having就不能,在速度上後者要慢若是要涉及到計算的字 段,就表示在沒計算以前,
這個字段的值是不肯定的,根據上篇寫的工做流程,where的做用時間是在計算以前就完成的,而having就是在計算後才起做 用的,因此在這種狀況下,二者的結果會不一樣。
在多表聯接查詢時,on比where更早起做用。系統首先根據各個表之間的聯接條件,把多個表合成一個臨時表 後,再由where進行過濾,而後再計算,計算完後再由having進行過濾。
因而可知,要想過濾條件起到正確的做用,首先要明白這個條件應該在何時起做用,而後再決定放在那裏
(12) 減小對錶的查詢: 在含有子查詢的SQL語句中,要特別注意減小對錶的查詢.
例子: SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT TAB_NAME,DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
(13) 經過內部函數提升SQL效率.: 複雜的SQL每每犧牲了執行效率. 可以掌握上面的運用函數解決問題的方法在實際工做中是很是有意義的
(14) 使用表的別名(Alias): 當在SQL語句中鏈接多個表時, 請使用表的別名並把別名前綴於每一個Column上.這樣一來,就能夠減小解析的時間並減小那些由Column歧義引發的語法錯誤.
(15) 用EXISTS替代IN、用NOT EXISTS替代NOT IN: 在許多基於基礎表的查詢中,爲了知足一個條件,每每須要對另外一個表進行聯接.在這種狀況下, 使用EXISTS(或NOT EXISTS)一般將提升查詢的效率. 在子查詢中,NOT IN子句將執行一個內部的排序和合並. 不管在哪一種狀況下,NOT IN都是最低效的 (由於它對子查詢中的表執行了一個全表遍歷). 爲了不使用NOT IN ,咱們能夠把它改寫成外鏈接(Outer Joins)或NOT EXISTS.
例子: (高效)SELECT * FROM EMP (基礎表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')
(低效)SELECT * FROM EMP (基礎表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')
(16) 識別'低效執行'的SQL語句: 雖然目前各類關於SQL優化的圖形化工具層出不窮,可是寫出本身的SQL工具來解決問題始終是一個最好的方法: SELECT EXECUTIONS , DISK_READS, BUFFER_GETS, ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio, ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run, SQL_TEXT FROM V$SQLAREA WHERE EXECUTIONS>0 AND BUFFER_GETS > 0 AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8 ORDER BY 4 DESC;
(17) 用索引提升效率: 索引是表的一個概念部分,用來提升檢索數據的效率,ORACLE使用了一個複雜的自平衡B-tree結構. 一般,經過索引查詢數據比全表掃描要快.
當ORACLE找出執行查詢和Update語句的最佳路徑時, ORACLE優化器將使用索引. 一樣在聯結多個表時使用索引也能夠提升效率. 另外一個使用索引的好處是,它提供了主鍵(primary key)的惟一性驗證.。
那些LONG或LONG RAW數據類型, 你能夠索引幾乎全部的列. 一般, 在大型表中使用索引特別有效. 固然,你也會發現, 在掃描小表時,使用索引一樣能提升效率.
雖然使用索引能獲得查詢效率的提升,可是咱們也必須注意到它的代價. 索引須要空間來存儲,也須要按期維護, 每當有記錄在表中增減或索引列被修改時, 索引自己也會被修改.
這意味着每條記錄的INSERT , DELETE , UPDATE將爲此多付出4 , 5 次的磁盤I/O . 由於索引須要額外的存儲空間和處理,那些沒必要要的索引反而會使查詢反應時間變慢.。
按期的重構索引是有必要的.: ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
18) 用EXISTS