搞WEB的離不開數據庫,在一個層面上,對數據庫的熟練程度決定了很多的事情。
本文就大家都糾結的ORACLE多表查詢的性能問題給出一系列個優化方法,那這些都是項目中長期用到的,所以很熟,很熟,已經成為習慣了。
ORACLE有個高速緩沖的概念,這個高速緩沖呢就是存放執行過的SQL語句,那oracle在執行sql語句的時候要做很多工作,例如解析sql語句, 估算索引利用率,綁定變量,讀取數據塊等等這些操作。假設高速緩沖里已經存儲了執行過的sql語句,那就直接匹配執行了,少了步驟,自然就快了,但是經過 測試會發現高速緩沖只對簡單的表起作用,多表的情況小完全沒有效果啊,例如在查詢單表的時候那叫一個快,但是假設連接多個表,就龜速了。
最重要一點,ORACLE的高速緩沖是全字符匹配的,什么意思呢,看下面三個select
--No.1 select * from tableA; --No.2 select * From tableA; --No.3 select * from tableA;
這三個語句乍一看是一樣的,但是高速緩存是不認的,是全字符匹配的,索引在高速緩存里會存儲三條不同的語句,說道這里,又引出一個習慣,就是要保持良好的編程習慣,這個很重要啊。
ORACLE的多表優化我積累了一些,都是常用的,介紹下
第一點呢是From 子句后面的 表順序有講究
先說為啥,ORACLE在解析sql語句的時候對From子句后面的表名是從右往左解析的,是先掃描最右邊的表,然后在掃描左邊的表,然后用左邊的表匹配數據,匹配成功后就合並。
所以,在對多表查詢中,一定要把小表寫在最右邊,為什么自己想想就明白了。例如下面的兩個語句:
--No.1 tableA 100w條記錄 tableB 1w條記錄 執行速度 十秒級 select count(*) from tableA,tableB; --No.2 執行速度百秒級甚至更高 select count(*) from tableB,tableA;
這個估計很多人都知道,但是要確認非常有用。
還有一種是三張表的查詢,例如
select count(1) from tableA a,tableB b ,tableC c where a.id=b.id and a.id=c.id;
上面種 tableA 就稱為交叉表,根據oracle對From子句從右向左的掃描方式,應該把交叉表放在最末尾,然后才是最小表,所以上面的應該這樣寫
--tableA a 交叉表 --tabelB b 100w --tableC c 1w select count(1) from tableB b ,tableC c ,tableA a where a.id=b.id and a.id=c.id;
這種寫法對大數據量會非常有用,大家謹記,也是很常用的。
第二點呢是Where子句后面的條件過濾有講究,ORACLE對where子句后面的條件過濾是自下向上,從右向左掃描的,所以和From子句一樣一樣的,把過濾條件排個序,按過濾數據的大小,自然就是最少數據的那個條件寫在最下面,最右邊,依次類推,例如
--No.1 不可取 性能低下 select * from tableA a where a.id>500 and a.lx = '2b' and a.id < 'select count(1) from tableA where id=a.id ' --No.2 性能高,能過濾最多數據的條件寫在最后【謹記】 select * from tableA a where a.id < 'select count(1) from tableA where id=a.id ' and a.id>500 and a.lx = '2b'
第三點呢估計搞數據庫的都知道啦,就是在select的時候少用*,多敲敲鍵盤,寫上字段名吧,因為ORACLE的查詢器會把*轉換為表的全部列名,這個會浪費時間的,所以在大表中少用。
第四點呢就是要使用rowid 這個很好啊,可以用來分頁,刪除查詢重復記錄,很強大的,給兩個例子:
--查找重復記錄 select * from tableA a where a.rowid> ( select min(rowid) from tableB b where a.column=b.column )
--刪除相同記錄 delete from tableA a where a.rowid> ( select min(rowid) from tableB b where a.column=b.column ) --分頁 start=10 limit=10 --end 為 start + limit select * from ( select A.*,Rownum rn from (select * from tableA order by id) A where rownum <= 20 ) b wehre rn> 10 order by id desc
/*解釋一下, 1.查詢要排列的表 A
2.查詢A表的Rownum 找出小於end的數據 組成表B
3.查詢B表通過rownum找出大於start的數據 完成
簡單的說先根據end值過濾數據,然后在根據start過濾數據
so 簡單的
*/
第五點是存儲過程中需要注意的,多用commit了,既可以釋放資源,但是要謹慎啊。
第六點是減少對數據庫表的查詢,這個很重要,能減少就減少,因為在執行語句的時候oracle會做很多初始工作。
第七點不要用in啦,用exists來代替咯,例如:
--NO.1 IN的寫法 SELECT * FROM TABLEA A WHERE A.ID IN ( SELECT ID FORM TABLEB B WHERE B.ID>1) --NO.2 exists 寫法 SELECT * FROM TABLEA A WHERE EXISTS ( SELECT 1 FROM TABLEB B WHERE A.ID=B.ID AND B.ID>1)
相同的還有使用not exists 代替 not in ,方法雷同啊,就不介紹了。
那還有一些簡單的方法,例如索引這些就比較簡單了,就不介紹了,就寫在這里吧。
