sql優化(oracle)

系統優化中很重要的方面是SQL語句的優化,對於海量數據,優質的SQL可以有效的提升系統的可用性。html

總結的有點羅嗦,列個簡單的目錄啦~sql

目錄

 

第一部分 知識準備                            第二部分 經常使用sql用法和注意事項                                第三部分  sql優化總結數據庫

    1.  sql執行過程            1. exists 和 in                                                       1. 優化通常原則服務器

    2.  sql 共享             2. union 和 union all                                               2. 具體注意事項
oracle

    3.  綁定變量                           3. with asapp

    4.  數據表訪問方式           4. order by函數

    5.  sql 執行順序           5. group by性能

    6.  索引使用             6. where 和 havingfetch

                        7. case when 和 decode優化

知識準備

 

1. sql執行過程

1)執行過程

  當一個oracle實例接收到一條sql後,執行過程以下:

  1) create  a cursor  建立遊標

  2) parse the statement 分析語句

  3)  describe results of a query 描述查詢的結果集

  4)define output of a query 定義查詢的輸出數據

  5)bind any variables 綁定變量

  6)parallelize the statement 並行執行語句

  7)run the statement 運行語句

  8)fetch rows of a query 取查詢結果

  9)close the cursor 關閉遊標

 

2.SQL 共享

  1. 爲不重複解析相同的SQL語句,oracle 將執行過的SQL語句存放在內存的共享池(shared buffer pool)中,能夠被全部數據庫用戶共享

  2. 當執行一個SQL語句時,若是它和以前執行過的語句徹底相同(注意同義詞和表是不一樣對象),oracle就能得到已經被解析的語句;

 

3.bind variables綁定變量

     1)解決重編譯問題

eg1:

insert into tab1(col1,col2) values (val1,val2); --普通方式

insert into tab1(col1,col2) values (:v1,:v2);--綁定變量,只需編譯一次

eg2:使用PreparedStatement

PreparedStatement ps = con.prepareStatement("insert into tab1 (col1, col2) values (?,?)");

  2)共享遊標

  好處:減小解析;提升內存使用率;動態內存調整

  假如輸入以下兩個sql:

select * from tab1 where id = :c;
select * from tab1 where id = :d;

  這兩句sql會被轉化爲:

select * from tab1 where id = :b;

 

4.訪問數據表方式

  1)全表掃描——順序訪問表中每條記錄

  oracle採用一個讀入多個數據塊的方式優化全表掃描

  2)經過rowid訪問表——rowid包含了表中記錄的物理位置信息, 基於rowid訪問方式能夠提升訪問表的效率

  oracle經過索引實現了數據和存放數據位置rowid之間的聯繫,一般索引提供了快速訪問rowid的方法

 

5. select sql執行順序

1)select子句

(8)SELECT (9)DISTINCT  (11)<Top Num> <select list>
(1)FROM <left_table>
(3)<join_type> JOIN <right_table>
(2)ON <join_condition>
(4)WHERE <where_condition>
(5)GROUP BY <group_by_list>
(6)WITH <CUBE | RollUP>
(7)HAVING <having_condition>
(10)ORDER BY <order_by_list>

2)執行順序說明

  1FROM [left table]——from前的表作笛卡爾集 ——虛擬表VT1

  2) ON <join condition>——篩選——VT2

  3) [join type] JOIN [right table]——鏈接——VT3 詳細見 oracle鏈接

  4) WHERE ——where篩選——VT4

  5GROUP BY ——按照GROUP BY子句中的列對VT4中行分組——VT5

  6)CUBE|ROLLUP——分組,eg:ROLLUP(A, B),首先會對(A、B)進行GROUP BY,而後對(A)進行GROUP BY,最後對全表GROUP BY

                CUBE(A,B), 首先對(A、B)GROUP BY, 而後(A)、(B) GROUP BY, 最後全表GROUP BY;

                ——VT6

  7) HAVING——HAVING篩選——VT7

  8SELECT——VT8

  9) DISTINCT——移除重複的行——VT9

  10ORDER BY——按照order by子句中的列將VT9中列表排序,生成遊標——VC10

  11) TOP ——從VC10的開始處選擇必定數量或者比例的行——VT11,返回結果

3)注意事項

  1. 只有ORDER BY 子句中可使用select列表中列的別名

     若是要在其餘地方使用須要使用以下方式:

SELECT * FROM (SELECT NAME, SALARY AS s FROM EMP ) vt WHERE vt.s<5000;

  2.  使用了ORDER BY子句的查詢不能用做表表達式(視圖、內聯表值函數、子查詢、派生表和共用表達式),以下的語句都會產生錯誤

create table tab1 as select * from student order by score;

select * from (select * from student order by score);

 

6.索引使用

  正確使用索引能夠有效提升系統性能,詳細見 oracle索引總結

 

 

經常使用sql用法和及注意事項

 

1.exits和in用法

1)說明:

  1. exists對外表作循環,每次循環對內表查詢;in將內表和外表作hash鏈接

  2. 使用exists oracle會先檢查主查詢; 使用in,首先執行子查詢,並將結果存儲在臨時表中

2)使用:

  表class和student表

         

  下面查詢student中classno在class中的數據

  1. 使用exists和not exists

select name, classno from student where exists (select * from class where student.classno= class.classno);

  結果:

  

select name, classno from student where not exists (select * from class where student.classno= class.classno);

  結果:

  

select name, classno  from student where classno  in (select classno from class);

  2. 使用in 和not in

select name, classno  from student where classno not in (select classno from class);

   

  結果:

  

3)比較

  1. 若是兩個表大小至關,in和exists差異不大

  2. 若是兩個表大小相差較大則子查詢表大的用exists,子查詢表小的用in

  3.儘可能不要使用not in

 

 

2.union和union all

1)說明:

  1. 使用場景:須要將兩個select語句結果總體顯示時,可使用union和union all

  2. union對兩個結果集取並集不包含重複結果同時進行默認規則的排序;而union all對兩個結果集去並集,包括重複行,不進行排序

  3.  union須要進行重複值掃描,效率低,若是沒有要刪除重複行,應該使用union all

  4. insersect和minus分別交集和差集,都不包括重複行,而且進行默認規則的排序

2)使用注意事項

  1.能夠將多個結果集合並

  2. 必須保證select集合的結果有相同個數的列,而且每一個列的類型是同樣的(列名不必定要相同,會默認將第一個結果的列名做爲結果集的列名)

3)例子:

表student

eg1:

select name, score from student where score> 60
union all 
select name, score from student where score <200;

結果:(有重複,沒有排序)

select name, score from student where score> 60
union
select name, score from student where score <200;

結果:(沒有重複,而且排序了)

 

 

3.with as

1)說明:

  1. with table as 能夠創建臨時表,一次分析,屢次使用

  2. 對於複雜查詢,使用with table as能夠抽取公共查詢部分,屢次查詢時能夠提升效率

  3. 加強了易讀性

2)語法:

with tabName asselect ...)

3)例子:

表student

eg1: 

select rownum, name, score from (select rownum, name,score from student where score >70 order by score); 

能夠更換成:

with table_s as (select rownum, name,score from student where score >70 order by score)
select name, score from table_s;

結果:

 4)多個with table as 一塊兒使用時用逗號隔開,而且只能使用一個with以下例子

eg1:

with vt1 as (select * from student where score >=60),
vt2 as (select * from class),
vt3 as (select * from teacher)
select vt1.name, vt1.score, vt2.classname, vt3.teachername  from vt1,vt2,vt3 where vt1.classno= vt2.classno and vt1.teacherid=vt3.teacherid;

 

eg2:

with vt as (select t.* 
from travelrecord t where t.starttime>=to_date('2014-02-01','yyyy-mm-dd') and t.endtime<=to_date('2014-04-30','yyyy-mm-dd')+1 and to_char(starttime,'hh24')>='08' and to_char(endtime,'hh24')<='11' and t.vehiclenum='100088110000'),
vt1 as (select  sum(vt4.traveltime) as stoptime from ((select * from vt where vt.state='0')vt4)),
vt2 as (select sum(vt.traveltime)as "ONLINETIME1",sum(vt.distance)as "DISTANCE1"from vt)
select vt1.stoptime,vt2.distance1, vt2.onlinetime1 from vt2, vt1;

 

4. order by

1)說明:

  1. order by 決定oracle如何將查詢結果排序

  2. 不指定asc或者desc時默認asc

2)使用:

  1. 單列升序(能夠去掉asc)

select * from student order by score asc;

  2. 多列升序

select * from student order by score,  deptno;

  3. 多列降序

select * from student order by score desc,  deptno  desc;

  4. 混合

select * from student order by score asc,  deptno  desc;

 

3)對NULL的處理

  1. oracle在order by 時認爲null是最大值,asc時排在最後,desc時排在最前

  eg:

select * from student order by score asc;

結果:

  2. 使用nulls first (無論asc或者desc,null記錄排在最前)或者nulls last 能夠控制null的位置,eg:

select * from student order by score asc nulls first;

結果以下:

 

4)將某行數據置頂(decode)

  eg1:

select * from student order by decode(score,100,1,2);

結果:

eg2: (某一行置頂,其餘的升序)

select * from student order by decode(score,100,1,2), score;

5)注意事項

  1. 任何在order by 語句的非索引項都將下降查詢速度

  2. 避免在order by 子句中使用表達式

 

5. group by

1)說明:

  1.用於對where執行結果進行分組

2)簡單例子:

eg1:

select sum(score), deptno from student group by deptno;

結果: 

  

 

eg2:

select deptno,sum(score) from student where deptno>1  group by deptno;

結果:

 

 

6.where和having

1)說明:

  1. where和having都是用來篩選數據,可是執行的順序不一樣 where --group by--having(即分組計算前計算where語句,分組計算後計算having'語句),詳情查看章節一sql執行順序

  2. having通常用來對分組後的數據進行篩選

  3. where中不能使用聚組函數如sum,count,max等

2)例子:

eg1: 對 5 中group by 的數據篩選

select deptno,sum(score) from student where deptno>1  group by deptno having sum(score)>100;

結果:

 

 

 

7. case when 和decode

1)說明:

  1. decode更簡潔

  2. decode只能作等值的條件區分,case when可使用區間的作判斷

2)語法:

decode(條件,值1,返回值1,值2,返回值2,...值n,返回值n,缺省值)

--等價於:

IF 條件=值1 THEN
    RETURN(翻譯值1)
ELSIF 條件=值2 THEN
    RETURN(翻譯值2)
    ......
ELSIF 條件=值n THEN
    RETURN(翻譯值n)
ELSE
    RETURN(缺省值)
END IF

 

CASE expr WHEN comparison_expr1 THEN return_expr1
         [WHEN comparison_expr2 THEN return_expr2
          WHEN comparison_exprn THEN return_exprn
          ELSE else_expr]
END

 

CASE
         WHEN comparison_expr1 THEN return_expr1
         [WHEN comparison_expr2 THEN return_expr2
          WHEN comparison_exprn THEN return_exprn
          ELSE else_expr]
END

 

3)例子:

eg1:

方式一:

 

select name, score,gender,
  case gender when '1' then ''
              when '2' then ''
              else '未說明'
  end gender_t
from student;

方式二:

select name, score,gender,
  case  when gender='1' then ''
        when  gender='2' then ''
              else '未說明'
  end gender_t
from student;

方式三:

select name,gender,decode(gender,'1','','2','','未說明')gender_t from student;

結果:

 

 

eg2:

select name,score, 
    case  when score >80 then'優秀' 
          when score>=60 and score <=80 then '良好' 
          when score<60 then '不及格'
   end  evalution
from student; 

結果:

設置默認值,將null置爲沒成績:

select name,score, 
    case  when score >80 then'優秀' 
          when score>=60 and score <=80 then '良好' 
          when score<60 then '不及格'
          else '沒成績'
   end  evalution
from student; 

結果:

 

4)注意:

  1.case有兩種形式,其中case 表達式 when then方式效率高於case when 表達式效率

  2.使用decode函數能夠避免重複掃描相同記錄或者重複鏈接相同的表,於是某些狀況能夠減小處理時間

 

 

SQL 優化總結

 

1. SQL優化通常性原則

  1)目標:減小服務器資源消耗(主要是磁盤IO)

  2)設計:

    1. 儘可能依賴oracle優化器

    2. 合適的索引(數據重複量大的列不要簡歷二叉樹索引,可使用位圖索引; 對應數據操做頻繁的表,索引須要按期重建,減小失效的索引和碎片)

  3)編碼:

    1.利用索引

    2. 合理利用臨時表

    3. 避免寫過於複雜的sql;

    4. 儘可能減少事務的粒度

2. 具體注意事項

  1)查詢時儘可能使用肯定的列名

  2)儘可能少使用嵌套的子查詢,這種查詢很消耗cpu資源

  3)多表查詢的時候,選擇最有效率的表名順序

   oracle解析器對錶的處理順序從右到左,因此記錄少的表放在右邊(最右邊的表爲基礎表,drivering table最早被處理), 若是3個以上的錶鏈接查詢,則要選擇交叉表做爲基礎表

  4)or比較多時分爲多個查詢,使用union all(儘可能用union all代替union)聯結(適應於索引列)

    詳細見上一章節union和union all

  5) 儘可能多用commit提交事務,能夠及時釋放資源、解鎖、釋放日誌

  6)訪問頻繁的表能夠放置在內存中

  7)避免複雜的多表關聯

  8)避免distinct,union(並集),minus(差集),intersect(交集),order by等耗費資源的操做,由於會執行耗費資源的排序功能

  9)使用exists替代distinct

eg:

 

select c.distinct c.classname, c.classid, classno from student s, class c where s.classno= c.classno;


--替換爲


select  classname, classid, classno from class c where exists (select * from student s where s.classno = c.classno);

 

   10)刪除全表時利用truncate代替delete

    delete刪除時,沒有commit前能夠回滾;truncate後不能回滾,執行時間較短

   11)使用表的別名,能夠減小解析時間

   12)exists和in的選擇問題,不一樣時候區分對待

     具體見上一章節exists和in

   13)合理使用索引,詳細見:oracle索引總結

相關文章
相關標籤/搜索