Oracle SQL的最佳化

來源:互聯網
上載者:User

SQL的最佳化應該從5個方面進行調整:
1.去掉不必要的大型表的全表掃描
2.緩衝小型表的全表掃描
3.檢驗最佳化索引的使用
4.檢驗最佳化的串連技術
5.儘可能減少執行計畫的Cost

SQL語句:
是對資料庫(資料)進行操作的惟一途徑;
消耗了70%~90%的資料庫資源;獨立於程式設計邏輯,相對於對程式原始碼的最佳化,對SQL語句的最佳化在時間成本和風險上的代價都很低;
可以有不同的寫法;易學,難精通。

SQL最佳化:
固定的SQL書寫習慣,相同的查詢盡量保持相同,預存程序的效率較高。
應該編寫與其格式一致的語句,包括字母的大小寫、標點符號、換行的位置等都要一致

ORACLE最佳化器:
在任何可能的時候都會對錶達式進行評估,並且把特定的文法結構轉換成等價的結構,這麼做的原因是
要麼結果運算式能夠比源運算式具有更快的速度
要麼源運算式只是結果運算式的一個等價語義結構
不同的SQL結構有時具有同樣的操作(例如:= ANY (subquery) and IN (subquery)),ORACLE會把他們映射到一個單一的語義結構。
1 常量最佳化:
常量的計算是在語句被最佳化時一次性完成,而不是在每次執行時。下面是檢索月薪大於2000的的運算式:
sal > 24000/12
sal > 2000
sal*12 > 24000
如果SQL語句包括第一種情況,最佳化器會簡單地把它轉變成第二種。
最佳化器不會簡化跨越比較符的運算式,例如第三條語句,鑒於此,應盡量寫用常量跟欄位比較檢索的運算式,而不要將欄位置於運算式當中。否則沒有辦法最佳化,比如如果sal上有索引,第一和第二就可以使用,第三就難以使用。

2 操作符最佳化:
最佳化器把使用LIKE操作符和一個沒有萬用字元的運算式組成的檢索運算式轉換為一個“=”操作符運算式。
例如:最佳化器會把運算式ename LIKE 'SMITH'轉換為ename = 'SMITH'
最佳化器只能轉換涉及到可變長資料類型的運算式,前一個例子中,如果ENAME欄位的類型是CHAR(10), 那麼最佳化器將不做任何轉換。

 

一般來講LIKE比較難以最佳化。
其中:
~~IN 操作符最佳化:
    最佳化器把使用IN比較符的檢索運算式替換為等價的使用“=”和“OR”操作符的檢索運算式。
    例如,最佳化器會把運算式ename IN ('SMITH','KING','JONES')替換為
ename = 'SMITH' OR ename = 'KING' OR ename = 'JONES‘
oracle 會將 in 後面的東西產生一張記憶體中的暫存資料表。然後進行查詢。

 

如何編寫高效的SQL:
    當然要考慮sql常量的最佳化和操作符的最佳化啦,另外,還需要:
1 合理的索引設計:
例:表record有620000行,試看在不同的索引下,下面幾個SQL的運行情況:
語句A
SELECT count(*) FROM record
WHERE date >'19991201' and date <'19991214‘ and amount >2000
語句B
SELECT count(*) FROM record
WHERE date >'19990901' and place IN ('BJ','SH')
語句C
SELECT date,sum(amount) FROM record
group by date
1 在date上建有一個非叢集索引
A:(25秒)
B:(27秒)
C:(55秒)
分析:
date上有大量的重複值,在非叢集索引下,資料在物理上隨機存放在資料頁上,在範圍尋找時,必須執行一次表掃描才能找到這一範圍內的全部行。
2 在date上的一個叢集索引
A:(14秒)
B:(14秒)
C:(28秒)
分析:
在叢集索引下,資料在物理上按順序在資料頁上,重複值也排列在一起,因而在範圍尋找時,可以先找到這個範圍的起末點,且只在這個範圍內掃描資料頁,避免了大範圍掃描,提高了查詢速度。
3 在place,date,amount上的複合式索引
A:(26秒)
C:(27秒)
B:(<1秒)
分析:
這是一個不很合理的複合式索引,因為它的前置列是place,第一和第二條SQL沒有引用place,因此也沒有利用上索引;第三個SQL使用了place,且引用的所有列都包含在複合式索引中,形成了索引覆蓋,所以它的速度是非常快的。
4 在date,place,amount上的複合式索引
A: (<1秒)
B:(<1秒)
C:(11秒)
分析:
這是一個合理的複合式索引。它將date作為前置列,使每個SQL都可以利用索引,並且在第一和第三個SQL中形成了索引覆蓋,因而效能達到了最優。

總結1
預設情況下建立的索引是非叢集索引,但有時它並不是最佳的;合理的索引設計要建立在對各種查詢的分析和預測上。一般來說:
有大量重複值、且經常有範圍查詢(between, >,<,>=,<=)和order by、group by發生的列,考慮建立叢集索引;
經 常同時存取多列,且每列都含有重複值可考慮建立複合式索引;在條件運算式中經常用到的不同值較多的列上建立檢索,在不同值少的列上不要建立索引。比如在僱員 表的“性別”列上只有“男”與“女”兩個不同值,因此就無必要建立索引。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。
複合式索引要盡量使關鍵查詢形成索引覆蓋,其前置列一定是使用最頻繁的列。
2 避免使用不相容的資料類型:
例如float和INt、char和varchar、bINary和varbINary是不相容的。資料類型的不相容可能使最佳化器無法執行一些本來可以進行的最佳化操作。例如:
SELECT name FROM employee WHERE salary > 60000
在這條語句中,如salary欄位是money型的,則最佳化器很難對其進行最佳化,因為60000是個整型數。我們應當在編程時將整型轉化成為錢幣型,而不要等到運行時轉化。
3 IS NULL 與IS NOT NULL:
不 能用null作索引,任何包含null值的列都將不會被包含在索引中。即使索引有多列這樣的情況下,只要這些列中有一列含有null,該列就會從索引中排 除。也就是說如果某列存在空值,即使對該列建索引也不會提高效能。任何在WHERE子句中使用is null或is not null的語句最佳化器是不允 許使用索引的。
5 IN、OR子句常會使用工作表,使索引失效:
如果不產生大量重複值,可以考慮把子句拆開。拆開的子句中應該包含索引。
6 避免或簡化排序:
應當簡化或避免對大型表進行重複的排序。當能夠利用索引自動以適當的次序產生輸出時,最佳化器就避免了排序的步驟。以下是一些影響因素:
索引中不包括一個或幾個待排序的列;
group by或order by子句中列的次序與索引的次序不一樣;
排序的列來自不同的表。
為了避免不必要的排序,就要正確地增建索引,合理地合并資料庫表(儘管有時可能影響表的正常化,但相對於效率的提高是值得的)。如果排序不可避免,那麼應當試圖簡化它,如縮小排序的列的範圍等。

7 消除對大型表行資料的順序存取:
在 巢狀查詢中,對錶的順序存取對查詢效率可能產生致命的影響。比如採用順序存取策略,一個嵌套3層的查詢,如果每層都查詢1000行,那麼這個查詢就要查詢 10億行資料。避免這種情況的主要方法就是對串連的列進行索引。例如,兩個表:學生表(學號、姓名、年齡??)和選課表(學號、課程號、成績)。如果兩個 表要做串連,就要在“學號”這個串連欄位上建立索引。
還可以使用並集來避免順序存取。儘管在所有的檢查列上都有索引,但某些形式的WHERE子句強迫最佳化器使用順序存取。下面的查詢將強迫對orders表執行順序操作:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
雖然在customer_num和order_num上建有索引,但是在上面的語句中最佳化器還是使用順序存取路徑掃描整個表。因為這個語句要檢索的是分離的行的集合,所以應該改為如下語句:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT * FROM orders WHERE order_num=1008
這樣就能利用索引路徑處理查詢。

8 避免相互關聯的子查詢:
一個列的標籤同時在主查詢和WHERE子句中的查詢中出現,那麼很可能當主查詢中的列值改變之後,子查詢必須重新查詢一次。查詢嵌套層次越多,效率越低,因此應當盡量避免子查詢。如果子查詢不可避免,那麼要在子查詢中過濾掉儘可能多的行。
9 避免困難的正規運算式:
MATCHES和LIKE關鍵字支援萬用字元匹配,技術上叫正規運算式。但這種匹配特別耗費時間。

例如:SELECT * FROM customer WHERE zipcode LIKE “98_ _ _”
即使在zipcode欄位上建立了索引,在這種情況下也還是採用順序掃描的方式。如果把語句改為SELECT * FROM customer WHERE zipcode >“98000”,在執行查詢時就會利用索引來查詢,顯然會大大提高速度。
另外,還要避免非開始的子串。例如語句:SELECT * FROM customer WHERE zipcode[2,3] >“80”,在WHERE子句中採用了非開始子串,因而這個語句也不會使用索引。

 

10 不充份的串連條件:
例:表card有7896行,在card_no上有一個非叢集索引,表account有191122行,在account_no上有一個非叢集索引,試看在不同的表串連條件下,兩個SQL的執行情況:
SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no
(20秒)
將SQL改為:
SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no and a.account_no=b.account_no
(<1秒)
分析:
在第一個串連條件下,最佳查詢方案是將account作外層表,card作內層表,利用card上的索引,其I/O次數可由以下公式估算為:
外層表account上的22541頁+(外層表account的191122行*內層表card上對應外層表第一行所要尋找的3頁)=595907次I/O
在第二個串連條件下,最佳查詢方案是將card作外層表,account作內層表,利用account上的索引,其I/O次數可由以下公式估算為:
外層表card上的1944頁+(外層表card的7896行*內層表account上對應外層表每一行所要尋找的4頁)= 33528次I/O
可見,只有充份的串連條件,真正的最佳方案才會被執行。
多表操作在被實際執行前,查詢最佳化工具會根據串連條件,列出幾組可能的串連方案並從中找出系統開銷最小的最佳方案。串連條件要充份考慮帶有索引的表、行數多的表;內外表的選擇可由公式:外層表中的匹配行數*內層表中每一次尋找的次數確定,乘積最小為最佳方案。
不可最佳化的WHERE子句
例1
下列SQL條件陳述式中的列都建有恰當的索引,但執行速度卻非常慢:
SELECT * FROM record WHERE substrINg(card_no,1,4)='5378'
(13秒)
SELECT * FROM record WHERE amount/30<1000
(11秒)
SELECT * FROM record WHERE convert(char(10),date,112)='19991201'
(10秒)
分析:
WHERE子句中對列的任何操作結果都是在SQL運行時逐列計算得到的,因此它不得不進行表搜尋,而沒有使用該列上面的索引;如果這些結果在查詢編譯時間就能得到,那麼就可以被SQL最佳化器最佳化,使用索引,避免表搜尋,因此將SQL重寫成下面這樣:
SELECT * FROM record WHERE card_no like '5378%'
(<1秒)
SELECT * FROM record WHERE amount<1000*30
(<1秒)
SELECT * FROM record WHERE date= '1999/12/01'
(<1秒)
11 預存程序中,採用暫存資料表最佳化查詢:

1.從parven表中按vendor_num的次序讀資料:
SELECT part_num,vendor_num,price FROM parven ORDER BY vendor_num
INTO temp pv_by_vn
這個語句順序讀parven(50頁),寫一個暫存資料表(50頁),並排序。假定排序的開銷為200頁,總共是300頁。
2.把暫存資料表和vendor表串連,把結果輸出到一個暫存資料表,並按part_num排序:
SELECT pv_by_vn,* vendor.vendor_num FROM pv_by_vn,vendor
WHERE pv_by_vn.vendor_num=vendor.vendor_num
ORDER BY pv_by_vn.part_num
INTO TMP pvvn_by_pn
DROP TABLE pv_by_vn
這 個查詢讀取pv_by_vn(50頁),它通過索引存取vendor表1.5萬次,但由於按vendor_num次序排列,實際上只是通過索引順序地讀 vendor表(40+2=42頁),輸出的表每頁約95行,共160頁。寫並存取這些頁引發5*160=800次的讀寫,索引共讀寫892頁。
3.把輸出和part串連得到最後的結果:
SELECT pvvn_by_pn.*,part.part_desc FROM pvvn_by_pn,part
WHERE pvvn_by_pn.part_num=part.part_num
DROP TABLE pvvn_by_pn
這樣,查詢順序地讀pvvn_by_pn(160頁),通過索引讀part表1.5萬次,由於建有索引,所以實際上進行1772次磁碟讀寫,最佳化比例為30∶1。~~ANY和SOME 操作符最佳化:
    最佳化器將跟隨值列表的ANY和SOME檢索條件用等價的同等操作符和“OR”組成的運算式替換。
    例如,最佳化器將如下所示的第一條語句用第二條語句替換:
    sal > ANY (:first_sal, :second_sal)
    sal > :first_sal OR sal > :second_sal
    最佳化器將跟隨子查詢的ANY和SOME檢索條件轉換成由“EXISTS”和一個相應的子查詢組成的檢索運算式。
    例如,最佳化器將如下所示的第一條語句用第二條語句替換:
    x > ANY (SELECT sal FROM emp WHERE job = 'ANALYST')
    EXISTS (SELECT sal FROM emp WHERE job = 'ANALYST' AND x > sal)
~~ALL操作符最佳化:
    最佳化器將跟隨值列表的ALL操作符用等價的“=”和“AND”組成的運算式替換。例如:
    sal > ALL (:first_sal, :second_sal)運算式會被替換為:
    sal > :first_sal AND sal > :second_sal
    對於跟隨子查詢的ALL運算式,最佳化器用ANY和另外一個合適的比較符組成的運算式替換。例如
    x > ALL (SELECT sal FROM emp WHERE deptno = 10) 替換為:
    NOT (x = ANY (SELECT sal FROM emp WHERE deptno = 10))
    接下來最佳化器會把第二個運算式適用ANY運算式的轉換規則轉換為下面的運算式:
    NOT EXISTS (SELECT sal FROM emp WHERE deptno = 10 AND x <= sal)
~~BETWEEN 操作符最佳化:
    最佳化器總是用“>=”和“<=”比較符來等價的代替BETWEEN操作符。
    例如:最佳化器會把運算式sal BETWEEN 2000 AND 3000用sal >= 2000 AND sal <= 3000來代替。
~~NOT 操作符最佳化:
    最佳化器總是試圖簡化檢索條件以消除“NOT”邏輯操作符的影響,這將涉及到“NOT”操作符的消除以及代以相應的比較子。
    例如,最佳化器將下面的第一條語句用第二條語句代替:
    NOT deptno = (SELECT deptno FROM emp WHERE ename = 'TAYLOR')
    deptno <> (SELECT deptno FROM emp WHERE ename = 'TAYLOR')
    通常情況下一個含有NOT操作符的語句有很多不同的寫法,最佳化器的轉換原則是使“NOT”操作符後邊的子句儘可能的簡單,即使可能會使結果運算式包含了更多的“NOT”操作符。
    例如,最佳化器將如下所示的第一條語句用第二條語句代替:
    NOT (sal <1000 OR comm IS NULL)
    NOT sal <1000 AND comm IS NOT NULL sal >= 1000 AND comm IS NOT NULL
 

SQL 最佳化34條建議

(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替換DISTINCT:
當提交一個包含一對多表資訊(比如部門表和僱員表)的查詢時,避免在SELECT子句中使用DISTINCT. 一般可以考慮用EXIST替換, EXISTS 使查詢更為迅速,因為RDBMS核心模組將在子查詢的條件一旦滿足後,立刻返回結果. 例子:
      (低效):
SELECT  DISTINCT  DEPT_NO,DEPT_NAME  FROM  DEPT D , EMP E
WHERE  D.DEPT_NO = E.DEPT_NO
(高效):
SELECT  DEPT_NO,DEPT_NAME  FROM  DEPT D  WHERE  EXISTS ( SELECT ‘X'
FROM  EMP E  WHERE E.DEPT_NO = D.DEPT_NO);
(19) sql語句用大寫的:
因為oracle總是先解析sql語句,把小寫字母轉換成大寫的再執行
(20)在java代碼中盡量少用串連符“+”連接字串!
(21) 避免在索引列上使用NOT:
我們要避免在索引列上使用NOT, NOT會產生在和在索引列上使用函數相同的影響. 當ORACLE”遇到”NOT,他就會停止使用索引轉而執行全表掃描.
(22)避免在索引列上使用計算.
WHERE子句中,如果索引列是函數的一部分.最佳化器將不使用索引而使用全表掃描.
舉例:
低效:
SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;
高效:
SELECT … FROM DEPT WHERE SAL > 25000/12;
(23)用>=替代>
高效:
SELECT * FROM  EMP  WHERE  DEPTNO >=4
低效:
SELECT * FROM EMP WHERE DEPTNO >3
兩者的區別在於, 前者DBMS將直接跳到第一個DEPT等於4的記錄而後者將首先定位到DEPTNO=3的記錄並且向前掃描到第一個DEPT大於3的記錄.
(24)用UNION替換OR (適用於索引列)
通常情況下, 用UNION替換WHERE子句中的OR將會起到較好的效果. 對索引列使用OR將造成全表掃描. 注意, 以上規則只針對多個索引列有效. 如果有column沒有被索引, 查詢效率可能會因為你沒有選擇OR而降低. 在下面的例子中, LOC_ID 和REGION上都建有索引.
高效:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10
UNION
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE REGION = “MELBOURNE”
低效:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10 OR REGION = “MELBOURNE”
如果你堅持要用OR, 那就需要返回記錄最少的索引列寫在最前面.
(25)用IN來替換OR 
這是一條簡單易記的規則,但是實際的執行效果還須檢驗,在ORACLE8i下,兩者的執行路徑似乎是相同的. 
低效:
SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30
高效
SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30);
(26)避免在索引列上使用IS NULL和IS NOT NULL
避免在索引中使用任何可以為空白的列,ORACLE將無法使用該索引.對於單列索引,如果列包含空值,索引中將不存在此記錄. 對於複合索引,如果每個列都為空白,索引中同樣不存在此記錄. 如果至少有一個列不為空白,則記錄存在於索引中.舉例: 如果唯一性索引建立在表的A列和B列上, 並且表中存在一條記錄的A,B值為(123,null) , ORACLE將不接受下一條具有相同A,B值(123,null)的記錄(插入). 然而如果所有的索引列都為空白,ORACLE將認為整個索引值為空白而空不等於空. 因此你可以插入1000 條具有相同索引值的記錄,當然它們都是空! 因為空白值不存在於索引列中,所以WHERE子句中對索引列進行空值比較將使ORACLE停用該索引.
低效: (索引失效)
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;
高效: (索引有效)
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;
(27)總是使用索引的第一個列:
如果索引是建立在多個列上, 只有在它的第一個列(leading column)被where子句引用時,最佳化器才會選擇使用該索引. 這也是一條簡單而重要的規則,當僅引用索引的第二個列時,最佳化器使用了全表掃描而忽略了索引
28)用UNION-ALL 替換UNION ( 如果有可能的話):
當SQL 陳述式需要UNION兩個查詢結果集合時,這兩個結果集合會以UNION-ALL的方式被合并, 然後在輸出最終結果前進行排序. 如果用UNION ALL替代UNION, 這樣排序就不是必要了. 效率就會因此得到提高. 需要注意的是,UNION ALL 將重複輸出兩個結果集合中相同記錄. 因此各位還是要從業務需求分析使用UNION ALL的可行性. UNION 將對結果集合排序,這個操作會使用到SORT_AREA_SIZE這塊記憶體. 對於這塊記憶體的最佳化也是相當重要的. 下面的SQL可以用來查詢排序的消耗量
低效:
SELECT  ACCT_NUM, BALANCE_AMT
FROM  DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
UNION
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
高效:
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
UNION ALL
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
(29)用WHERE替代ORDER BY:
ORDER BY 子句只在兩種嚴格的條件下使用索引.
ORDER BY中所有的列必須包含在相同的索引中並保持在索引中的排列順序.
ORDER BY中所有的列必須定義為非空.
WHERE子句使用的索引和ORDER BY子句中所使用的索引不能並列.
例如:
表DEPT包含以下列:
DEPT_CODE PK NOT NULL
DEPT_DESC NOT NULL
DEPT_TYPE NULL
低效: (索引不被使用)
SELECT DEPT_CODE FROM  DEPT  ORDER BY  DEPT_TYPE
高效: (使用索引)
SELECT DEPT_CODE  FROM  DEPT  WHERE  DEPT_TYPE > 0
(30)避免改變索引列的類型:
當比較不同資料類型的資料時, ORACLE自動對列進行簡單的類型轉換.
假設 EMPNO是一個數實值型別的索引列.
SELECT …  FROM EMP  WHERE  EMPNO = ‘123'
實際上,經過ORACLE類型轉換, 語句轉化為:
SELECT …  FROM EMP  WHERE  EMPNO = TO_NUMBER(‘123')
幸運的是,類型轉換沒有發生在索引列上,索引的用途沒有被改變.
現在,假設EMP_TYPE是一個字元類型的索引列.
SELECT …  FROM EMP  WHERE EMP_TYPE = 123
這個語句被ORACLE轉換為:
SELECT …  FROM EMP  WHERETO_NUMBER(EMP_TYPE)=123
因為內部發生的類型轉換, 這個索引將不會被用到! 為了避免ORACLE對你的SQL進行隱式的類型轉換, 最好把類型轉換用顯式表現出來. 注意當字元和數值比較時, ORACLE會優先轉換數實值型別到字元類型
(31)需要當心的WHERE子句:
某些SELECT 語句中的WHERE子句不使用索引. 這裡有一些例子.
在下面的例子裡,
(1)‘!=' 將不使用索引. 記住, 索引只能告訴你什麼存在於表中, 而不能告訴你什麼不存在於表中.
(2) ‘||'是字元串連函數. 就象其他函數那樣, 停用了索引.
(3) ‘+'是數學函數. 就象其他數學函數那樣, 停用了索引.
(4)相同的索引列不能互相比較,這將會啟用全表掃描.

(32)
a. 如果檢索資料量超過30%的表中記錄數.使用索引將沒有顯著的效率提高.
b. 在特定情況下, 使用索引也許會比全表掃描慢, 但這是同一個數量級上的區別. 而通常情況下,使用索引比全表掃描要塊幾倍乃至幾千倍!
(33)避免使用耗費資源的操作:
帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啟動SQL引擎
執行耗費資源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要執行兩次排序. 通常, 帶有UNION, MINUS , INTERSECT的SQL語句都可以用其他方式重寫. 如果你的資料庫的SORT_AREA_SIZE調配得好, 使用UNION , MINUS, INTERSECT也是可以考慮的, 畢竟它們的可讀性很強
(34)最佳化GROUP BY:
提高GROUP BY 語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉.下面兩個查詢返回相同結果但第二個明顯就快了許多.
低效:
SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
高效:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
GROUP by JOB

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/tianlesoftware/archive/2009/10/15/4672023.aspx

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.