SQl多表查詢優化 高效率SQL語句


這個功能大大地提高了SQL的執行性能並節省了內存的使用:我們發現,單表數據的統計比多表統計的速度完全是兩個概念.單表統計可能只要0.02秒,但是2張表聯合統計就可能要幾十表了.這是因為ORACLE只對簡單的表提供高速緩沖(cache buffering) ,這個功能並不適用於多表連接查詢..數據庫管理員必須在init.ora中為這個區域設置合適的參數,當這個內存區域越大,就可以保留更多的語句,當然被共享的可能性也就越大了.

當你向ORACLE提交一個SQL語句,ORACLE會首先在這塊內存中查找相同的語句. 
這里需要注明的是,ORACLE對兩者采取的是一種嚴格匹配,要達成共享,SQL語句必須 
完全相同(包括空格,換行等). 
共享的語句必須滿足三個條件: 
A.       字符級的比較: 
當前被執行的語句和共享池中的語句必須完全相同. 
例如: 
SELECT * FROM EMP; 
和下列每一個都不同 
SELECT * from EMP; 
Select * From Emp; 
SELECT       *      FROM EMP;

B.       兩個語句所指的對象必須完全相同:

   用戶對象名                       如何訪問 
Jack   sal_limit                      private synonym 
Work_city                           public synonym 
Plant_detail                         public synonym 
Jill sal_limit                         private synonym 
Work_city                           public synonym 
Plant_detail                        table owner 
考慮一下下列SQL語句能否在這兩個用戶之間共享. 
SQL 能否共享 原因 
select max(sal_cap) from sal_limit; 不能 每個用戶都有一個private synonym - sal_limit , 它們是不同的對象 
select count(*) from work_city where sdesc like 'NEW%'; 能 兩個用戶訪問相同的對象public synonym - work_city 
select a.sdesc,b.location from work_city a , plant_detail b where a.city_id = b.city_id 不能 用戶jack 通過private synonym訪問plant_detail 而jill 是表的所有者,對象不同.

C.       兩個SQL語句中必須使用相同的名字的綁定變量(bind variables) 
例如:第一組的兩個SQL語句是相同的(可以共享),而第二組中的兩個語句是不同的(即使在運行時,賦於不同的綁定變量相同的值) 
a. 
select pin , name from people where pin = :blk1.pin; 
select pin , name from people where pin = :blk1.pin; 
b. 
select pin , name from people where pin = :blk1.ot_ind; 
select pin , name from people where pin = :blk1.ov_ind; 
重點關注1:選擇最有效率的表名順序(只在基於規則的優化器中有效)重點關注
ORACLE
的解析器按照從右到左的順序處理FROM子句中的表名,因此FROM子句中寫在最后的表(基礎表 driving table)將被最先處理. 在FROM子句中包含多個表的情況下,你必須選擇記錄條數最少的表作為基礎表.當ORACLE處理多個表時, 會運用排序及合並的方式連接它們.首先,掃描第一個表(FROM子句中最后的那個表)並對記錄進行派序,然后掃描第二個表(FROM子句中最后第二個表),最后將所有從第二個表中檢索出的記錄與第一個表中合適記錄進行合並. 
例如:       表 TAB1 16,384 條記錄 
表 TAB2 1       條記錄 
選擇TAB2作為基礎表 (最好的方法) 
select count(*) from tab1,tab2    執行時間0.96秒 
選擇TAB2作為基礎表 (不佳的方法)
select count(*) from tab2,tab1    執行時間26.09秒 
如果有3個以上的表連接查詢, 那就需要選擇交叉表(intersection table)作為基礎表, 交叉表是指那個被其他表所引用的表. 
例如:    EMP表描述了LOCATION表和CATEGORY表的交集. 
SELECT * 
FROM LOCATION L , 
CATEGORY C, 
EMP E 
WHERE E.EMP_NO BETWEEN 1000 AND 2000 
AND E.CAT_NO = C.CAT_NO 
AND E.LOCN = L.LOCN 
將比下列SQL更有效率 
SELECT * 
FROM EMP E , 
LOCATION L , 
CATEGORY C 
WHERE   E.CAT_NO = C.CAT_NO 
AND E.LOCN = L.LOCN 
AND E.EMP_NO BETWEEN 1000 AND 2000 
重點關注2:WHERE子句中的連接順序 重點關注

From:QQ吻's Blog:http://qqhack8.blog.163.com

ORACLE采用自下而上的順序解析WHERE子句,根據這個原理,表之間的連接必須寫在其他WHERE條件之前, 那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾. 
例如: 
(低效,執行時間156.3秒) 
SELECT … 
FROM EMP E 
WHERE   SAL >; 50000 
AND     JOB = ‘MANAGER’ 
AND     25 < (SELECT COUNT(*) FROM EMP
WHERE MGR=E.EMPNO); 
(高效,執行時間10.6秒) 
SELECT … 
FROM EMP E 
WHERE 25 < (SELECT COUNT(*) FROM EMP 
WHERE MGR=E.EMPNO) 
AND     SAL >; 50000 
AND     JOB = ‘MANAGER’; 
重點關注3:SELECT子句中避免使用 ‘ * ‘ .重點關注

當你想在SELECT子句中列出所有的COLUMN時,使用動態SQL列引用 ‘*’ 是一個方便的方法.不幸的是,這是一個非常低效的方法. 實際上,ORACLE在解析的過程中, 會將’*’ 依次轉換成所有的列名, 這個工作是通過查詢數據字典完成的, 這意味着將耗費更多的時間. 
7.      減少訪問數據庫的次數 
當執行每條SQL語句時, ORACLE在內部執行了許多工作: 解析SQL語句, 估算索引的利用率, 綁定變量 , 讀數據塊等等. 由此可見, 減少訪問數據庫的次數 , 就能實際上減少ORACLE的工作量. 
例如, 
以下有三種方法可以檢索出雇員號等於0342或0291的職員. 
方法1 (最低效) 
     SELECT EMP_NAME , SALARY , GRADE 
FROM EMP 
WHERE EMP_NO = 342; 
SELECT EMP_NAME , SALARY , GRADE 
FROM EMP 
WHERE EMP_NO = 291; 
方法2 (次低效) 
        DECLARE 
CURSOR C1 (E_NO NUMBER) IS 
SELECT EMP_NAME,SALARY,GRADE
FROM EMP 
WHERE EMP_NO = E_NO; 
BEGIN 
OPEN C1(342); 
FETCH C1 INTO …,..,.. ; 
OPEN C1(291); 
FETCH C1 INTO …,..,.. ; 
CLOSE C1; 
END; 
方法3 (高效) 
     SELECT A.EMP_NAME , A.SALARY , A.GRADE, 
B.EMP_NAME , B.SALARY , B.GRADE 
FROM EMP A,EMP B 
WHERE A.EMP_NO = 342 
AND    B.EMP_NO = 291; 
注意: 
在SQL*Plus , SQL*Forms和Pro*C中重新設置ARRAYSIZE參數, 可以增加每次數據庫訪問的檢索數據量 ,建議值為200.

重點關注4:使用DECODE函數來減少處理時間.重點關注
使用DECODE函數可以避免重復掃描相同記錄或重復連接相同的表. 
例如: 
SELECT COUNT(*),SUM(SAL) 
FROM EMP 
WHERE DEPT_NO = 0020 
AND ENAME LIKE ‘SMITH%’; 
SELECT COUNT(*),SUM(SAL) 
FROM EMP 
WHERE DEPT_NO = 0030 
AND ENAME LIKE ‘SMITH%’; 
你可以用DECODE函數高效地得到相同結果 
SELECT COUNT(DECODE(DEPT_NO,0020,’X’,NULL)) D0020_COUNT, 
COUNT(DECODE(DEPT_NO,0030,’X’,NULL)) D0030_COUNT, 
SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL, 
SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL 
FROM EMP WHERE ENAME LIKE ‘SMITH%’; 
類似的,DECODE函數也可以運用於GROUP BY 和ORDER BY子句中.

重點關注5: 刪除重復記錄.重點關注

最高效的刪除重復記錄方法 ( 因為使用了ROWID) 
DELETE FROM EMP E 
WHERE E.ROWID >; (SELECT MIN(X.ROWID) 
FROM EMP X 
WHERE X.EMP_NO = E.EMP_NO);

重點關注6: 用TRUNCATE替代DELETE . 重點關注

當刪除表中的記錄時,在通常情況下, 回滾段(rollback segments ) 用來存放可以被恢復的信息. 如果你沒有COMMIT事務,ORACLE會將數據恢復到刪除之前的狀態(准確地說是恢復到執行刪除命令之前的狀況) 
而當運用TRUNCATE時, 回滾段不再存放任何可被恢復的信息.當命令運行后,數據不能被恢復.因此很少的資源被調用,執行時間也會很短. 
(譯者按: TRUNCATE只在刪除全表適用,TRUNCATE是DDL不是DML)

重點關注7: 盡量多使用COMMIT . 重點關注

只要有可能,在程序中盡量多使用COMMIT,這樣程序的性能得到提高,需求也會因為COMMIT所釋放的資源而減少: 
COMMIT所釋放的資源: 
a.        回滾段上用於恢復數據的信息. 
b.        被程序語句獲得的鎖 
c.        redo log buffer 中的空間 
d.       ORACLE為管理上述3種資源中的內部花費 
(譯者按: 在使用COMMIT時必須要注意到事務的完整性,現實中效率和事務完整性往往是魚和熊掌不可得兼) 
重點關注8:減少對表的查詢.重點關注

在含有子查詢的SQL語句中,要特別注意減少對表的查詢.

例如: 
低效 
SELECT TAB_NAME 
FROM TABLES 
WHERE TAB_NAME = ( SELECT TAB_NAME 
FROM TAB_COLUMNS 
WHERE VERSION = 604) 
AND DB_VER= ( SELECT DB_VER 
FROM TAB_COLUMNS 
WHERE VERSION = 604) 
高效 
SELECT TAB_NAME 
FROM TABLES 
WHERE   (TAB_NAME,DB_VER) 
= ( SELECT TAB_NAME,DB_VER) 
FROM TAB_COLUMNS 
WHERE VERSION = 604) 
Update 多個Column 例子: 
低效: 
UPDATE EMP 
SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES), 
SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES) 
WHERE EMP_DEPT = 0020; 
高效: 
UPDATE EMP 
SET (EMP_CAT, SAL_RANGE) 
= (SELECT MAX(CATEGORY) , MAX(SAL_RANGE) 
FROM EMP_CATEGORIES) 
WHERE EMP_DEPT = 0020; 
重點關注9:用EXISTS替代IN.重點關注

在許多基於基礎表的查詢中,為了滿足一個條件,往往需要對另一個表進行聯接.在這種情況下, 使用EXISTS(或NOT EXISTS)通常將提高查詢的效率. 
低效: 
SELECT * 
FROM EMP (基礎表) 
WHERE EMPNO >; 0 
AND DEPTNO IN (SELECT DEPTNO 
FROM DEPT 
WHERE LOC = ‘MELB’) 
高效: 
SELECT * 
FROM EMP (基礎表) 
WHERE EMPNO >; 0 
AND EXISTS (SELECT ‘X’ 
FROM DEPT 
WHERE DEPT.DEPTNO = EMP.DEPTNO 
AND LOC = ‘MELB’) 
(譯者按: 相對來說,用NOT EXISTS替換NOT IN 將更顯著地提高效率,下一節中將指出) 
重點關注10:用NOT EXISTS替代NOT IN .重點關注

在子查詢中,NOT IN子句將執行一個內部的排序和合並. 無論在哪種情況下,NOT IN都是最低效的 (因為它對子查詢中的表執行了一個全表遍歷).   為了避免使用NOT IN ,我們可以把它改寫成外連接(Outer Joins)或NOT EXISTS. 
例如: 
SELECT … 
FROM EMP 
WHERE DEPT_NO NOT IN (SELECT DEPT_NO 
FROM DEPT 
WHERE DEPT_CAT=’A’); 
為了提高效率.改寫為: 
(方法一: 高效) 
SELECT …. 
FROM EMP A,DEPT B 
WHERE A.DEPT_NO = B.DEPT(+) 
AND B.DEPT_NO IS NULL 
AND B.DEPT_CAT(+) = ‘A’ 
(方法二: 最高效) 
SELECT …. 
FROM EMP E 
WHERE NOT EXISTS (SELECT ‘X’ 
FROM DEPT D 
WHERE D.DEPT_NO = E.DEPT_NO 
AND DEPT_CAT = ‘A’); 
當然,最高效率的方法是有表關聯.直接兩表關系對聯的速度是最快的!
重點關注11:識別’低效執行’的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; 

Face your past without regret. Handle your present with confidence.Prepare for future without fear. keep the faith and drop the fear. 面對過去無怨無悔,把握現在充滿信心,備戰未來無所畏懼。保持信念,克服恐懼!一點一滴的積累,一點一滴的沉淀,學技術需要不斷的積淀!
 
http://www.cnblogs.com/200911/archive/2012/07/26/2610018.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM