二.讉KTable的方?br />
ORACLE 采用两种讉K表中记录的方?
a. 全表扫描
全表扫描是序地访问表中每条记? ORACLE采用一ơ读入多个数 据块(database block)的方式优化全表扫描?br />
b. 索引扫描
你可以采用基于ROWID的访问方式情?提高讉K表的效率, ROWID包含了表中记录的物理位置信息.ORACLE采用索引(INDEX)实现了数据和存放数据的物理位|?ROWID)之间的联p? 通常索引提供了快速访问ROWID的方?因此那些Z索引列的查询可以得到性能上的提高.
其中ORACLE对烦引又有两U访问模?
a)索引唯一扫描 ( INDEX UNIQUE SCAN)
大多数情况下, 优化器通过WHERE子句讉KINDEX.
例如:
表LOADING有两个烦?: 建立在LOADING列上的唯一性烦引LOADING_PK和徏立在MANAGER列上的非唯一性烦引IDX_MANAGER.
SELECT loading
FROM LOADING
WHERE LOADING = ‘ROSE HILL’;
在内?, 上述SQL被分成两步执行, 首先 , LOADING_PK 索引通过索引唯一扫描的方式被讉K , 获得相对应的ROWID, 通过ROWID讉K表的方式执行下一步检?
如果被检索返回的列包括在INDEX列中,ORACLE不执行W二步的处理(通过ROWID讉K?. 因ؓ索数据保存在索引? 单单讉K索引可以完全满x询结?
下面SQL只需要INDEX UNIQUE SCAN 操作.
SELECT LOADING
FROM LOADING
WHERE LOADING = ‘ROSE HILL’;
b)索引范围查询(INDEX RANGE SCAN)
适用于两U情?
1. Z一个范围的?br />
2. Z非唯一性烦引的?br />
?:
SELECT LOADING
FROM LOADING
WHERE LOADING LIKE ‘M%’;
WHERE子句条g包括一pd? ORACLE通过索引范围查询的方式查询LODGING_PK . ׃索引范围查询返回一l? 它的效率p比烦引唯一扫描
低一?
?:
SELECT LOADING
FROM LOADING
WHERE MANAGER = ‘BILL GATES’;
q个SQL的执行分两步, IDX_MANAGER的烦引范围查?得到所有符合条件记录的ROWID) 和下一步同qROWID讉K表得到LOADING列的? ׃IDX_MANAGER是一个非唯一性的索引,数据库不能对它执行烦引唯一扫描.
׃SQLq回LOADING?而它q不存在于IDX_MANAGER索引? 所以在索引范围查询后会执行一个通过ROWID讉K表的操作.
WHERE子句? 如果索引列所对应的值的W一个字W由通配W?WILDCARD)开? 索引不被采?
SELECT LOADING
FROM LOADING
WHERE MANAGER LIKE ‘QHANMAN’;
在这U情况下QORACLE用全表扫?
三.SQL调优的本质就是调整执行计划?br />
在好多情况下Qoracle自动选择的执行计划ƈ不是最优的Q这旉要我们h工去q预?什么是执行计划?)
对SQL调优基本步骤Q?br />
a) 捕获SQL语句
b) 产生SQL语句的执行计划;
c) 验证l计信息(SQL语句涉及到的表格是否做过分析)Q表g?l果集的记录敎ͼ索引)Q字D上面数据分布特?br />
d) 通过手工攉到的信息QŞ成自q想的执行计划?br />
e) 如果做过分析Q则重新分析相关表格或者做q囑ֈ析?br />
f) 如果没有做过分析Q则通过试不同的HintQ从而获得合适的执行计划?br />
g) 当我们正常无法调优到位时Q可以打开10053事g打开优化器的跟踪Q看看Oracle如何选择?
alter session set events='10053 trace name context forever,level 2';
四.如何捕获SQL语句
捕获SQL语句的方法有如下几种Q?br />
1QSQL TRACE?0046跟踪某个模块?br />
2QPERFSTAT性能l计包,使用Ҏ见附录二?br />
3QV$SQLQV$SESSION_WAITQV$SQL_TEXT
五.如何查看执行计划
查看SQL语句的执行计划有以下几种Q?br />
1QSet autotrace on(set autotrace traceonly exp)
2QExplain plan for …..
@?/rdbms/admin/utlxpls.sql
3QV$SQL_PLAN视图
column operation format a16
column "Query Plan" format a60
column options format a15
column object_name format a20
column id format 99
select id,lpad(' ',2*(level-1))||operation||' '||options||' '||object_name||' '
||decode(id,0,'Cost = '||position) "Query Plan"
from (select *
from v$sql_plan
where address='&a') sql_plan
start with id = 0
connect by prior id = parent_id
/
4Q第三方工具Q如pl/sql developer,TOAD
六.SQL语句主要的连接方?/p>
a) Nested-loop join
适合于小?几千条,几万条记?与大表做联接
在联接列上有索引?/p>
分内表和外表(驱动?Q靠qfrom子句的是内表。从效率上讲Q小表应该作外表Q大表应该作内表Q即大表查询时走索引?/p>
COST= Access cost of A(驱动? + (access cost of B * number of rows from A)
成本计算ҎQ?br /> 讑ְ?00行,大表100000行?/p>
两表均有索引Q?br />
如果表在内Q大表在?驱动?的话Q则扫描ơ数为:
100000+100000*2 (其中2表示IOơ数Q一ơ烦引,一ơ数?
如果大表在内Q小表在?驱动?的话Q则扫描ơ数为:
100+100*2.
两表均无索引Q?br />
如果表在内Q大表在外的话,则扫描次CؓQ?br />
100000+100*100000
如果大表在内Q小表在外的话,则扫描次CؓQ?br />
100+100000*100
注意Q如果一个表有烦引,一个表没有索引QORACLE会将没有索引的表作驱动表。如果两个表都有索引Q则外表作驱动表。如果两个都没烦引的话,则也是外表作驱动表?/p>
基本的执行计划如下所C:
NESTED LOOPS
TABLE ACCESS (BY ROWID) OF our_outer_table
INDEX (..SCAN) OF outer_table_index(….)
TABLE ACCESS (BY ROWID) OF our_inner_table
INDEX (..SCAN) OF inner_table_index(….)
b) Hash join
适合于大表与大表Q小?几十万,几百?与大表之间的联连?br /> 联接列上不需要烦引?/p>
基本执行计划如下Q?br />
HASH JOIN
TABLE ACCESS (….) OF tableA
TABLE ACCESS (….) OF tableB
cost= (access cost of A * number of hash partitions of B) + access cost of B
可以看出主要成本在于A表是否可以被Cache。Hash_area_size的大将军_Hash Join的主要成本。可以看出Hash Join的成本和q回集合q没有直接的关系Q所以当q回l果集比较大的时候一般具有较好的性能?/p>
Z加快hash join的速度Q可以调大hash_area_size和pga_aggregate_targetQ默认ؓ25MQ的倹{?/p>
c) Sort Merge join
每一个Row Source在Join列上均排序?br /> 然后两个排序后的Row Source合ƈ后,作一个结果集q回?br /> Sort/Merge Join仅仅对equal Join有效?/p>
基本执行计划
MERGE (JOIN)
SORT (JOIN)
TABLE ACCESS (….) OF tableA
SORT (JOIN)
TABLE ACCESS (….) OF tableB
cost= access cost of A + access cost of B +(sort cost of A + sort cost of B)
可以看出Sort的成本是Merge Join的主要构成部分。这样sort_area_size的大将很大E度军_Merge Join的大。同样如果A表或者B表已l经q排序的Q那么Merge Join往往h很好的性能。其不会走烦引?/p>
没有驱动表的概念Q即时响应能力较差?/p>
七.一般情况下最常见?U问?/p>
1. Statement not written for indexes 25%
2. Indexes are missing or inappropriate 16%
3. Use of single-column index merge 15%
4. Misuse of nested loop, sort merge, or hash join 12%
5. Misuse of IN, EXISTS, NOT IN, NOT EXISTS, or table joins 8%
不过在我们这里,最常见的问题是在第2条,W?条,W?条?/p>
1Q?nbsp;Statement not written for indexes
cM于这LQ?br />
SELECT account_name, trans_date, amount
FROM transaction
WHERE SUBSTR(account_name,1,7) = ' CAPITAL';
WHERE account_name LIKE 'CAPITAL%';
Account_date 日期
To_char(Account_date,’YYYY-MM-DD:HH24:MI:SS’)=’200508XXX’;
Account_date=to_date(‘200508….’,’yyyy-mm-dd);
2QIndexes are missing or inappropriate
例如REP_C021中有q样一句:
select SUBSIDIARYID,260,' 300电话?,
sum(decode(feetype, 1, ceil(duration / 60))) +
sum(decode(feetype, 0, ceil(duration / 60))),
sum(decode(feetype, 1, ceil(duration / 60))),
sum(decode(feetype, 0, ceil(duration / 60))),0
from cardsusage200508 a, service b
where a.caller = b.servicecode and
(b.property = i_property or i_property is null) and
a.cdrtype = 102
group by SUBSIDIARYID, 260, ' 300电话?;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 SORT (GROUP BY)
2 1 NESTED LOOPS
3 2 TABLE ACCESS (FULL) OF 'CARDSUSAGE200508'
4 2 TABLE ACCESS (BY INDEX ROWID) OF 'SERVICE'
5 4 INDEX (UNIQUE SCAN) OF 'SERVICE_CODE'
我们取其中的select语句q行调优。在调整之前Q原select语句需?分钟左右?/p>
12:19:20 SQL> select cdrtype,count(*) from cardsusage200508
12:20:12 2 group by cdrtype;
CDRT COUNT(*)
---- ----------
102 637
106 1973757
107 2390097
112 46016
113 20
针对cardsuage200508表格的特性,我们在CDRTYPE字段上徏立一个位囄引CARDSUSAGE_CDRTYPE_BTIDX?br />
SQL语句加上以下HintQ?br />
select /*+ INDEX(A, CARDSUSAGE_CDRTYPE_BTIDX)*/
SUBSIDIARYID,260,' 300电话?,
sum(decode(feetype, 1, ceil(duration / 60))) +
sum(decode(feetype, 0, ceil(duration / 60))),
sum(decode(feetype, 1, ceil(duration / 60))),
sum(decode(feetype, 0, ceil(duration / 60))),0
from cardsusage200508 a, service b
where a.caller = b.servicecode and
(b.property = i_property or i_property is null) and
a.cdrtype = 102
group by SUBSIDIARYID, 260, ' 300电话?;
q样调整后,只需要几U钟卛_出来?/p>
3. Use of single-column index merge
复合索引有的时候比单列索引效率更高。根据where子句中的具体情况Q有 时可以徏立复合烦引。例如:
select a.AccountNum,a.ChargeID,a.Total,b.ItemID,
b.Amount,c.billingcycle
from charge_bill a, chargedetail_bill b, Account c
where a.AccountNum > 1 and a.AccountNum <= 1969618 and
a.status = '0' and a.InvoiceID is null and c.paymentmethod != '7' and
a.Total > 0 and a.AccountNum = c.AccountNum and
a.ChargeID = b.ChargeID
order by a.AccountNum, a.ChargeID, b.ItemID;
q样的SQL语句执行需??7U?/p>
我们做了以下优化Q?br /> 在charge_bill表格的accountnum,status,total,invoiceid列上建立一个复合烦引。这样上qSQL语句需?0U左叟?/p>
Resume Serviceq程中有q么一句:
SELECT NVL(SUM(A.FEE),0)
FROM ACCOUNTBALANCE A,INVOICE B
WHERE A.OBJECTID = B.INVOICEID AND A.ACCOUNTNUM = :b1
AND B.BILLINGBEGINDATE < TO_DATE(:b2,'yyyymmdd');
该语句需要执行大?2000ơ。整个过E执行大概需?00分钟左右?/p>
?b1以具体的g替,q条SQL语句执行很快Q大?.1U左叟?/p>
我们做了以下优化Q?br />
在invoiceid,billingbegindate列上创徏了一个烦引idx_invoice_hc?br />
上qSQL语句ҎQ?br />
select /*+ use_nl(a,b) index(b,IDX_INVOICE_HC)*/ nvl(sum(a.fee),0)
from accountbalance a,invoice b
where a.objectid=b.invoiceid and a.accountnum=m_accountnum
and b.billingbegindate
q样一来,该过E的执行旉快的时候大概在10分钟左右Q慢的时?IO异常紧张的时)大概?0分钟左右?/p>
4. Misuse of nested loop, sort merge, or hash join
表格之间的连接方式和q接序都将极大的媄响SQL语句的性能。这U问 题在qx最常见。ORACLE在处?张或5张以上的表格的连接时候,很容 易出问题。一般情况下Q}记前面表g间的q接原则Q即可以处理此类?nbsp;题?br />
例如Q?br />
select b.SUBSIDIARYID,
c.paymentmethod || ':' || nvl(subscribertype, '9999999'),
'gsm',count(*),sum(decode(untelLOCALCHARGE,
0,decode(duration,0,1,
decode(sign(duration - 1800),
1, 2 + trunc((duration - 1201) / 600),
2)), trunc((duration + 599) / 600))),
sum(nvl(GSMCHARGE, 0)),nvl(property, '0'),
SUM(trunc((duration + 599) / 600))
from rt_untelecomusage a ,service b, account c
where a.starttime >
to_date(to_char(add_months(to_date('200508 ', 'YYYYMM'), -1),
'YYYYMM') || '20235959',
'YYYYMMDDHH24MISS') and
a.starttime < to_date('200508 ' || '21', 'YYYYMMdd') and
gsmcharge > 0 and a.serviceid = b.serviceid and
b.accountnum = c.accountnum
group by b.SUBSIDIARYID,
c.paymentmethod || ':' || nvl(subscribertype, '9999999'),
'gsm',nvl(property, '0');
该语句原先需?Q?个小时左叟?/p>
优化Q?br /> alter session set hash_area_size=300000000;
select /*+ use_hash(b,c) ordered NO_EXPAND full(a) use_hash(a)*/ b.SUBSIDIARYID,c.paymentmethod || ':' || nvl(subscribertype, '9999999'),
'gsm',count(*), sum(decode(untelLOCALCHARGE,0,decode(duration,0, 1,
decode(sign(duration - 1800), 1,2 + trunc((duration - 1201) / 600), 2)),
trunc((duration + 599) / 600))),sum(nvl(GSMCHARGE, 0)),
nvl(property, '0'),SUM(trunc((duration + 599) / 600))
from service b, account c,untelecomusage_200508 a
where a.starttime >
to_date(to_char(add_months(to_date('200508', 'YYYYMM'), -1),
'YYYYMM') || '20235959',
'YYYYMMDDHH24MISS') and
a.starttime < to_date('200508' || '21', 'YYYYMMdd') and
gsmcharge > 0 and a.serviceid = b.serviceid and
b.accountnum = c.accountnum
group by b.SUBSIDIARYID,c.paymentmethod || ':' || nvl(subscribertype, '9999999'),'gsm',nvl(property, '0');
q样优化后,只需?0分钟左右卛_?/p>
八.案例
1Q?nbsp;循环Update操作
以下q程太慢了, 半个时q?000条记录都未处理,?共有7万多条?br />
declare
cursor c1 is
select caller
from zxx_sms_step where chargemonth=200504 and fee is null;
icnt number;
begin
icnt:=0;
for m_c1 in c1 loop
update zxx_sms_step a set fee=
(select nvl(sum(pascharge),0) from ipasimport_200504 where caller=m_c1.caller and pastag in (1243,1251))
where caller=m_c1.caller and chargemonth=200504;
icnt:=icnt+1;
if icnt=500 then
exit;
end if;
end loop;
end;
q样的SQL语句Q徏议先update中的子查询生成一张中间表Q然后再update?br /> alter session set hash_area_size=400000000 ;
select /*+use_hash(a,b)*/ b.caller,nvl(sum(a.pascharge),0) from ipasimport_200504 a,zxx_sms_step b
where b.chargemonth=200504 and b.fee is null
and a.caller=b.caller and a.pastag in (1243,1251)
group by b.caller;
q样10分钟不到可产生中间表,然后再update只需几分钟即可?/p>
2Q?nbsp;部分表格未做l计信息分析
|通OApȝ自从oracle服务器从pc服务器上q到型Z后,其CPU利用率经常冲到很高。而其中每一个进E在某个瞬间占?0%左右的CPU。这些进E都是通过jdbc thin client q过来的?/p>
通过抓取其sql_textQ发C下两条SQL语句不正常?br />
1.
SQL> select D.flow_inid,D.step_inco,D.deal_man,D.agen_men,D.time_set,D.peri_man,
2 S2.fsub_set,S2.fsub_id,F.mtbl_stru,F.doc_name,F.svr_name
3 from deal_info D,step_inst S1,step_def S2,flow_inst F
4 where D.step_inco=S1.step_inco and S1.flow_id=S2.flow_id
5 and S1.step_code=S2.step_code and S1.flow_inid=F.flow_inid and D.step_type=5
6 and D.fsub_flag is not null and D.fsub_flag=1 and rownum<=1;
其执行计划和l计信息如下Q?/p>
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=22 Card=1 Bytes=1077)
1 0 COUNT (STOPKEY)
2 1 NESTED LOOPS (Cost=22 Card=1 Bytes=1077)
3 2 NESTED LOOPS (Cost=21 Card=1 Bytes=360)
4 3 NESTED LOOPS (Cost=20 Card=1 Bytes=150)
5 4 TABLE ACCESS (FULL) OF 'STEP_INST' (Cost=2 Card=9 Bytes=153)
6 4 TABLE ACCESS (BY INDEX ROWID) OF 'DEAL_INFO' (Cost=2 Card=1 Bytes=133)
7 6 INDEX (RANGE SCAN) OF 'DEAL_INFO_STEP_INCO' (NON-UNIQUE) (Cost=2
8 3 TABLE ACCESS (BY INDEX ROWID) OF 'FLOW_INST' (Cost=1 Card=1 Bytes=210)
9 8 INDEX (UNIQUE SCAN) OF 'PK_FLOW_INST' (UNIQUE)
10 2 TABLE ACCESS (BY INDEX ROWID) OF 'STEP_DEF' (Cost=1 Card=1 Bytes=717)
11 10 INDEX (UNIQUE SCAN) OF 'STEP_DEF_PK11119358638593' (UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
270626 consistent gets
273 physical reads
0 redo size
1079 bytes sent via SQL*Net to client
655 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
q条SQL语句执行的时间也不长Q就几秒钟,但是我们看到consistent gets很高?7万多Q这个操作就是消耗CPU的祸首。从执行计划来看Q其执行计划昄不可理,问题出在表格的连接顺序上面,应该是deal_info表格做ؓ驱动表先讉K?/p>
查这些表格的l计分析Q发现step_def表格未做分析Q对该表格做l计信息分析Qƈ对deal_info表做q囑ֈ析后Q?br /> analyze table deal_info compute statistics for all indexed columns;
其执行计划正是我们所惌的,同时consistent gets也只?00左右Q该操作所消耗的CPU也下降到?%?/p>
2.表格的柱状图信息没有分析Q?br />
SELECT SO.SO_NBR, so_type.name,STATUS.STS_WORDS, SO.REMARKS, SO.CHECK_TYPE,CTRL_ASGN.DISPATCHED_DATE,
CTRL_ASGN.PRE_ALARM_DATE, CTRL_ASGN.ALARM_DATE
from SO,SO_HANDLE, CTRL_ASGN,so_type,status
WHERE SO_HANDLE.SO_NBR=SO.SO_NBR AND SO.SO_NBR=CTRL_ASGN.SO_NBR
AND SO_HANDLE.HANDLE_TYPE_ID=1017
and so.so_type_id=so_type.so_type_id and so.PRIORITY=status.sts_id and status.table_name='SO'
AND STATUS.column_name ='PRIORITY' AND SO_HANDLE.WORK_AREA_ID= 300101
AND SO.STATE= 'B' AND SO.HALT ='N'
AND CTRL_ASGN.STATE = 'B'
AND CTRL_ASGN.STS = 'D';
该SQL语句执行旉?分钟左右?br />
执行计划如下Q?br />
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: RULE
1 0 NESTED LOOPS
2 1 NESTED LOOPS
3 2 NESTED LOOPS
4 3 NESTED LOOPS
5 4 TABLE ACCESS (BY INDEX ROWID) OF 'STATUS'
6 5 INDEX (RANGE SCAN) OF 'PK_STATUS' (UNIQUE)
7 4 TABLE ACCESS (BY INDEX ROWID) OF 'CTRL_ASGN'
8 7 INDEX (RANGE SCAN) OF 'CTRL_ASGN_0002'
9 3 TABLE ACCESS (BY INDEX ROWID) OF 'SO'
10 9 INDEX (UNIQUE SCAN) OF 'PK_SO' (UNIQUE)
11 2 TABLE ACCESS (BY INDEX ROWID) OF 'SO_TYPE'
12 11 INDEX (UNIQUE SCAN) OF 'PK_SO_TYPE' (UNIQUE)
13 1 TABLE ACCESS (BY INDEX ROWID) OF 'SO_HANDLE'
14 13 INDEX (RANGE SCAN) OF 'PK_SO_HANDLE' (UNIQUE)
我们攉表格信息和结果集的信?
SQL> select count(*) from CTRL_ASGN;
COUNT(*)
----------
1832469
SQL> select count(*) from status;
COUNT(*)
----------
1718
SQL> select count(*) from so;
COUNT(*)
----------
300296
SQL> select count(*) from so_type;
COUNT(*)
----------
265
SQL> select count(*) from so_handle;
COUNT(*)
----------
1296263
select count(*) from ctrl_asgn where CTRL_ASGN.STATE = 'B' AND CTRL_ASGN.STS = 'D';
COUNT(*)
----------
331490
select count(*) from so where SO.STATE= 'B' AND SO.HALT ='N';
COUNT(*)
----------
361
select count(*) from so_handle where SO_HANDLE.HANDLE_TYPE_ID=1017 and SO_HANDLE.WORK_AREA_ID= 300101;
COUNT(*)
----------
30086
通过对上面这些信息进行分析,我们可以发现q个问题也可以归lؓ表格之间的连接顺序上面。通过SO表做q囑ֈ析后Q该SQL语句只需1U钟卛_出来?br /> Analyze table so compute statistics for all indexed columns;
执行计划变成如下Q?br />
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=273 Card=32 Bytes=3936)
1 0 NESTED LOOPS (Cost=273 Card=32 Bytes=3936)
2 1 NESTED LOOPS (Cost=153 Card=30 Bytes=2730)
3 2 HASH JOIN (Cost=33 Card=30 Bytes=2130)
4 3 NESTED LOOPS (Cost=31 Card=30 Bytes=1620)
5 4 TABLE ACCESS (FULL) OF 'STATUS' (Cost=2 Card=1 Bytes=25)
6 4 TABLE ACCESS (BY INDEX ROWID) OF 'SO' (Cost=29 Card=59 Bytes=1711)
7 6 INDEX (RANGE SCAN) OF 'SO_0003' (NON-UNIQUE) (Cost=2 Card=59)
8 3 TABLE ACCESS (FULL) OF 'SO_TYPE' (Cost=1 Card=128 Bytes=2176)
9 2 TABLE ACCESS (BY INDEX ROWID) OF 'SO_HANDLE' (Cost=4 Card=280 Bytes=5600)
10 9 INDEX (RANGE SCAN) OF 'PK_SO_HANDLE' (UNIQUE) (Cost=3 Card=280)
11 1 TABLE ACCESS (BY INDEX ROWID) OF 'CTRL_ASGN' (Cost=4 Card=13620 Bytes=435840)
12 11 INDEX (RANGE SCAN) OF 'CTRL_ASGN_0003' (NON-UNIQUE) (Cost=2 Card=13620)
3Q?nbsp;Not exists的?br />
--停机保号用户?除欠?
select 'XJ'||1||'180','停机保号用户?,count(distinct serviceid),1,'200509',groupid from cbq_lch_usage0
where subsidiaryid=1 and subid<>'02' and subid<>'06' and status='7' and
serviceid not in (select serviceorderid from cbq_qf_usage1 where status<>'3' and status <> '8')
group by 'XJ'||1||'180','停机保号用户?,1,'200509',groupid ;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 SORT (GROUP BY)
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF 'CBQ_LCH_USAGE0'
4 2 TABLE ACCESS (FULL) OF 'CBQ_QF_USAGE1'
Elapsed: 13:48:26.85
调整Q?br />
not in Ҏnot exists
create index idx_serviceorderid on cbq_qf_usage1(serviceorderid) nologging;
select 'XJ'||1||'180','停机保号用户?,count(distinct serviceid),1,'200509',a.groupid
from cbq_lch_usage0 a
where a.subsidiaryid=1 and a.subid<>'02' and a.subid<>'06' and a.status='7'
and not exists(select 1 from cbq_qf_usage1 b where status<>'3' and status<>'8' and a.serviceid=b.serviceorderid)
group by 'XJ'||1||'180','停机保号用户?,1,'200509',a.groupid;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 SORT (GROUP BY)
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF 'CBQ_LCH_USAGE0'
4 2 TABLE ACCESS (BY INDEX) OF 'CBQ_QF_USAGE1'
5 4 INDEX (RANGE SCAN) OF 'IDX_SERVICEORDERID'
Elapsed: 00:00:01.36
九.其他
1QSELECT子句中避免?‘ * ‘
当你惛_SELECT子句中列出所有的COLUMN?使用动?SQL列引?‘*’ 是一个方便的Ҏ.不幸的是,q是一个非怽效的Ҏ. 实际?ORACLE在解析的q程? 会将’*’ 依次转换成所有的列名, q个工作是通过查询数据字典完成? q意味着耗费更多的时?
2Q用TRUNCATE替代DELETE
3Q用表的别?Alias)
当在SQL语句中连接多个表? 请用表的别名ƈ把别名前~于每个Column?q样一?可以减解析的旉q减那些由Column歧义引v的语法错?
4.索引的等U?br />
一般情늃引等U如下:
a) {式比较比范围比较要高?br />
b) 唯一性烦引比非唯一性烦引要高?br />
c) 一般情况下单列索引{要比复合索引高,但如果where子句中包含所 有复合烦引的字段Q则复合索引{高?br />
例如Q?br />
SELECT col1, ...
FROM emp
WHERE emp_name = 'GURRY'
AND emp_no = 127
AND dept_no = 12
Index1 (emp_name)
Index2 (emp_no, dept_no, emp_name)
ORACLE用烦引Index2?/p>
5.l计信息分析
在现实当中,有关analyze分析有以下两U误区:
a) 只要对主要的或者关键的表格做分析即可。其实正的应该是需要对所有涉及到的表格都做过分析?/p>
b) 做一ơ分析后卛_高枕无忧。事实上Q一旦做q分析后Q就应该定期更新q些l计信息Q以保证l计信息的正性?/p>
6QExistsLIn?br />
有许多h认ؓ用ExistsL用In要快,q也是一个误区。有时用in反而比用Exists快?br />
他们之间的区别如下:
IN subqueryQ首先执行subqueryQ由subquery来驱动父查询。而Exists子查询则q查询来驱动子查询。这是两者之间的区别?br />
所以如果子查询的话,则可以采用in会快一些,如果子查询大的话Q则采用exists会快一些?/p>
7Q?gt;?gt;=
大于或小于操作符一般情况下是不用调整的Q因为它有烦引就会采用烦引查找,但有的情况下可以对它q行优化Q如一个表?00万记录,一个数值型字段AQ?br />
30万记录的A=0Q?0万记录的A=1Q?9万记录的A=2Q?万记录的A=3?br />
那么执行A>2与A>=3的效果就有很大的区别了,因ؓA>2时ORACLE会先扑և
?的记录烦引再q行比较Q而A>=3时ORACLE则直接找?3的记录烦引?/p>
8. 使用索引来避免排?br />
索引是排好序的,在某些情况下可以使用索引来避免排序?br />
SELECT acc_name, acc_surname
FROM account acct
ORDER BY 1;
SELECT /*+ INDEX_ASC(acct acc_ndx1) */ acc_name,acc_surname
FROM account acct;
9.大对象操?br />
a)Big Insert
(1)direct insert(serial and parallel)
insert /*+append*/into tab1 select * from tab2;
Insert /*+append parallel(emp,8)*/ into emp select * from emp_bak;
(2)nologging
insert into tab1 nologging select * from tab2;
(3)Large extent size
更大的extent可以获得更好的insert性能?br />
(5)Large rollback segment
b)Large Index Create
大的索引extent size?br />
大的Sort_area_size?br />
采用nologging
采用parallel
大的临时表空?/p>
alter session sort_area_size=100000000;
create index xxx on aa(ab) nologging parallel 2;
c)Large Delete
分几ơdelete?/p>
附录一
Hint全集
174. /*+ALL_ROWS*/
表明对语句块选择Z开销的优化方?q获得最佛_吐量,使资源消耗最化.例如:
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';
175. /*+FIRST_ROWS*/
表明对语句块选择Z开销的优化方?q获得最佛_应时?使资源消耗最化.例如:
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';
176. /*+CHOOSE*/
表明如果数据字典中有讉K表的l计信息,基于开销的优化方?q获得最佳的吞吐?表明如果数据字典中没有访问表的统计信?基于规则开销的优化方?例如:
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';
177. /*+ RULE*/
表明对语句块选择Z规则的优化方?例如:
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)*/
提示明确表明Ҏ定表ҎROWIDq行讉K.例如:
SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>='AAAAAAAAAAAAAA'
AND EMP_NO='CCBZZP';
180. /*+CLUSTER(TABLE)*/
提示明确表明Ҏ定表选择扫描的讉KҎ,它只对簇对象有效.例如:
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)*/
/*+index(table ind_name) index(table ind_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*/
为指定表选择位图讉K路经,如果INDEX_COMBINE中没有提供作为参数的索引,选择Z囄引的布尔l合方式.例如:
SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS
WHERE SAL<5000000 AND HIREDATE
184. /*+INDEX_JOIN(TABLE INDEX_NAME)*/
提示明确命o优化器用烦引作问\?例如:
SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE
FROM BSEMPMS WHERE SAL<60000;
185. /*+INDEX_DESC(TABLE INDEX_NAME)*/
表明对表选择索引降序的扫描方?例如:
SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='CCBZZP';
186. /*+INDEX_FFS(TABLE INDEX_NAME)*/
Ҏ定的表执行快速全索引扫描,而不是全表扫描的办法.例如:
SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';
187. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/
提示明确q行执行规划的选择,几个单列烦引的扫描合v?例如:
SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO='CCBZZP' AND DPT_NO='TDC306';
188. /*+USE_CONCAT*/
Ҏ询中的WHERE后面的OR条gq行转换为UNION ALL的组合查?例如:
SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
189. /*+NO_EXPAND*/
对于WHERE后面的OR 或者IN-LIST的查询语?NO_EXPAND阻止其Z优化器对其进行扩?例如:
SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
190. /*+NOWRITE*/
止Ҏ询块的查询重写操?
191. /*+REWRITE*/
可以视图作为参?
192. /*+MERGE(TABLE)*/
能够对视囄各个查询q行相应的合q?例如:
SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) Va WHERE A.DPT_NO=V.DPT_NO
AND A.SAL>V.AVG_SAL;
193. /*+NO_MERGE(TABLE)*/
对于有可合ƈ的视图不再合q?例如:
SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO
AND A.SAL>V.AVG_SAL;
194. /*+ORDERED*/
Ҏ表出现在FROM中的序,ORDERED使ORACLE依此序对其q接.例如:
SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C
WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;
195. /*+USE_NL(TABLE)*/
指定表与嵌套的q接的行源进行连?q把指定表作为内部表.例如:
SELECT /*+ORDERED USE_NL(BSEMPMS)*/ BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
196. /*+USE_MERGE(TABLE)*/
指定的表与其他行源通过合ƈ排序q接方式q接h.例如:
SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE
BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
197. /*+USE_HASH(TABLE)*/
指定的表与其他行源通过哈希q接方式q接h.例如:
SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE
BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
198. /*+DRIVING_SITE(TABLE)*/
强制与ORACLE所选择的位|不同的表进行查询执?例如:
SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;
199. /*+LEADING(TABLE)*/
指定的表作接次序中的首?
200. /*+CACHE(TABLE)*/
当进行全表扫描时,CACHE提示能够表的检索块攄在缓冲区~存中最q最列表LRU的最q用端例如:
SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
201. /*+NOCACHE(TABLE)*/
当进行全表扫描时,CACHE提示能够表的检索块攄在缓冲区~存中最q最列表LRU的最q用端Q例?
SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
202. /*+APPEND*/
直接插入到表的最?可以提高速度.
insert /*+append*/ into test1 select * from test4 ;
203. /*+NOAPPEND*/
通过在插入语句生存期内停止ƈ行模式来启动常规插入.
insert /*+noappend*/ into test1 select * from test4;
附录?br />
STATSPACK包的使用指南
1.oracle8.1.6开始引qstatspackQstatspack是诊断oracle性能的强有力的工兗?br />
2.安装前准?br />
A.首先是系l参数的认Q?br />
job_query_processesQؓ了徏立自动Q务,执行数据攉Q该参数要大?
time_statisticsQؓ了收集操作系l计时信息等Q需要将其设|ؓTRUE
B.最好是单独的ؓperfstat用户Q即安装statspack要徏的用P单独建立数据表空间和临时表空_数据表空间至要?00M的空闲空_否则创徏statspack对象会失败,如果打算长期使用statspackQ可以考虑建稍大些的数据表I间?br />
3.安装
A.安装脚本
安装的脚本所在目录是$ORACLE_HOME/rdbms/adminQ在oracle8.1.6版本安装脚本是statscre.sqlQ之?8.1.7版本开始就是spcreate.sqlQ安装所需用户?i之前的需要internal或者拥有sysdba权限的用P9i需要的用户?sysQ?i已经不存在internal用户了)
执行安装脚本如下Q?br />
SQL> @$ORACLE_HOME/rdbms/admin/spcreate
B. 在安装过E中Q需要填写perfstat用户的密码,q且选择perfstat用户的数据表I间和时表I间Q安装完成之后,察看相应?lis文g查安装是否正无误,有问题可以通过spdrop.sql完成statspack的卸载,重新q行spcreate.sql完成statspack的安装?/p>
4. 试
最单的statspack报告生成Q运行两ơstatspack.snapQ然后运行spreport.sql生成一个基于两个时间点的报告。如果是8.1.7.3之前版本的OracleQ需要修改spcpkg.sqlQ要substr修改为substrbQ如下位|:
select l_snap_id
, p_dbid
, p_instance_number
, substr(sql_text,1,31) ? substrb(sql_text,1,31)
建立单的statspack报告q程如下Q?br />
SQL> execute statspack.snap (i_snap_level=>10)
PL/SQL procedure successfully completed.
SQL> execute statspack.snap
PL/SQL procedure successfully completed.
SQL> @$ORACLE_HOME/rdbms/admin/spreport
Spreport的执行过E中会列出需要选择的快照,你需要填写该报告描述的开始和l束的快照序Pq填写报告的文g名,当然可以不填Q用默认的报告文g名,默认的会生成在目?ORACLE_HOME/rdbms/admin?br />
q样可以验证statspack已经正确的安装完成了
自动攉statspack快照
正常在真正的环境下,我们是需要连l的采样一D|_q样生成的statspack才能更好的反映系l的现状Q我们是可以通过spauto.sql来自动收集数据的?br />
主要可能会设计到修改如下部分的内?br />
variable jobno number;
variable instno number;
begin
select instance_number into :instno from v$instance;
dbms_job.submit(:jobno, 'statspack.snap;', trunc(sysdate+1/24,'HH'), 'trunc(SYSDATE+1/24,''HH'')', TRUE, :instno);
commit;
end;
/
主要是修?/24q个|目前是一个小时自动收集一ơ数据,如果要改动ؓ半个时攉一ơ数据就修改?/48,同理Q进行或大或的修改?br />
执行后,可以在spauto.lis文g中看到当前自动收集数据的jobL信息。当惌生成statspack报告的时候,只要选择M两个不跨停机时间的快照序号可以了。注意,statspack是不能跨停机的?/p>