1. 程式人生 > >ORACLE中高效SQL的寫法

ORACLE中高效SQL的寫法

sum 結果 過程 avg 觸發 declare 結果集 num 也有

1、
書寫格式規範
1-1、大小文字及空格的統一
1-2、日期格式明確化
1-3、Bind變量的使用
1-4、表別名的使用
1-5、檢索時盡量避免檢索不需要的列
1-6、ORDER BY列明確指定
1-7、插入列明確指定
1-8、關聯表數目的限制
1-9、子查詢中不使用視圖
1-10、Hint的寫法
1-11、命名規範
2、
索引應用規範
2-1、WHERE子句中盡量不用OR
2-2、WHERE子句中盡量不用LIKE ‘%c%’, LIKE ’%c’
2-3、WHERE子句中盡量不用NOT
2-4、WHERE子句中盡量不用IS NULL, IS NOT NULL
2-5、在WHERE中慎用<>,!=
2-6、WHERE子句中IS NOT NULL使用的補充
2-7、盡量用EXISTS代替DISTINCT
2-8、ORDER BY使用上的註意點
2-9、Index列盡量不參加計算
2-10、復合Index中前面的列應在條件中指定
3、
其他規範
3-1、列比較時盡量保持類型一致
3-2、盡量避免使用子查詢
3-3、對子查詢層次限定
3-4、盡量用NOT EXISTS代替NOT IN + 子查詢
3-5、用表連接替換EXISTS
3-5、盡量不使用HAVING子句
3-6、為FROM子句中的表指定順序
3-7、WHERE子句中的連接順序
3-8、ROWID的使用
3-9、用ROWNUM判斷紀錄是否存在
3-10、翻頁SQL(下N件記錄取得)的寫法
3-11、MERGE的使用
3-12、多表插入的使用
3-13、DBlink使用的限制
3-14、盡量用DECODE代替集合函數
3-15、刪除重復記錄
3-16、減量減少對表的查詢
3-17、避免使用耗費資源的操作

1、 書寫格式規範
1-1、大小文字及空格的統一
數據庫對象名(表、索引等)和變量全半角小寫,其他的SQL文全半角大寫,避免不要的空格,如果使用空格只使用半角空格,既保持SQL文的可讀性,又盡可能的減少SQL的解析時間。
因為ORACLE的共享SQL機制,只有在共享池中找到完全匹配的(字符級比較,包括空格,字符大小寫)SQL才不需要再次解析(parsing)被執行SQL,所以應盡量保持統一風格的SQL。
錯:select E.EMP_NO FROM emp E;
對:SELECT e.emp_no FROM emp e;

1-2、日期格式明確化
如果不明確的指定時間的格式,默認使用NLS_DATE_FORMAT參數指定的時間格式,就有可能得不到你想要的結果,所以需要用TO_CHAR或TO_DATE函數明確轉換再使用。
錯:SELECT e.ename, e.hire_date FROM emp e WHERE e.hire_date > ‘20100726’
對:SELECT e.ename, TO_CHAR(e.hire_date,’YYYYMMDD’) FROM emp e WHERE e.hire_date > TO_DATE(‘20100726’,’YYYYMMDD’)

1-3、Bind變量的使用
對於相同條件不同條件值的SQL文,采用bind變量使其變成同一SQL文,從而減少ORACLE對它的解析時間。(Java語言中一般用?來作為bind變量)
錯:SELECT e.ename FROM emp e WHERE e.emp_no = 123;
SELECT e.ename FROM emp e WHERE e.emp_no = 567;
對:SELECT e.ename FROM emp e WHERE e.emp_no = ?;

1-4、表別名的使用
在進行多表關聯查詢時,檢索列前加上表別名,這樣既明確列的定義,又減少了SQL文的解析時間。
錯:SELECT ename, dname FROM emp, dept WHERE emp.deptno=dept.deptno AND sal >1000;
對:SELECT e.ename, d.dname FROM emp e, dept d WHERE e.deptno=d.deptno AND e.sal >1000;

1-5、檢索時盡量避免檢索不需要的列
在檢索紀錄時,特別是紀錄的列比較多時,盡量不要使用‘*’來代替所有列,這樣不僅增加處理時間(ORACLE在解析的過程中,會將‘*’ 依次轉換成所有的列名, 這個工作是通過查詢數據字典完成的, 這意味著將耗費更多的時間,同時也增加了I/O的量),而且當表結構變化時,原來的列順序有可能完全改變而導致不必要的bug或修改。
錯:SELECT * FROM emp WHERE eadrs = “上海市”;
對:SELECT e.empno e.ename FROM emp WHERE e.eadrs = “上海市”;

1-6、ORDER BY列明確指定
雖然使用ORDER BY字句時可以根據SELECT句中檢索列的列號來指定所需排序的列,但是從代碼的維護性考慮,應該盡量不使用列號而是明確指定排序列來排序。
錯:SELECT e.emp_no, e.ename, e.sal FROM emp e ORDER BY 2;
對:SELECT e.emp_no, e.ename, e.sal FROM emp e ORDER BY e.ename;

1-7、插入列明確指定
在對對象表進行全列插入時,SQL文法上允許省略對列的指定,但是考慮到因表結構(列的增刪改)有可能發生變化,而減少不要的修改,即使全列插入也需要明確指定插入列。
錯:INSERT INTO emp VALUES(100, ‘AAA’);
對:INSERT INTO emp(emp_no, ename) VALUES(100, ‘AAA’);

1-8、關聯表數目的限制
在進行多表關聯查詢時,隨著關聯表的增多ORACLE訪問路徑也會變得越來越復雜,從而導致ORACLE執行了不合理的實行計劃,所以要避免不必要的關聯。
錯:SELECT ~ FROM emp1 e1, emp2 e2, emp3 e3; //emp3為不需要關聯的表
對:SELECT ~ FROM emp1 e1, emp2 e2;

1-9、子查詢中不使用視圖
在子查詢中使用視圖會使ORACLE的訪問路徑變得很復雜,從而導致ORACLE執行了不合理的實行計劃,所以盡量不要在子查詢中使用視圖。
CREATE VIEW emp_v AS SELECT e.name, e.sal*12 sal FROM emp e WHERE e.deptno < 120;
錯:SELECT t1.dept_no, t2.ename FROM dept1 t1, (SELECT v.ename, FROM emp_v WHERE v.sal < 3000) t2 WHERE t1.dept_no = t2.dept_no;
對:SELECT t1.dept_no, t2.ename FROM dept1 t1, (SELECT v.ename, FROM emp e WHERE e.salary < (3000/12) AND e.dept_no < 120) t2 WHERE t1.dept_no = t2.dept_no;

1-10、Hint的寫法
一般的SQL中不考慮使用hint句,除非在性能試驗等試驗結果性能很差的時候才考慮使用。(使用hint之後會改變原有的實行計劃)
錯:SELECT /*+FIRST_ROWS*/ e.ename FROM emp e WHERE e.emp_no = ‘SCOTT‘;
對:SELECT e.ename FROM emp e WHERE e.emp_no = ‘SCOTT‘;

1-11、命名規範
表別名,列別名命名時,盡量按照原表名和列名的省略縮寫形式,保持SQL的可讀性。
錯:[表名]employee -->[別名]a
對:[表名]employee -->[別名]emp 2、 索引應用規範
2-1、WHERE子句中盡量不用OR
使用了OR之後,有的情況下(涉及到索引的掃描方式)會使對索引的檢索變成無效,從而降低檢索的性能,這這種情況下,可以考慮是否可以用UNION或IN來代替。
RBO(基於規則)下,OR就像UNION ALL文的展開一樣,在有外部結合、CONNECT BY句的情況下就不能被執行。CBO(基於成本)下,OR根據各結合順序的基礎結合成本,在再估算階段,根據成本對INLIST和OR文能不能再擴展進行判斷。
錯1:SELECT name FROM emp WHERE deptno = 99 OR deptno = 999;
對1:SELECT name FROM emp WHERE deptno = 99
UNION ALL
SELECT name FROM emp WHERE deptno = 999;
錯2:~ FROM emp WHERE deptno = 10 OR deptno = 20 OR teptno = 30;
對2:~ FROM emp WHERE deptno IN(10,20,30);

2-2、WHERE子句中盡量不用LIKE ‘%c%’, LIKE ’%c’
在有索引的列上使用LIKE ‘%c%’, LIKE ’%c’之後,索引就會失效,因而性能也會下降。
錯:~ WHERE name LIKE ‘%c%’;
錯:~ WHERE name LIKE ’%c’;
對:~ WHERE name LIKE ’ c%’;

2-3、WHERE子句中盡量不用NOT
在有索引的列上使用NOT、!=、 <>之後,索引檢索就會失效,因而性能也會下降。
例1:job列大部分值為NULL或’SALESMAN’,並且job列上建有B*Tree索引。
錯:~ WHERE job != ‘SALESMAN’
對:~ WHERE job > ‘SALESMAN’ OR job < ‘SALESMAN’
例2:dname為建有B*Tree索引的文字列,並且基數比較少(值比較少,譬如性別列,只有男和女兩個值),且不經常被更新。
錯:~ WHERE dname IS NOT NULL;
對:DROP INDEX btree_dname_idx;
CREATE BITMAP INDEX bitmap_dname_idx ON emp(dname);
SELECT ~ WHERE dname IS NOT NULL;

2-4、WHERE子句中盡量不用IS NULL, IS NOT NULL
在用了IS NULL、IS NOT NULL 之後,對應檢索列的B*Tree索引就會失去索引功能,從而性能會大幅下降。
在使用IS NULL的時候也應該考慮一下是否真的需要NULL值。IS NOT NULL在CBO下統計情報取得的情況下,索引的檢索也有可能被使用(參照IS NOT NULL使用的補充)。即使使用IS NULL、IS NOT NULL,BITMAP索引還是照樣能被使用。
錯:Hiredate列為日期型,在hiredate列上建有B*Tree索引,並且’9999-12-31’是系統中不可能出現的日期,執行下面的SQL
SELECT ~ WHERE hiredate IS NULL;
對:CREATE INDEX function_hiredate_idx ON emp(NVL(hiredate, TO_DATE(‘9999-12-31’)));
SELECT ~ WHERE NVL(hiredate, TO_DATE(‘9999-12-31’)) = TO_DATE(‘9999-12-31’, ‘YYYYMMDD’);

2-5、在WHERE中慎用<>,!=
記住索引只能告訴你什麽存在於表中,而不能告訴你什麽不存在於表中。
在下面的例子中‘!=‘ 將不使用索引。
不使用索引:SELECT account_name FROM transaction WHERE amout != 0;
使用索引:SELECT account_name FROM transaction WHERE amout > 0;

2-6、WHERE子句中IS NOT NULL使用的補充
在NULL值比率較低的列上使用IS NOT NULL,會使對B*Tree索引變得無效,從而降低檢索性能,相反,在NULL值比率相當高的列上使用IS NOT NULL,B*Tree索引會使用全表掃描(full scan)從而地高性能。(這是在CBO中統計情報已經取得的條件下)
例:Comm為建有B*Tree索引的數值型列,且NULL值比率相當高(CBO並且統計情報已經取得)
錯:~ WHERE comm >= 0 OR comm < 0;
對:~ WHERE comm IS NOT NULL;

2-7、盡量用EXISTS代替DISTINCT
DEPT表和EMP表是一對多的關系,根據EMP表從DEPT表中取出紀錄,這種情況要避免使用DISTINCT,因為用DISTINCT後滿足條件的所有紀錄將都會被檢索、排序、重復行刪除,從而影響了性能。取而代之的可以用EXSITS子查詢,EXSITS子查詢當有一件符合條件的紀錄存在時馬上就返回不處理剩下的紀錄,因而速度很快。
錯:SELECT DISTINCT d.dept_code, d.dept_name FROM dept d, emp e WHERE e.dept_code = d.dept_code;
對:SELECT d.dept_code, d.dept_name FROM dept d WHERE EXSITS(SELECT ‘X’ FROM emp e WHERE e.dept_code = d.dept_code);

2-8、ORDER BY使用上的註意點
如果想要ORDER BY句的排序用到索引,就必須滿足下面的兩個條件
1)、ORDER BY句中的列順序要完全包含在同一順序的一個復合索引中
2)、ORDER BY句中的列要全部在表中定義為NOT NULL列

2-9、Index列盡量不參加計算
檢索條件中索引列被參與計算,或被用作函數的參數,那麽就會失去該列的索引功能,從而導致性能急劇下降。
可以通過建函數索引的方法,計算結果或函數值事前計算好作為所引來用。
錯:~ WHERE sal*1.1 > 950;
對:~ WHERE sal > 950/1.1;
錯:~ WHERE name || type =’XXXY’ ;
對:~ WHERE name = ‘XXX’ AND type = ’Y’ ;
錯:~ WHERE TO_CHAR(hiredate, ‘YYYYMMDD’) = ‘20100722’;
對:~ WHERE hiredate = TO_DATE( ‘20100722’ , ‘YYYYMMDD’) ;
錯:~ WHERE SUBSTR(name, 1, 7) = ‘CAPTIAL’;
對:~ WHERE name LIKE ‘CAPTIAL%’;
錯:~ WHERE TRUNC(trans_date) = TRUNC(SYSDATE);
對:~ WHERE trans_date BETWEEN TRUNC(SYSDATE) AND TURNC(SYSDATE) + .99999
註:在日期上加上超過5位小數的數是,日期就自動變為下一日的日期。
TO_DATE(‘2010-7-22’)+.99999 &#61664; ‘2010-7-22 23:59:59’
TO_DATE(‘2010-7-22’)+.999999 &#61664; ‘2010-7-23 00:00:00’

2-10、復合Index中前面的列應在條件中指定
在使用復合索引時,復合索引中前面的列要在條件中指定。如果前面列在條件中沒指定的情況下,雖然通過索引的SKIP SCAN機能有可能會使用索引,但是一定要確認索引是否真的被使用。
(depno, job)列順序的復合索引
對:~ WHERE depno = 20 AN job = ‘MANAGER’;
對:~ WHERE job = ‘MANAGER’ AND depno = 20;
對:~ WHERE depno = 20; //索引前方部分使用
對:~ WHERE job = ‘MANAGER’; //這種情況下,一定要確認索引是否被使用

3、 其他規範
3-1、列比較時盡量保持類型一致
數值型列和文字列比較的時候,文字列會自動的轉換成數值型,但是這樣的處理會使基於索引的檢索無效,從而降低性能。
例:
對:~ WHERE emp_no = 123 (emp_no建有索引的數值列)
可能不對:~ WHERE emp_no = ‘123’ (emp_no建有索引的數值列)
註)ORACLE在實際執行中會做~ WHERE emp_no = TO_NUMBER(‘123’)的轉換而可能導致索引不被使用
對:~ WHERE emp_type = ‘123’ (emp_tepe建有索引的文字列)
錯:~ WHERE emp_type = ‘123’ (emp_tepe建有索引的文字列)
註)ORACLE在實際執行中會做~ WHERE TO_NUMBER(emp_no) = ‘123’的轉換而導致索引使用無效

3-2、盡量避免使用子查詢
不使用子查詢的SQL訪問路徑上自由度增大,處理性能上也提升不少,所以原則上不使用子查詢而通過考慮是否能用通常的結合處理來代替。
錯:SELECT e.ename FROM emp e, (SELECT d1.deptno FROM dept d1 WHERE d1.deptno = ‘A’) d WHERE e.deptno = d.deptno;
對:SELECT e.ename FROM emp e, dept d WHERE e.deptno = d.deptno AND d.deptno = ‘A’;

3-3、對子查詢層次限定
在子查詢中再嵌套子查詢會使ORACLE的實行計劃變得更加復雜,隨著子查詢的重度越高執行不確切的實行計劃的可能性越高。因此,盡量使避免使用嵌套子查詢。

3-4、盡量用NOT EXISTS代替NOT IN + 子查詢
在子查詢中使用了NOT IN演算後,會發生內部排序、合並處理,為了提高性能,可以用NOT EXISTS來代替NOT IN + 子查詢。
錯:SELECT e.ename FROM emp e WHERE e.deptno NOT IN(SELECT d.deptno FROM dept d WHERE d.deptno = e.deptno AND d.dept_cat = ’A’) ;
對:SELECT e.ename FROM emp e WHERE NOT EXISTS(SELECT ‘X’ FROM dept d WHERE d.deptno = e.deptno AND d.dept_cat = ’A’);


3-5、用EXISTS替換DISTINCT
當提交一個包含一對多表信息(比如部門表和雇員表)的查詢時,避免在SELECT子句中使用DISTINCT. 一般可以考慮用EXIST替換。
註)EXISTS 使查詢更為迅速,因為RDBMS核心模塊將在子查詢的條件一旦滿足後,立刻返回結果。
低效:SELECT DISTINCT dept_no,dept_name FROM dept d,emp e
WHERE d.dept_no = e.dept_no;
高效:SELECT dept_no,dept_name FROM dept d
WHERE EXISTS ( SELECT ‘X’ FROM emp e
WHERE e.dept_no = d.dept_no);

3-6、用表連接替換EXISTS
通常來說 , 采用表連接的方式比EXISTS更有效率
註)在RBO的情況下,前者的執行路徑包括FILTER,後者使用NESTED LOOP
低效:SELECT ename
FROM emp e
WHERE EXISTS (SELECT ‘X’ FROM dept
WHERE dept_no = e.dept_no AND DEPT_CAT = ‘A’);
 高效:SELECT ename FROM dept d, emp e
WHERE e.dept_no = d.dept_no AND dept_cat = ‘A’ ;

3-5、盡量不使用HAVING子句
HAVING子句是把全部的紀錄選中之後,對選擇的紀錄再按條件進行過濾,相比之下用WHERE子句來對選擇的紀錄進行過濾性能更勝一籌,所以在用HAVING子句的時候考慮一下是不是能夠移到WHERE子句中實現。
錯:
SELECT e.deptno AVG(e.sal) FROM emp e GROUP BY e.deptno HAVING e.deptno > 10;
對:
SELECT e.deptno AVG(e.sal) FROM emp e WHERE e.deptno > 10 GROUP BY e.deptno;

3-6、基礎表的選擇
基礎表(Driving Table,也被叫做驅動表)是指被最先訪問的表(通常以全表掃描的方式被訪問)。根據優化器的不同,SQL語句中基礎表的選擇是不一樣的。
CBO模式下,優化器會檢查SQL語句中的每個表的物理大小,索引的狀態,然後選用花費最低的執行路徑。
RBO模式下,表結合的條件全部相同的情況下,FROM子句中從右到左結合,所FROM子句最右邊的表為基表。因為基表紀錄數越少性能越好,所以FROM子句中按紀錄數從大到小順序指定結合表。
註)但是條件指定後件數變化的情況下,按照變化後的件數從大到小來指定。
CBO例:SELECT a.name,b.manager FROM worker a,lodging b
WHERE a.loding = b.loding;
由於lodging表的loding列上有一個索引, 而且worker表中沒有相比較的索引,worker表將被作為查詢中的基礎表。
RBO例:
錯:~ FROM a,b,c; (紀錄數 c < b < a)
對:~ FROM c,b,a; (紀錄數 c < b < a)

3-7、WHERE子句中的連接順序
ORACLE采用自下而上的順序解析WHERE子句,根據這個原理,表之間的連接必須寫在其他WHERE條件之前, 那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾。
錯:SELECT …
FROM emp e
WHERE sal > 50000
AND job = ‘MANAGER’
AND 25 < (SELECT COUNT(*) FROM emp WHERE mgr=e.empno);
對:SELECT …
FROM emp e
WHERE 25 < (SELECT COUNT(*) FROM emp WHERE mgr=e.empno)
AND sal > 50000
AND job = ‘MANAGER’;

3-8、ROWID的使用
在通過SELECT結果作為條件來DELTE或者UPDATE的場合,使用ROWID作為條件能減輕負荷。
註)ROWID是數據庫中實際的ORACLE數據塊對應的物理紀錄番號,通過ROWID檢索是最快的檢索方式。
錯:SELECT e.ename INTO:emp_ename FROM emp e WHERE e.emp_no = 123 FOR UPDATE OF e.ename;
UPDATE emp e SET e.ename = ‘XXX’WHERE e.emp_no = 123;
對:SELECT e.ename e.rowid INTO:emp_ename,:emp_rowid FROM emp e WHERE e.emp_no = 123 FOR UPDATE OF e.ename;
UPDATE emp e SET e.ename = ‘XXX’WHERE ROWID = e.emp_rowid;

3-9、用ROWNUM判斷紀錄是否存在
記錄存在與否用ROWNUM<=1來判斷效率更高,因為當指定ROWNUM<=1的時候,只要一條記錄被找到SQL的執行就結束了,所以能恒號的提升執行的速度。
錯:SELECT ‘X’FROM dual WHERE EXISTS(SELECT ‘X’FROM emp WHERE sal > 100);
對:SELECT ‘X’FROM dual WHERE sal > 100 AND ROWNUM<=1;

3-10、翻頁SQL(下N件記錄取得)的寫法
在取下N件紀錄的時候,在FORM子句中用ROWNUM限定結果集,然後再在WHERE句中的指定所取件數,這樣會大大減少response的性能。
錯:SELECT row_num,empno,name FROM (SELECT ROWNUM row_num, empno,name WHERE emp) WHERE row_num >= 6 AND row_num <= 10;
對:SELECT row_num,empno,name FROM (SELECT ROWNUM row_num, empno,name WHERE emp ROWNUM <= 10) WHERE row_num >= 6;

3-11、MERGE的使用
MERGE是ORACLE 9i之後才用的新機能,對應條件的紀錄存在的時候執行UPDATE,不存在的時候執行INSERT處理。使用MERGE可以簡化編程工作量及復雜度。
錯:DECLARE
CURSOR dept_cur IS SELECT * FROM dept FOP UPDATE;
wk dept_cur%ROWTYPE;
BEGIN
FOR wk IN dept_cur LOOP
UPDATE dept2 SET dname = wk.dname WHERE deptno = wk.deptno;
IF SQL%NOTFOUND THEN
INSERT INTO dept2 VALUES(wk.deptno,dname,wk.loc);
END IF
END LOOP;
END;
對:MERGE INTO dept2 d2
USING dept d
ON (d2.deptno = d.deptno)
WHERE MATCHED THEN
UPDATE SET d2.dname = d.dname
WHEN NO MATCHED THEN
INSERT(d2.deptno,d2.dname,d2.loc) VALUES(d.deptno,d.name,d.loc);

3-12、多表插入的使用
同一數據源插入復數個表,這在ORACLE 9i之前需要執行復數個單獨的INSERT ~
SELECT語句,但是在ORACLE 9i以後我們可以用多表插入功能,只要執行一次INSERT文就可以向復數的表中插入數據。
過去:
INSERT INTO sales_month_a(month,sales) SELECT month,SUM(sales) FROM sales WHERE prod_id = ‘PROD_A’ GROUP BY month;
INSERT INTO sales_month_b(month,sales) SELECT month,SUM(sales) FROM sales WHERE prod_id = ‘PROD_B’ GROUP BY month;
現在:
INSERT INTO FIRST
WHEN prod_id = ‘PROD_A’INTO sales_month_a(month,sales) VALUES(month,slaes)
WHEN prod_id = ‘PROD_B’INTO sales_month_b(month,sales) VALUES(month,slaes)
SELECT month,SUM(sales) FROM sales GROUP BY month;

3-13、DBlink使用的限制
分散DB環境中,通過DBlink使用不同DB中的表進行結合處理,會因為傳送數據量的過多而造成性能嚴重低下。所以盡量用其他方法來替換不同DB中的表結合處理。

3-14、盡量用DECODE代替集合函數
為了減輕集合函數中的總數或合計的計算的負荷,盡量使用DECODE來減少處理時間。使用DECODE函數可以避免重復掃描相同記錄或重復連接相同的表。
錯:SELECT COUNT(*),SUM(sal) FROM emp WHERE deptno=10;
SELECT COUNT(*),SUM(sal) FROM emp WHERE deptno=20;
對:SELECT COUNT(DECODE(deptno,10,’D10’)) “COUNT-10”,
COUNT(DECODE(deptno,20,’D20’)) “COUNT-20”,
SUM(DECODE(deptno,10,sal,NULL) “SUM-10”,
SUM(DECODE(deptno,20,sal,NULL) “SUM-20”
FROM emp;

3-15、刪除重復記錄
最高效的刪除重復記錄方法 ( 因為使用了ROWID)
DELETE FROM emp e
WHERE E.ROWID > (SELECT MIN(x.ROWID)
FROM emp x
WHERE x.emp_no = e.emp_no);

3-16、減量減少對表的查詢
在含有子查詢的SQL語句中,要特別註意減少對表的查詢。
錯:SELECT tab_name
FROM tables
WHERE tab_name = ( SELECT tab_name
FROM tab_colums
WHERE version = 604)
AND db_ver= ( SELECT db_ver
FROM tab_colums
WHERE version = 604);
對:SELECT tab_name
FROM tables
WHERE (tab_name, db_ver) = ( SELECT tab_name, db_ver)
FROM tab_colums
WHERE version = 604);

3-17、避免使用耗費資源的操作
帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啟動SQL引擎,執行耗費資源的排序(SORT)功能。DISTINCT需要一次排序操作,而其他的至少需要執行兩次排序。
例如一個UNION查詢,其中每個查詢都帶有GROUP BY子句,GROUP BY會觸發嵌入排序(NESTED SORT); 這樣,每個查詢需要執行一次排序,然後在執行UNION時,又一個唯一排序(SORT UNIQUE)操作被執行而且它只能在前面的嵌入排序結束後才能開始執行。 嵌入的排序的深度會大大影響查詢的效率。通常, 帶有UNION, MINUS , INTERSECT的SQL語句都可以用其他方式重寫。

ORACLE中高效SQL的寫法