為(wèi)了獲得(de)穩定的執行性能,SQL語句越簡單越到綠好(hǎo)。對複雜的SQL語句,要設法內冷對之進行簡化,本文給大(dà)家(jiā)介紹優化SQL語句提高數據庫性能。
現在數據越來越複雜和龐大(dà),很多時(shí)候影響程序運行性能不(bù媽姐)理想的原因中除了一部分是因為(wèi)應用程序的票機負載确實超過了服務器(qì)的實際處理能件暗力外,更多的是因為(wèi)系統存在大(dà)量的SQL語句需要優能音化。
一、問題的提出
在項目實際使用中,數據是一個長(cháng)期累計的過程,随着數據庫不從中數據的增加,系統的響應速度就成為(wèi)目前系統通遠需要解決的最主要的問題之一。系統優化中一個很重要的方面就是SQL呢但語句的優化。對于海量數據,劣質SQL語句和優質SQL黑微語句之間的速度差别可以達到成千上百倍,因北店此高質量的SQL語句,更能提高系統的可用性。
二、SQL語句編寫注意問題
下面就某些SQL語句的where子(zǐ)著廠句編寫中需要注意的問題作詳細介紹。在這些where子(zǐ)句中,即使某些列存影兒在索引,但是由于編寫了劣質的SQL,系統在運行鄉為該SQL語句時(shí)也不(bù)能使用該索引,而在窗同樣使用全表掃描,這就造成了響應速度的極大(dà)降低(dī)。
1. 操作符優化
(a) IN 操作符
在使用中盡量用EXISTS替代IN、用NOT EXISTS那是替代NOT IN 。
在許多基于基礎表的查詢中,為(wèi)了滿足一個條件,往往需新件要對另一個表進行聯接。在這種情況下, 使用EXISTS(或NOT EXISTS)通(tōng)常将提高查詢的效率。。在子(zǐ)查詢中,NOT IN子(zǐ)句将執行一個内部的排序和合并。 無論在哪種情況下,NOT IN都是最低(dī)效的 (因為(wèi)它對子(zǐ)查詢中的表執行了的生一個全表遍曆)。。為(wèi)了避免使用NOT IN ,我們(men)可以把它改寫成外連接(Outer Joins)或NOT EXISTS。
例子(zǐ):
(推薦)select* from dt_article where exist麗司s(select id from dt_article_categ理近ory wheredt_article_category。厭農id=dt_article。category_id andtitle='公司作劇新聞')
(不(bù)推薦)select* from dt_article where categor白兵y_id in (select id from dt_article_c訊章ategorywhere title='公司新聞')
(b) IS NULL 或IS NOT NULL操作(判斷字段是否為(wèi)空)
判斷字段是否為(wèi)空一般是不(bù)會不道(huì)應用索引的,因為(wèi)索引是不(bù)索引空值的。不(bù)能煙能用null作索引,任何包含null值的列都将不(b喝花ù)會(huì)被包含在索引中。即使索引有多列這樣的情況下,隻要這些列中有民討一列含有null,該列就會(huì)從索引中排除低舊。也就是說(shuō)如(rú)果某列存在空值,即使對該列建索引也不答小(bù)會(huì)提高性能。任何在where子(zǐ)句中使用is n光女ull或is not null的語句優化器(qì短謝)是不(bù)允許使用索引的。
例子(zǐ):
(推薦)select* from dt_article where title&鄉木gt;'';
(不(bù)推薦)select* from dt_article where 為樂title is null;
(c) > 及 < 操作符(大(dà)于或小于操作符)
(推薦)select * from dt_article見樂 where id>=101;
(不(bù)推薦)select * from dt_a道長rticle where id>100;愛學
兩者的區别在于, 前者将直接跳到第一個id等于101的記錄而後者将首先定位到id=100的記錄并且向前掃描到第一個id大(dà)于100的記錄。
(d)LIKE操作符
LIKE操作符可以應用通(tōng)配符查詢,裡面的通用你(tōng)配符組合可能達到幾乎是任意的查詢,但是如和不(rú)果用得(de)不(bù)好(hǎo)則會(huì)大小産生性能上的問題,如(rú)like '%福瑞希%'這種查詢不(bù)會(化體huì)引用索引,而like'福瑞希%'則會(huì)引用範你能圍索引。
一個實際例子(zǐ):用dt_article表中内容可來查詢, content like'%福瑞希%'這個條件會(huì)産生全表掃描,如(輛請rú)果改成contentlike '福瑞希%'則會(huì森林)利用content的索引進行範圍的查詢,性能肯定大(dà)大(d銀朋à)提高。
在很多情況下可能無法避免這種情況,但是一定也跳要心中有底,通(tōng)配符如(rú)此使用會(huì)又議降低(dī)查詢速度。然而當通(tōng)配符出現在字符串其他(tā)位置睡區時(shí),優化器(qì)就能利用索引。
(e) UNION操作符
當SQL語句需要UNION兩個查詢結果集合時(shí),這兩個結果頻喝集合會(huì)以UNION-ALL的方式被合并,暗空 然後在輸出最終結果前進行去重和排序。 假如(rú)用UNION AL車唱L替代UNION, 這樣排序就不(bù)是必要了。 效率就會(huì)因此得(醫就de)到提高。 需要注重的是,UNION ALL 将重複輸出作中兩個結果集合中相同記錄。 因此各位還是要從業務需求分析使用UNIONALL化錯的可行性。 UNION 将對結果集合去重排序,這個操作會(huì科們)使用到SORT_AREA_SIZE這塊内存。 對于這塊内存的購這優化也是相當重要的。
(f) NOT
我們(men)要避免在索引列上使用NOT, 熱藍NOT會(huì)産生在和在索引列上使用函數子自相同的影響。 當查詢列碰到”NOT,他(tā)就會(huì)停止你都使用索引轉而執行全表掃描。
(g) OR
通(tōng)常情況下在數, 用UNION替換WHERE子(zǐ)句中的OR将會(hu城煙ì)起到較好(hǎo)的效果。 對索引列匠草使用OR将造成全表掃描。 注重, 以上規則員筆隻針對多個索引列有效。 假如(rú)有column沒有被索引, 民用查詢效率可能會(huì)因為(wèi)你沒有選擇OR而降低(dī)。 門一在下面的例子(zǐ)中, title和category_id上都建計機有索引。
(推薦)select * from dt_articl身是e where title='清洗空氣' union all selec不銀t * from dt_article where category_id=9制訊2
(不(bù)推薦)select * fro離事m dt_article where title='清洗空氣'鐵自 or category_id=92 假如(rú)你堅持要用OR科知, 那就需要返回記錄最少的索引列寫在最前面。
&nbs說開p; 另外在一些情況下,也可以使用I明但N來替代OR, 這是一條簡單易記的唱化規則,但是實際的執行效果還須檢驗。
(推薦)select * from dt朋費_article where category_id in (89廠煙,92)
(不(bù)推薦)select * from道件 dt_article where category_id=92 or ca船紙tegory_id=89
(h) DISTINCT
&nbs輛民p; 當提交一個包含一對多表信息的查詢時(shí),避免在S明知ELECT子(zǐ)句中使用DISTINCT。 一般可以考慮用E購女XIST替換, EXISTS 使查詢更為(wèi)迅這湖速,因為(wèi)RDBMS核心模塊将在子(zǐ)女現查詢的條件一旦滿足後,馬上返回結果。
2. SQL書寫的影響
(a) WHERE後面的條件順序影響
WHERE子(zǐ)句後面的條件順序對大(dà)數據量表的得樹查詢會(huì)産生直接的影響。如(rú):
select * from dt_article where category_id=92 and is_hot=1
select * from dt_article where is_hot=1 and category_id=92
以上兩個SQL中category_id(電壓等級)及is_多腦hot(銷戶标志)兩個字段都沒進行索引,所以執行的時服刀(shí)候都是全表掃描,第一條SQL的is_hot=1在記錄集間行内比率為(wèi)99%,而category_id=92的比率隻為作開(wèi)1%,在進行第一條SQL的時(shí)候99%條門遠記錄都進行category_id及is_ho個城t的比較,而在進行第二條SQL的時(shí)候1%條記錄都進行cate又弟gory_id及is_hot的比較,以此可以得(de)出第二條SQL的C姐我PU占用率明顯比第一條低(dī)。
WHERE解析是采用自下而上的順序解析WHERE子(zǐ)句,妹頻根據這個原理,表之間的連接必須寫在其他(tā)WHERE看計條件之前, 那些可以過濾掉最大(dà)數量記錄的條件必須寫喝嗎在WHERE子(zǐ)句的末尾。
3. 更多方面SQL優化資(zī)料分享
(1) 選擇最有效率的表名順序(隻在基于規則歌校的優化器(qì)中有效):
ORACLE 的解析器(qì)按照從右到左的順序處理FROM子(zǐ)句中的厭玩表名,FROM子(zǐ)句中寫在最後的表(基礎表用木 driving table)将被最先處理,舊風在FROM子(zǐ)句中包含多個表的情況下,你必須選擇記錄條數最少新務的表作為(wèi)基礎表。如(rú)果有3個以上的表連接查詢, 那就需要選小票擇交叉表(intersectiontable)作為(wè也黑i)基礎表, 交叉表是指那個被其他(tā)表所引上車用的表.
(2) SELECT子(zǐ)句中避免使用 ‘ * ‘:
ORACLE在解析的過程中, 會(huì)将'*' 依次轉頻兒換成所有的列名, 這個工(gōng)作是通(tōng)過查詢數據字典完成的, 煙市這意味着将耗費更多的時(shí)間。
(3) 減少訪問數據庫的次數:
ORACLE在内部執行了許多工(gōng)作: 解析SQL語句, 估算索紙放引的利用率, 綁定變量 , 讀數據塊等。
(4) 整合簡單,無關聯的數據庫訪問:
如(rú)果你有幾個簡單的數據庫查詢語句,你可以把它們(men)整合到一個討水查詢中(即使它們(men)之間沒有關系) 。
(5) 用TRUNCATE替代DELETE:
當删除表中的記錄時(shí),在通(t計照ōng)常情況下, 回滾段(rollbacksegme農雨nts ) 用來存放可以被恢複的信息. 如(rú)果你沒有COMMIT訊說事務,ORACLE會(huì)将數據恢複到删除之前的狀态(準請路确地說(shuō)是恢複到執行删除命令之前的狀況) 而當運用TRUNCA黃北TE時(shí), 回滾段不(bù)再存放任何可被恢複湖歌的信息.當命令運行後,數據不(bù)能被恢複.因此路草很少的資(zī)源被調用,執行時(shí)間也會(來自huì)很短(duǎn). (譯者按: TRUNCA些制TE隻在删除全表适用,TRUNCATE是DDL不(bù)是DM業明L) 。
(6) 盡量多使用COMMIT:
隻要有可能,在程序中盡量多使用COMMIT, 這樣的到程序的性能得(de)到提高,需求也會(huì)因為(化些wèi)COMMIT所釋放的資(zī)源而減少,COMMIT所釋放的資(zī)很學源:
a. 回滾段上用于恢複數據的信息.
b. 被程序語句獲得(de)的鎖
c. redo log buffer 中的空間
(7) 通(tōng)過内部函數提高SQL效率:
複雜的SQL往往犧牲了執行效率. 能夠掌握上面銀金的運用函數解決問題的方法在實際工(gōng)作中是非常有意義的。
(8) 使用表的别名(Alias):
當在SQL語句中連接多個表時(shí), 請使用表的别名并志師把别名前綴于每個Column上.這樣一來,就可以減少解析的時(關雜shí)間并減少那些由Column歧義引起紙玩的語法錯(cuò)誤。
(9) 總是使用索引的第一個列:
如(rú)果索引是建立在多個列上, 隻飛技有在它的第一個列(leading col西船umn)被where子(zǐ)句引用時(shí),優化器(qì)才會(h件件uì)選擇使用該索引. 這也是一條簡單而重要的規則,當僅引用分輛索引的第二個列時(shí),優化器(qì)使用了全表掃不舞描而忽略了索引。
(10) 避免使用耗費資(zī)源的操作:
帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER廠錯 BY的SQL語句會(huì)啟動SQL引擎執行耗費資(zī)源的排序(SO現他RT)功能. DISTINCT需要一次排序操作, 而師廠其他(tā)的至少需要執行兩次排序. 通(tōng)議西常, 帶有UNION, MINUS , INTERSECT的SQL也花語句都可以用其他(tā)方式重寫. 如(rú)果你的數據庫的SOR不北T_AREA_SIZE調配得(de)好(hǎo)暗習, 使用UNION , MINUS, IN他說TERSECT也是可以考慮的, 畢竟它們(men)的可讀性很強。