系統優化中很重要的方面是SQL語句的優化,對於海量數據,優質的SQL能夠有效的提高系統的可用性。
總結的有點羅嗦,列個簡單的目錄啦~
目錄
第一部分 知識准備 第二部分 常用sql用法和注意事項 第三部分 sql優化總結
1. sql執行過程 1. exists 和 in 1. 優化一般原則
2. sql 共享 2. union 和 union all 2. 具體注意事項
3. 綁定變量 3. with as
知識准備
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)執行順序說明
1)FROM [left table]——from前的表做笛卡爾集 ——虛擬表VT1 2) ON <join condition>——篩選——VT2 3) [join type] JOIN [right table]——連接——VT3 詳細見 oracle連接 4) WHERE ——where篩選——VT4 5)GROUP 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 8)SELECT——VT8 9) DISTINCT——移除重復的行——VT9 10)ORDER 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 as (select ...)
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索引總結