Oracle常見問題集(三)


151. 如何監控 SGA 中字典緩衝區的命中率?

select parameter, gets,Getmisses , getmisses/(gets+getmisses)*100

"miss ratio",

(1-(sum(getmisses)/ (sum(gets)+sum(getmisses))))*100 "Hit ratio"

from v$rowcache

where gets+getmisses <>0

group by parameter, gets, getmisses;

152. 如何監控 SGA 中共用緩存區的命中率,應該小於1% ?

select sum(pins) "Total Pins", sum(reloads) "Total Reloads",

sum(reloads)/sum(pins) *100 libcache

from v$librarycache;

select sum(pinhits-reloads)/sum(pins) "hit

radio",sum(reloads)/sum(pins) "reload percent"

from v$librarycache;

153. 如何顯示所有資料庫物件的類別和大小?

select count(name) num_instances ,type ,sum(source_size)

source_size ,

sum(parsed_size) parsed_size ,sum(code_size) code_size

,sum(error_size) error_size,

sum(source_size) +sum(parsed_size) +sum(code_size)

+sum(error_size) size_required

from dba_object_size

group by type order by 2;

154. 監控 SGA 中重做日誌緩存區的命中率,應該小於1%

SELECT name, gets, misses, immediate_gets, immediate_misses,

Decode(gets,0,0,misses/gets*100) ratio1,

Decode(immediate_gets+immediate_misses,0,0,

immediate_misses/(immediate_gets+immediate_misses)*100) ratio2

FROM v$latch WHERE name IN ('redo allocation', 'redo copy');

155. 監控記憶體和硬碟的排序比率,最好使它小於 .10,增加 sort_area_size

SELECT name, value FROM v$sysstat WHERE name IN ('sorts (memory)',

'sorts (disk)');

156. 如何監控當前資料庫誰在運行什麽SQL語句?

SELECT osuser, username, sql_text from v$session a, v$sqltext b

where a.sql_address =b.address order by address, piece;

157. 如何監控字典緩衝區?

SELECT (SUM(PINS - RELOADS)) / SUM(PINS) "LIB CACHE" FROM

V$LIBRARYCACHE;

SELECT (SUM(GETS - GETMISSES - USAGE - FIXED)) / SUM(GETS) "ROW

CACHE" FROM V$ROWCACHE;

SELECT SUM(PINS) "EXECUTIONS", SUM(RELOADS) "CACHE MISSES WHILE

EXECUTING" FROM V$LIBRARYCACHE;

後者除以前者,此比率小於1%,接近0%爲好。

SELECT SUM(GETS) "DICTIONARY GETS",SUM(GETMISSES) "DICTIONARY

CACHE GET MISSES"

FROM V$ROWCACHE

158. 監控 MTS

select busy/(busy+idle) "shared servers busy" from v$dispatcher;

此值大於0.5時,參數需加大

select sum(wait)/sum(totalq) "dispatcher waits" from v$queue where

type='dispatcher';

select count(*) from v$dispatcher;

select servers_highwater from v$mts;

servers_highwater接近mts_max_servers時,參數需加大

159. 如何知道當前用戶的ID號

SQL>SHOW USER;

OR

SQL>select user from dual;

160. 如何查看碎片程度高的表

SELECT segment_name table_name , COUNT(*) extents

FROM dba_segments WHERE owner NOT IN ('SYS', 'SYSTEM') GROUP BY

segment_name

HAVING COUNT() = (SELECT MAX( COUNT() ) FROM dba_segments GROUP

BY segment_name);

162. 如何知道表在表空間中的存儲情況

select segment_name,sum(bytes),count(*) ext_quan from dba_extents

where

tablespace_name='&tablespace_name' and segment_type='TABLE' group

by tablespace_name,segment_name;

163. 如何知道索引在表空間中的存儲情況

select segment_name,count(*) from dba_extents where

segment_type='INDEX' and owner='&owner'

group by segment_name;

164、如何知道使用CPU多的用戶session

11是cpu used by this session

select a.sid,spid,status,substr(a.program,1,40)

prog,a.terminal,osuser,value/60/100 value

from v$session a,v$process b,v$sesstat c

where c.statistic#=11 and c.sid=a.sid and a.paddr=b.addr order by

value desc;

165. 如何知道監聽器日誌文件

以8I爲例

$ORACLE_HOME/NETWORK/LOG/LISTENER.LOG

166. 如何知道監聽器參數文件

以8I爲例

$ORACLE_HOME/NETWORK/ADMIN/LISTENER.ORA

167. 如何知道TNS 連接文件

以8I爲例

$ORACLE_HOME/NETWORK/ADMIN/TNSNAMES.ORA

168. 如何知道Sql*Net 環境文件

以8I爲例

$ORACLE_HOME/NETWORK/ADMIN/SQLNET.ORA

169. 如何知道警告日誌文件

以8I爲例

$ORACLE_HOME/ADMIN/SID/BDUMP/SIDALRT.LOG

170. 如何知道基本結構

以8I爲例

$ORACLE_HOME/RDBMS/ADMIN/STANDARD.SQL

171. 如何知道建立資料字典視圖

以8I爲例

$ORACLE_HOME/RDBMS/ADMIN/CATALOG.SQL

172. 如何知道建立審計用資料字典視圖

以8I爲例

$ORACLE_HOME/RDBMS/ADMIN/CATAUDIT.SQL

173. 如何知道建立快照用資料字典視圖

以8I爲例

$ORACLE_HOME/RDBMS/ADMIN/CATSNAP.SQL

本講主要講的是SQL語句的優化方法! 主要基於ORACLE9I的.

174. /+ALL_ROWS/

表明對語句塊選擇基於開銷的優化方法,並獲得最佳吞吐量,使資源消耗最小化.

例如:

SELECT /+ALL+_ROWS/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE

EMP_NO='CCBZZP';

175. /+FIRST_ROWS/

表明對語句塊選擇基於開銷的優化方法,並獲得最佳回應時間,使資源消耗最小化.

例如:

SELECT /+FIRST_ROWS/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE

EMP_NO='CCBZZP';

176. /+CHOOSE/

表明如果資料字典中有訪問表的統計資訊,將基於開銷的優化方法,並獲得最佳的吞吐量;

表明如果資料字典中沒有訪問表的統計資訊,將基於規則開銷的優化方法;

例如:

SELECT /+CHOOSE/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE

EMP_NO='CCBZZP';

177. /+RULE/

表明對語句塊選擇基於規則的優化方法.

例如:

SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE

EMP_NO='CCBZZP';

178. /+FULL(TABLE)/

表明對表選擇全局掃描的方法.

例如:

SELECT /+FULL(A)/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE

EMP_NO='CCBZZP';

179. /+ROWID(TABLE)/

提示明確表明對指定表根據ROWID進行訪問.

例如:

SELECT /+ROWID(BSEMPMS)/ * FROM BSEMPMS WHERE

ROWID>='AAAAAAAAAAAAAA'

AND EMP_NO='CCBZZP';

180. /+CLUSTER(TABLE)/

提示明確表明對指定表選擇簇掃描的訪問方法,它只對簇物件有效.

例如:

SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS

WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

181. /+INDEX(TABLE INDEX_NAME)/

表明對表選擇索引的掃描方法.

例如:

SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE

FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';

182. /+INDEX_ASC(TABLE INDEX_NAME)/

表明對表選擇索引昇冪的掃描方法.

例如:

SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE

DPT_NO='CCBZZP';

183. /+INDEX_COMBINE/

爲指定表選擇點陣圖訪問路經,如果INDEX_COMBINE中沒有提供作爲參數的索引,將選擇出點陣圖索引的

布林組合方式.

例如:

SELECT /+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)/ * FROM

BSEMPMS

WHERE SAL<5000000 AND HIREDATE

   1<sysdate;< span="">   
   2  
   3  
   4  
   5184\. /*+INDEX_JOIN(TABLE INDEX_NAME)*/   
   6  
   7提示明確命令優化器使用索引作爲訪問路徑.   
   8  
   9例如:   
  10  
  11SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE   
  12  
  13FROM BSEMPMS WHERE SAL&lt;60000;   
  14  
  15  
  16  
  17185\. /*+INDEX_DESC(TABLE INDEX_NAME)*/   
  18  
  19表明對表選擇索引降冪的掃描方法.   
  20  
  21例如:   
  22  
  23SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE   
  24  
  25DPT_NO='CCBZZP';   
  26  
  27  
  28  
  29186\. /*+INDEX_FFS(TABLE INDEX_NAME)*/   
  30  
  31對指定的表執行快速全索引掃描,而不是全表掃描的辦法.   
  32  
  33例如:   
  34  
  35SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE   
  36  
  37DPT_NO='TEC305';   
  38  
  39  
  40  
  41187\. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/   
  42  
  43提示明確進行執行規劃的選擇,將幾個單列索引的掃描合起來.   
  44  
  45例如:   
  46  
  47SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM   
  48  
  49BSEMPMS WHERE EMP_NO='CCBZZP' AND DPT_NO='TDC306';   
  50  
  51  
  52  
  53188\. /*+USE_CONCAT*/   
  54  
  55對查詢中的WHERE後面的OR條件進行轉換爲UNION ALL的組合查詢.   
  56  
  57例如:   
  58  
  59SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND   
  60  
  61SEX='M';   
  62  
  63189\. /*+NO_EXPAND*/   
  64  
  65對於WHERE後面的OR 或者IN-LIST的查詢語句,NO_EXPAND將阻止其基於優化器對其進行擴展.   
  66  
  67例如:   
  68  
  69SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND   
  70  
  71SEX='M';   
  72  
  73  
  74  
  75190\. /*+NOWRITE*/   
  76  
  77禁止對查詢塊的查詢重寫操作.   
  78  
  79  
  80  
  81191\. /*+REWRITE*/   
  82  
  83可以將視圖作爲參數.   
  84  
  85  
  86  
  87192\. /*+MERGE(TABLE)*/   
  88  
  89能夠對視圖的各個查詢進行相應的合併.   
  90  
  91例如:   
  92  
  93SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A   
  94  
  95(SELET DPT_NO   
  96  
  97,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE   
  98  
  99A.DPT_NO=V.DPT_NO   
 100  
 101AND A.SAL&gt;V.AVG_SAL;   
 102  
 103  
 104  
 105193\. /*+NO_MERGE(TABLE)*/   
 106  
 107對於有可合併的視圖不再合併.   
 108  
 109例如:   
 110  
 111SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS   
 112  
 113A (SELET DPT_NO   
 114  
 115,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE   
 116  
 117A.DPT_NO=V.DPT_NO   
 118  
 119AND A.SAL&gt;V.AVG_SAL;   
 120  
 121  
 122  
 123194\. /*+ORDERED*/   
 124  
 125根據表出現在FROM中的順序,ORDERED使ORACLE依此順序對其連接.   
 126  
 127例如:   
 128  
 129SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2   
 130  
 131B,TABLE3 C   
 132  
 133WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;   
 134  
 135  
 136  
 137195\. /*+USE_NL(TABLE)*/   
 138  
 139將指定表與嵌套的連接的行源進行連接,並把指定表作爲內部表.   
 140  
 141例如:   
 142  
 143SELECT /*+ORDERED USE_NL(BSEMPMS)*/   
 144  
 145BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS   
 146  
 147WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;   
 148  
 149  
 150  
 151196\. /*+USE_MERGE(TABLE)*/   
 152  
 153將指定的表與其他行源通過合併排序連接方式連接起來.   
 154  
 155例如:   
 156  
 157SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS   
 158  
 159WHERE   
 160  
 161BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;   
 162  
 163  
 164  
 165197\. /*+USE_HASH(TABLE)*/   
 166  
 167將指定的表與其他行源通過哈希連接方式連接起來.   
 168  
 169例如:   
 170  
 171SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE   
 172  
 173BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;   
 174  
 175  
 176  
 177198\. /*+DRIVING_SITE(TABLE)*/   
 178  
 179強制與ORACLE所選擇的位置不同的表進行查詢執行.   
 180  
 181例如:   
 182  
 183SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE   
 184  
 185BSEMPMS.DPT_NO=DEPT.DPT_NO;   
 186  
 187  
 188  
 189199\. /*+LEADING(TABLE)*/   
 190  
 191將指定的表作爲連接次序中的首表.   
 192  
 193  
 194  
 195200\. /*+CACHE(TABLE)*/   
 196  
 197當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端   
 198  
 199例如:   
 200  
 201SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;   
 202  
 203  
 204  
 205201\. /*+NOCACHE(TABLE)*/   
 206  
 207當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端   
 208  
 209例如:   
 210  
 211SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;   
 212  
 213  
 214  
 215202\. /*+APPEND*/   
 216  
 217直接插入到表的最後,可以提高速度.   
 218  
 219insert /*+append*/ into test1 select * from test4 ;   
 220  
 221  
 222  
 223203\. /*+NOAPPEND*/   
 224  
 225通過在插入語句生存期內停止並行模式來啓動常規插入.   
 226  
 227insert /*+noappend*/ into test1 select * from test4 ;   
 228  
 229  
 230  
 231ORACLE優化器   
 232  
 233. 選用適合的ORACLE優化器   
 234  
 235  
 236  
 237ORACLE的優化器共有3種:   
 238  
 239a. RULE (基於規則) b. COST (基於成本) c. CHOOSE (選擇性)   
 240  
 241  
 242  
 243設置缺省的優化器,可以通過對init.ora文件中OPTIMIZER_MODE參數的各種聲明,如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS   
 244  
 245. 你當然也在SQL句級或是會話(session)級對其進行覆蓋.   
 246  
 247爲了使用基於成本的優化器(CBO, Cost-Based Optimizer) , 你必須經常運行analyze   
 248  
 249命令,以增加資料庫中的物件統計資訊(object statistics)的準確性.   
 250  
 251如果資料庫的優化器模式設置爲選擇性(CHOOSE),那麽實際的優化器模式將和是否運行過analyze命令有關.   
 252  
 253如果table已經被analyze過, 優化器模式將自動成爲CBO , 反之,資料庫將採用RULE形式的優化器.   
 254  
 255  
 256  
 257在缺省情況下,ORACLE採用CHOOSE優化器, 爲了避免那些不必要的全表掃描(full table scan) ,   
 258  
 259你必須儘量避免使用CHOOSE優化器,而直接採用基於規則或者基於成本的優化器.   
 260  
 261  
 262  
 2632\. 訪問Table的方式   
 264  
 265  
 266  
 267ORACLE 採用兩種訪問表中記錄的方式:   
 268  
 269  
 270  
 271a. 全表掃描   
 272  
 273全表掃描就是順序地訪問表中每條記錄. ORACLE採用一次讀入多個資料塊(database block)的方式優化全表掃描.   
 274  
 275  
 276  
 277b. 通過ROWID訪問表   
 278  
 279你可以採用基於ROWID的訪問方式情況,提高訪問表的效率, ,   
 280  
 281ROWID包含了表中記錄的物理位置資訊..ORACLE採用索引(INDEX)實現了資料和存放資料的物理位置(ROWID)之間的聯繫.   
 282  
 283通常索引提供了快速訪問ROWID的方法,因此那些基於索引列的查詢就可以得到性能上的提高.   
 284  
 285  
 286  
 2873\. 共用SQL語句   
 288  
 289  
 290  
 291爲了不重復解析相同的SQL語句,在第一次解析之後, ORACLE將SQL語句存放在記憶體中.這塊位於系統全局區域SGA(system   
 292  
 293global area)的共用池(shared buffer pool)中的記憶體可以被所有的資料庫用戶共用.   
 294  
 295因此,當你執行一個SQL語句(有時被稱爲一個游標)時,如果它   
 296  
 297和之前的執行過的語句完全相同, ORACLE就能很快獲得已經被解析的語句以及最好的   
 298  
 299執行路徑. ORACLE的這個功能大大地提高了SQL的執行性能並節省了記憶體的使用.   
 300  
 301可惜的是ORACLE只對簡單的表提供高速緩衝(cache buffering) ,這個功能並不適用於多表連接查詢.   
 302  
 303資料庫管理員必須在init.ora中爲這個區域設置合適的參數,當這個記憶體區域越大,就可以保留更多的語句,當然被共用的可能性也就越大了.   
 304  
 305當你向ORACLE 提交一個SQL語句,ORACLE會首先在這塊記憶體中查找相同的語句.   
 306  
 307這裏需要注明的是,ORACLE對兩者採取的是一種嚴格匹配,要達成共用,SQL語句必須   
 308  
 309完全相同(包括空格,換行等).   
 310  
 311共用的語句必須滿足三個條件:   
 312  
 313  
 314  
 315A. 字元級的比較:   
 316  
 317當前被執行的語句和共用池中的語句必須完全相同.   
 318  
 319例如:   
 320  
 321SELECT * FROM EMP;   
 322  
 323和下列每一個都不同   
 324  
 325SELECT * from EMP;   
 326  
 327Select * From Emp;   
 328  
 329SELECT * FROM EMP;   
 330  
 331B. 兩個語句所指的物件必須完全相同:   
 332  
 333例如:   
 334  
 335用戶 物件名 如何訪問   
 336  
 337Jack sal_limit private synonym   
 338  
 339Work_city public synonym   
 340  
 341Plant_detail public synonym   
 342  
 343  
 344  
 345Jill sal_limit private synonym   
 346  
 347Work_city public synonym   
 348  
 349Plant_detail table owner   
 350  
 351  
 352  
 353考慮一下下列SQL語句能否在這兩個用戶之間共用.   
 354  
 355SQL   
 356  
 357能否共用   
 358  
 359原因   
 360  
 361select max(sal_cap) from sal_limit;   
 362  
 363不能   
 364  
 365每個用戶都有一個private synonym - sal_limit , 它們是不同的物件   
 366  
 367select count(*0 from work_city where sdesc like 'NEW%';   
 368  
 369 370  
 371兩個用戶訪問相同的物件public synonym - work_city   
 372  
 373select a.sdesc,b.location from work_city a , plant_detail b where   
 374  
 375a.city_id = b.city_id   
 376  
 377不能   
 378  
 379用戶jack 通過private synonym訪問plant_detail 而jill 是表的所有者,物件不同.   
 380  
 381C. 兩個SQL語句中必須使用相同的名字的綁定變數(bind variables)   
 382  
 383  
 384  
 385例如:   
 386  
 387  
 388  
 389第一組的兩個SQL語句是相同的(可以共用),而第二組中的兩個語句是不同的(即使在運行時,賦於不同的綁定變數相同的值)   
 390  
 391a.   
 392  
 393select pin , name from people where pin = :blk1.pin;   
 394  
 395select pin , name from people where pin = :blk1.pin;   
 396  
 397  
 398  
 399b.   
 400  
 401select pin , name from people where pin = :blk1.ot_ind;   
 402  
 403select pin , name from people where pin = :blk1.ov_ind;   
 404  
 4054\. 選擇最有效率的表名順序(只在基於規則的優化器中有效)   
 406  
 407ORACLE的解析器按照從右到左的順序處理FROM子句中的表名,因此FROM子句中寫在最後的表(基礎表 driving   
 408  
 409table)將被最先處理. 在FROM子句中包含多個表的情況下,你必須選擇記錄條數最少的表作爲基礎表.當ORACLE處理多個表時,   
 410  
 411會運用排序及合併的方式連接它們.首先,掃描第一個表(FROM子句中最後的那個表)並對記錄進行派序,然後掃描第二個表(FROM子句中最後第二個表),最後將所有從第二個表中檢索出的記錄與第一個表中合適記錄進行合併.   
 412  
 413  
 414  
 415例如:   
 416  
 417表 TAB1 16,384 條記錄   
 418  
 419表 TAB2 1 條記錄   
 420  
 421  
 422  
 423選擇TAB2作爲基礎表 (最好的方法)   
 424  
 425select count(*) from tab1,tab2 執行時間0.96秒   
 426  
 427  
 428  
 429選擇TAB2作爲基礎表 (不佳的方法)   
 430  
 431select count(*) from tab2,tab1 執行時間26.09秒   
 432  
 433  
 434  
 435如果有3個以上的表連接查詢, 那就需要選擇交叉表(intersection table)作爲基礎表,   
 436  
 437交叉表是指那個被其他表所引用的表.   
 438  
 439  
 440  
 441例如:   
 442  
 443  
 444  
 445EMP表描述了LOCATION表和CATEGORY表的交集.   
 446  
 447  
 448  
 449SELECT *   
 450  
 451FROM LOCATION L ,   
 452  
 453CATEGORY C,   
 454  
 455EMP E   
 456  
 457WHERE E.EMP_NO BETWEEN 1000 AND 2000   
 458  
 459AND E.CAT_NO = C.CAT_NO   
 460  
 461AND E.LOCN = L.LOCN   
 462  
 463  
 464  
 465將比下列SQL更有效率   
 466  
 467  
 468  
 469SELECT *   
 470  
 471FROM EMP E ,   
 472  
 473LOCATION L ,   
 474  
 475CATEGORY C   
 476  
 477WHERE E.CAT_NO = C.CAT_NO   
 478  
 479AND E.LOCN = L.LOCN   
 480  
 481AND E.EMP_NO BETWEEN 1000 AND 2000   
 482  
 4835\. WHERE子句中的連接順序.   
 484  
 485  
 486  
 487ORACLE採用自下而上的順序解析WHERE子句,根據這個原理,表之間的連接必須寫在其他WHERE條件之前,   
 488  
 489那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾.   
 490  
 491  
 492  
 493例如:   
 494  
 495  
 496  
 497(低效,執行時間156.3秒)   
 498  
 499SELECT …   
 500  
 501FROM EMP E   
 502  
 503WHERE SAL &gt; 50000   
 504  
 505AND JOB = ‘MANAGER’   
 506  
 507AND 25 &lt; (SELECT COUNT(*) FROM EMP   
 508  
 509WHERE MGR=E.EMPNO);   
 510  
 511  
 512  
 513(高效,執行時間10.6秒)   
 514  
 515SELECT …   
 516  
 517FROM EMP E   
 518  
 519WHERE 25 &lt; (SELECT COUNT(*) FROM EMP   
 520  
 521WHERE MGR=E.EMPNO)   
 522  
 523AND SAL &gt; 50000   
 524  
 525AND JOB = ‘MANAGER’;   
 526  
 5276\. SELECT子句中避免使用 ‘ * ‘   
 528  
 529當你想在SELECT子句中列出所有的COLUMN時,使用動態SQL列引用 ‘*’   
 530  
 531是一個方便的方法.不幸的是,這是一個非常低效的方法. 實際上,ORACLE在解析的過程中, 會將’*’ 依次轉換成所有的列名,   
 532  
 533這個工作是通過查詢資料字典完成的, 這意味著將耗費更多的時間.   
 534  
 5357\. 減少訪問資料庫的次數   
 536  
 537當執行每條SQL語句時, ORACLE在內部執行了許多工作: 解析SQL語句, 估算索引的利用率, 綁定變數 , 讀資料塊等等.   
 538  
 539由此可見, 減少訪問資料庫的次數 , 就能實際上減少ORACLE的工作量.   
 540  
 541  
 542  
 543例如,   
 544  
 545以下有三種方法可以檢索出雇員號等於0342或0291的職員.   
 546  
 547  
 548  
 549方法1 (最低效)   
 550  
 551SELECT EMP_NAME , SALARY , GRADE   
 552  
 553FROM EMP   
 554  
 555WHERE EMP_NO = 342;   
 556  
 557  
 558  
 559SELECT EMP_NAME , SALARY , GRADE   
 560  
 561FROM EMP   
 562  
 563WHERE EMP_NO = 291;   
 564  
 565  
 566  
 567方法2 (次低效)   
 568  
 569  
 570  
 571DECLARE   
 572  
 573CURSOR C1 (E_NO NUMBER) IS   
 574  
 575SELECT EMP_NAME,SALARY,GRADE   
 576  
 577FROM EMP   
 578  
 579WHERE EMP_NO = E_NO;   
 580  
 581BEGIN   
 582  
 583OPEN C1(342);   
 584  
 585FETCH C1 INTO …,..,.. ;   
 586  
 587…..   
 588  
 589OPEN C1(291);   
 590  
 591FETCH C1 INTO …,..,.. ;   
 592  
 593CLOSE C1;   
 594  
 595END;   
 596  
 597  
 598  
 599方法3 (高效)   
 600  
 601  
 602  
 603SELECT A.EMP_NAME , A.SALARY , A.GRADE,   
 604  
 605B.EMP_NAME , B.SALARY , B.GRADE   
 606  
 607FROM EMP A,EMP B   
 608  
 609WHERE A.EMP_NO = 342   
 610  
 611AND B.EMP_NO = 291;   
 612  
 613注意:   
 614  
 615在SQL*Plus , SQL*Forms和Pro*C中重新設置ARRAYSIZE參數, 可以增加每次資料庫訪問的檢索資料量   
 616  
 617,建議值爲200   
 618  
 619  
 620  
 6218\. 使用DECODE函數來減少處理時間   
 622  
 623使用DECODE函數可以避免重復掃描相同記錄或重復連接相同的表.   
 624  
 625  
 626  
 627例如:   
 628  
 629SELECT COUNT(*),SUM(SAL)   
 630  
 631FROM EMP   
 632  
 633WHERE DEPT_NO = 0020   
 634  
 635AND ENAME LIKE ‘SMITH%’;   
 636  
 637  
 638  
 639SELECT COUNT(*),SUM(SAL)   
 640  
 641FROM EMP   
 642  
 643WHERE DEPT_NO = 0030   
 644  
 645AND ENAME LIKE ‘SMITH%’;   
 646  
 647  
 648  
 649你可以用DECODE函數高效地得到相同結果   
 650  
 651  
 652  
 653SELECT COUNT(DECODE(DEPT_NO,0020,’X’,NULL)) D0020_COUNT,   
 654  
 655COUNT(DECODE(DEPT_NO,0030,’X’,NULL)) D0030_COUNT,   
 656  
 657SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL,   
 658  
 659SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL   
 660  
 661FROM EMP WHERE ENAME LIKE ‘SMITH%’;   
 662  
 663  
 664  
 665類似的,DECODE函數也可以運用於GROUP BY 和ORDER BY子句中.   
 666  
 6679\. 整合簡單,無關聯的資料庫訪問   
 668  
 669  
 670  
 671如果你有幾個簡單的資料庫查詢語句,你可以把它們整合到一個查詢中(即使它們之間沒有關係)   
 672  
 673例如:   
 674  
 675  
 676  
 677SELECT NAME   
 678  
 679FROM EMP   
 680  
 681WHERE EMP_NO = 1234;   
 682  
 683  
 684  
 685SELECT NAME   
 686  
 687FROM DPT   
 688  
 689WHERE DPT_NO = 10 ;   
 690  
 691  
 692  
 693SELECT NAME   
 694  
 695FROM CAT   
 696  
 697WHERE CAT_TYPE = ‘RD’;   
 698  
 699  
 700  
 701上面的3個查詢可以被合併成一個:   
 702  
 703  
 704  
 705SELECT E.NAME , D.NAME , C.NAME   
 706  
 707FROM CAT C , DPT D , EMP E,DUAL X   
 708  
 709WHERE NVL(‘X’,X.DUMMY) = NVL(‘X’,E.ROWID(+))   
 710  
 711AND NVL(‘X’,X.DUMMY) = NVL(‘X’,D.ROWID(+))   
 712  
 713AND NVL(‘X’,X.DUMMY) = NVL(‘X’,C.ROWID(+))   
 714  
 715AND E.EMP_NO(+) = 1234   
 716  
 717AND D.DEPT_NO(+) = 10   
 718  
 719AND C.CAT_TYPE(+) = ‘RD’;   
 720  
 721  
 722  
 723(譯者按: 雖然採取這種方法,效率得到提高,但是程式的可讀性大大降低,所以讀者 還是要權衡之間的利弊)   
 724  
 725  
 726  
 72710\. 刪除重復記錄   
 728  
 729最高效的刪除重復記錄方法 ( 因爲使用了ROWID)   
 730  
 731  
 732  
 733DELETE FROM EMP E   
 734  
 735WHERE E.ROWID &gt; (SELECT MIN(X.ROWID)   
 736  
 737FROM EMP X   
 738  
 739WHERE X.EMP_NO = E.EMP_NO);   
 740  
 741  
 742  
 74311\. 用TRUNCATE替代DELETE   
 744  
 745當刪除表中的記錄時,在通常情況下, 回滾段(rollback segments ) 用來存放可以被恢復的資訊.   
 746  
 747如果你沒有COMMIT事務,ORACLE會將資料恢復到刪除之前的狀態(準確地說是   
 748  
 749恢復到執行刪除命令之前的狀況)   
 750  
 751  
 752  
 753而當運用TRUNCATE時,   
 754  
 755回滾段不再存放任何可被恢復的資訊.當命令運行後,資料不能被恢復.因此很少的資源被調用,執行時間也會很短.   
 756  
 757  
 758  
 759(譯者按: TRUNCATE只在刪除全表適用,TRUNCATE是DDL不是DML)   
 760  
 76112\. 儘量多使用COMMIT   
 762  
 763  
 764  
 765只要有可能,在程式中儘量多使用COMMIT, 這樣程式的性能得到提高,需求也會因爲COMMIT所釋放的資源而減少:   
 766  
 767COMMIT所釋放的資源:   
 768  
 769a. 回滾段上用於恢復資料的資訊.   
 770  
 771b. 被程式語句獲得的鎖   
 772  
 773c. redo log buffer 中的空間   
 774  
 775d. ORACLE爲管理上述3種資源中的內部花費   
 776  
 777  
 778  
 779(譯者按: 在使用COMMIT時必須要注意到事務的完整性,現實中效率和事務完整性往往是魚和熊掌不可得兼)   
 780  
 781  
 782  
 78313\. 計算記錄條數   
 784  
 785和一般的觀點相反, count(*) 比count(1)稍快 , 當然如果可以通過索引檢索,對索引列的計數仍舊是最快的. 例如   
 786  
 787COUNT(EMPNO)   
 788  
 789  
 790  
 791(譯者按: 在CSDN論壇中,曾經對此有過相當熱烈的討論,   
 792  
 793作者的觀點並不十分準確,通過實際的測試,上述三種方法並沒有顯著的性能差別)   
 794  
 795  
 796  
 79714\. 用Where子句替換HAVING子句   
 798  
 799  
 800  
 801避免使用HAVING子句, HAVING 只會在檢索出所有記錄之後才對結果集進行過濾. 這個處理需要排序,總計等操作.   
 802  
 803如果能通過WHERE子句限制記錄的數目,那就能減少這方面的開銷.   
 804  
 805  
 806  
 807例如:   
 808  
 809  
 810  
 811低效:   
 812  
 813SELECT REGION,AVG(LOG_SIZE)   
 814  
 815FROM LOCATION   
 816  
 817GROUP BY REGION   
 818  
 819HAVING REGION REGION != ‘SYDNEY’   
 820  
 821AND REGION != ‘PERTH’   
 822  
 823  
 824  
 825高效   
 826  
 827SELECT REGION,AVG(LOG_SIZE)   
 828  
 829FROM LOCATION   
 830  
 831WHERE REGION REGION != ‘SYDNEY’   
 832  
 833AND REGION != ‘PERTH’   
 834  
 835GROUP BY REGION   
 836  
 837(譯者按: HAVING 中的條件一般用於對一些集合函數的比較,如COUNT() 等等.   
 838  
 839除此而外,一般的條件應該寫在WHERE子句中)   
 840  
 841  
 842  
 84315\. 減少對表的查詢   
 844  
 845在含有子查詢的SQL語句中,要特別注意減少對表的查詢.   
 846  
 847  
 848  
 849例如:   
 850  
 851低效   
 852  
 853SELECT TAB_NAME   
 854  
 855FROM TABLES   
 856  
 857WHERE TAB_NAME = ( SELECT TAB_NAME   
 858  
 859FROM TAB_COLUMNS   
 860  
 861WHERE VERSION = 604)   
 862  
 863AND DB_VER= ( SELECT DB_VER   
 864  
 865FROM TAB_COLUMNS   
 866  
 867WHERE VERSION = 604)   
 868  
 869  
 870  
 871高效   
 872  
 873SELECT TAB_NAME   
 874  
 875FROM TABLES   
 876  
 877WHERE (TAB_NAME,DB_VER)   
 878  
 879= ( SELECT TAB_NAME,DB_VER)   
 880  
 881FROM TAB_COLUMNS   
 882  
 883WHERE VERSION = 604)   
 884  
 885  
 886  
 887Update 多個Column 例子:   
 888  
 889低效:   
 890  
 891UPDATE EMP   
 892  
 893SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES),   
 894  
 895SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES)   
 896  
 897WHERE EMP_DEPT = 0020;   
 898  
 899  
 900  
 901高效:   
 902  
 903UPDATE EMP   
 904  
 905SET (EMP_CAT, SAL_RANGE)   
 906  
 907= (SELECT MAX(CATEGORY) , MAX(SAL_RANGE)   
 908  
 909FROM EMP_CATEGORIES)   
 910  
 911WHERE EMP_DEPT = 0020;   
 912  
 91316\. 通過內部函數提高SQL效率.   
 914  
 915  
 916  
 917SELECT H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC,COUNT(*)   
 918  
 919FROM HISTORY_TYPE T,EMP E,EMP_HISTORY H   
 920  
 921WHERE H.EMPNO = E.EMPNO   
 922  
 923AND H.HIST_TYPE = T.HIST_TYPE   
 924  
 925GROUP BY H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC;   
 926  
 927  
 928  
 929通過調用下面的函數可以提高效率.   
 930  
 931FUNCTION LOOKUP_HIST_TYPE(TYP IN NUMBER) RETURN VARCHAR2   
 932  
 933AS   
 934  
 935TDESC VARCHAR2(30);   
 936  
 937CURSOR C1 IS   
 938  
 939SELECT TYPE_DESC   
 940  
 941FROM HISTORY_TYPE   
 942  
 943WHERE HIST_TYPE = TYP;   
 944  
 945BEGIN   
 946  
 947OPEN C1;   
 948  
 949FETCH C1 INTO TDESC;   
 950  
 951CLOSE C1;   
 952  
 953RETURN (NVL(TDESC,’ ’));   
 954  
 955END;   
 956  
 957  
 958  
 959FUNCTION LOOKUP_EMP(EMP IN NUMBER) RETURN VARCHAR2   
 960  
 961AS   
 962  
 963ENAME VARCHAR2(30);   
 964  
 965CURSOR C1 IS   
 966  
 967SELECT ENAME   
 968  
 969FROM EMP   
 970  
 971WHERE EMPNO=EMP;   
 972  
 973BEGIN   
 974  
 975OPEN C1;   
 976  
 977FETCH C1 INTO ENAME;   
 978  
 979CLOSE C1;   
 980  
 981RETURN (NVL(ENAME,’ ’));   
 982  
 983END;   
 984  
 985  
 986  
 987SELECT H.EMPNO,LOOKUP_EMP(H.EMPNO),   
 988  
 989H.HIST_TYPE,LOOKUP_HIST_TYPE(H.HIST_TYPE),COUNT(*)   
 990  
 991FROM EMP_HISTORY H   
 992  
 993GROUP BY H.EMPNO , H.HIST_TYPE;   
 994  
 995  
 996  
 997(譯者按: 經常在論壇中看到如 ’能不能用一個SQL寫出….’ 的貼子, 殊不知複雜的SQL往往犧牲了執行效率.   
 998  
 999能夠掌握上面的運用函數解決問題的方法在實際工作中是非常有意義的)   
1000  
1001  
1002  
100317\. 使用表的別名(Alias)   
1004  
1005當在SQL語句中連接多個表時,   
1006  
1007請使用表的別名並把別名字首於每個Column上.這樣一來,就可以減少解析的時間並減少那些由Column歧義引起的語法錯誤.   
1008  
1009  
1010  
1011(譯者注:   
1012  
1013Column歧義指的是由於SQL中不同的表具有相同的Column名,當SQL語句中出現這個Column時,SQL解析器無法判斷這個Column的歸屬)   
1014  
1015  
1016  
101718\. 用EXISTS替代IN   
1018  
1019在許多基於基礎表的查詢中,爲了滿足一個條件,往往需要對另一個表進行聯接.在這種情況下, 使用EXISTS(或NOT   
1020  
1021EXISTS)通常將提高查詢的效率.   
1022  
1023  
1024  
1025低效:   
1026  
1027SELECT *   
1028  
1029FROM EMP (基礎表)   
1030  
1031WHERE EMPNO &gt; 0   
1032  
1033AND DEPTNO IN (SELECT DEPTNO   
1034  
1035FROM DEPT   
1036  
1037WHERE LOC = ‘MELB’)   
1038  
1039  
1040  
1041高效:   
1042  
1043SELECT *   
1044  
1045FROM EMP (基礎表)   
1046  
1047WHERE EMPNO &gt; 0   
1048  
1049AND EXISTS (SELECT ‘X’   
1050  
1051FROM DEPT   
1052  
1053WHERE DEPT.DEPTNO = EMP.DEPTNO   
1054  
1055AND LOC = ‘MELB’)   
1056  
1057(譯者按: 相對來說,用NOT EXISTS替換NOT IN 將更顯著地提高效率,下一節中將指出)   
1058  
105919\. 用NOT EXISTS替代NOT IN   
1060  
1061在子查詢中,NOT IN子句將執行一個內部的排序和合併. 無論在哪種情況下,NOT IN都是最低效的   
1062  
1063(因爲它對子查詢中的表執行了一個全表遍曆). 爲了避免使用NOT IN ,我們可以把它改寫成外連接(Outer Joins)或NOT   
1064  
1065EXISTS.   
1066  
1067  
1068  
1069例如:   
1070  
1071SELECT …   
1072  
1073FROM EMP   
1074  
1075WHERE DEPT_NO NOT IN (SELECT DEPT_NO   
1076  
1077FROM DEPT   
1078  
1079WHERE DEPT_CAT=’A’);   
1080  
1081  
1082  
1083爲了提高效率.改寫爲:   
1084  
1085  
1086  
1087(方法一: 高效)   
1088  
1089SELECT ….   
1090  
1091FROM EMP A,DEPT B   
1092  
1093WHERE A.DEPT_NO = B.DEPT(+)   
1094  
1095AND B.DEPT_NO IS NULL   
1096  
1097AND B.DEPT_CAT(+) = ‘A’   
1098  
1099(方法二: 最高效)   
1100  
1101SELECT ….   
1102  
1103FROM EMP E   
1104  
1105WHERE NOT EXISTS (SELECT ‘X’   
1106  
1107FROM DEPT D   
1108  
1109WHERE D.DEPT_NO = E.DEPT_NO   
1110  
1111AND DEPT_CAT = ‘A’);   
1112  
111320\. 用表連接替換EXISTS   
1114  
1115  
1116  
1117通常來說 , 採用表連接的方式比EXISTS更有效率   
1118  
1119SELECT ENAME   
1120  
1121FROM EMP E   
1122  
1123WHERE EXISTS (SELECT ‘X’   
1124  
1125FROM DEPT   
1126  
1127WHERE DEPT_NO = E.DEPT_NO   
1128  
1129AND DEPT_CAT = ‘A’);   
1130  
1131  
1132  
1133(更高效)   
1134  
1135SELECT ENAME   
1136  
1137FROM DEPT D,EMP E   
1138  
1139WHERE E.DEPT_NO = D.DEPT_NO   
1140  
1141AND DEPT_CAT = ‘A’ ;   
1142  
1143  
1144  
1145(譯者按: 在RBO的情況下,前者的執行路徑包括FILTER,後者使用NESTED LOOP)</sysdate;<>
Published At
Categories with 数据库类
Tagged with
comments powered by Disqus