sql语句性能提高
很久不来?最q忙于Oracle数据库大数据量的业务逻辑处理,整理一些零散的|上资料.目忙完后再l合自己l验,l出文章.x:
下面内容来自http://www.delphibbs.com/keylife/iblog_show.asp?xid=9256
1.合理使用索引
索引是数据库中重要的数据l构Q它的根本目的就是ؓ了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引l构。烦引的使用要恰到好处,其用原则如下:
●在l常q行q接Q但是没有指定ؓ外键的列上徏立烦引,而不l常q接的字D则׃化器自动生成索引?
●在频繁q行排序或分l(卌行group by或order by操作Q的列上建立索引?
●在条g表达式中l常用到的不同D多的列上建立索,在不同值少的列上不要徏立烦引。比如在雇员表的“性别”列上只有“?#8221;?#8220;?#8221;两个不同|因此无必要建立索引。如果徏立烦引不但不会提高查询效率,反而会严重降低更新速度?
●如果待排序的列有多个,可以在这些列上徏立复合烦引(compound indexQ?
●用系l工兗如Informix数据库有一个tbcheck工具Q可以在可疑的烦引上q行查。在一些数据库服务器上Q烦引可能失效或者因为频J操作而得读取效率降低,如果一个用烦引的查询不明不白地慢下来Q可以试着用tbcheck工具查烦引的完整性,必要时进行修复。另外,当数据库表更新大量数据后Q删除ƈ重徏索引可以提高查询速度?
(1)在下面两条select语句?
select * from table1 where field1<=10000 and field1>=0;
select * from table1 where field1>=0 and field1<=10000;
如果数据表中的数据field1?gt;=0,则第一条select语句要比W二条select语句效率高的多,因ؓW二条select语句的第一个条件耗费了大量的pȝ资源?br /> W一个原则:在where子句中应把最具限制性的条g攑֜最前面?br />
(2)在下面的select语句?
select * from tab where a=… and b=… and c=…;
若有索引index(a,b,c)Q则where子句中字D늚序应和索引中字D顺序一致?br /> W二个原则:where子句中字D늚序应和索引中字D顺序一致?br />
以下假设在field1上有唯一索引I1Q在field2上有非唯一索引I2?br /> (3) select field3,field4 from tb where field1='sdf' ?br /> select * from tb where field1='sdf' 慢,
因ؓ后者在索引扫描后要多一步ROWID表访问?br />
(4) select field3,field4 from tb where field1>='sdf' ?br /> select field3,field4 from tb where field1>'sdf' ?br /> 因ؓ前者可以迅速定位烦引?br />
(5) select field3,field4 from tb where field2 like 'R%' ?br /> select field3,field4 from tb where field2 like '%R' 慢,
因ؓ后者不使用索引?br />
(6) 使用函数如:
select field3,field4 from tb where upper(field2)='RMN'不用烦引?br /> 如果一个表有两万条记录Q徏议不使用函数Q如果一个表有五万条以上记录Q严格禁止用函敎ͼ两万条记录以下没有限制?br />
(7) Ig在烦引中存储Q所?br /> select field3,field4 from tb where field2 is[not] null不用烦引?br />
(8) 不等式如
select field3,field4 from tb where field2!='TOM'不用烦引?br /> 怼圎ͼ
select field3,field4 from tb where field2 not in('M','P')不用烦引?br />
(9) 多列索引Q只有当查询中烦引首列被用于条gӞ索引才能被用?br />
(10) MAXQMIN{函敎ͼ?br /> Select max(field2) from tb使用索引。所以,如果需要对字段取maxQminQsum{,应该加烦引?br /> 一ơ只使用一个聚集函敎ͼ如:
select “min”=min(field1), “max”=max(field1) from tb
不如Qselect “min”=(select min(field1) from tb) , “max”=(select max(field1) from tb)
(11) 重复D多的索引不会被查询优化器使用。而且因ؓZ索引Q修改该字段值时q要修改索引Q所以更新该字段的操作比没有索引更慢?br />
(12) 索引D大(如在一个char(40)的字D上建烦引)Q会造成大量的I/O开销Q甚至会过表扫描的I/O开销Q。因此,量使用整数索引?Sp_estspace可以计算表和索引的开销?br />
(13) 对于多列索引Qorder by的顺序必d索引的字D顺序一致?br />
(14) 在sybase中,如果order by的字D늻成一个簇索引Q那么无dorder by。记录的排列序是与烦引一致的?br />
(15) 多表联结Q具体查询方案需要通过试得到Q?br /> where子句中限定条件尽量用相兌的字D,且尽量把相关联的字段攑֜前面?br /> select a.field1,b.field2 from a,b where a.field3=b.field3
1. field3上没有烦引的情况?
对a作全表扫描,l果排序
对b作全表扫描,l果排序
l果合ƈ?br /> 对于很小的表或巨大的表比较合适?br />
2. field3上有索引
按照表联l的ơ序Qb为驱动表Qa驱动?br /> 对b作全表扫?br /> 对a作烦引范围扫?br /> 如果匚wQ通过a的rowid讉K
(16) 避免一对多的join。如Q?br /> select tb1.field3,tb1.field4,tb2.field2 from tb1,tb2 where tb1.field2=tb2.field2 and tb1.field2=‘BU1032’ and tb2.field2= ‘aaa’
不如Q?br /> declare @a varchar(80)
select @a=field2 from tb2 where field2=‘aaa’
select tb1.field3,tb1.field4,@a from tb1 where field2= ‘aaa’
(16) 子查?br /> 用exists/not exists代替in/not in操作
比较Q?br /> select a.field1 from a where a.field2 in(select b.field1 from b where b.field2=100)
select a.field1 from a where exists( select 1 from b where a.field2=b.field1 and b.field2=100)
select field1 from a where field1 not in( select field2 from b)
select field1 from a where not exists( select 1 from b where b.field2=a.field1)
(17) 丅R外键主要用于数据约束,sybase中创Z键时会自动创建烦引,外键与烦引无养I提高性能必须再徏索引?br />
(18) charcd的字D不建烦引比intcd的字D不建烦引更p糕。徏索引后性能只稍差一炏V?br />
(19) 使用count(*)而不要用count(column_name)Q避免用count(distinct column_name)?br />
(20) {号双量不要使用字段名,如:
select * from tb where field1 = field3
(21) 避免使用or条gQ因为or不用烦引?br />
2.避免使用order by和group by字句?br /> 因ؓ使用q两个子句会占用大量的时空?tempspace),如果一定要使用Q可用视图、h工生成时表的方法来代替?br /> 如果必须使用Q先查memory、tempdb的大?br /> 试证明Q特别要避免一个查询里既用join又用group byQ速度会非常慢Q?br />
3.量用子查询,特别是相兛_查询。因样会D效率下降?br /> 一个列的标{时在L询和where子句中的查询中出玎ͼ那么很可能当L询中的列值改变之后,子查询必重新查询一ơ。查询嵌套层ơ越多,效率低Q因此应当尽量避免子查询。如果子查询不可避免Q那么要在子查询中过滤掉可能多的行?
4Q消除对大型表行数据的顺序存?
在嵌套查询中Q对表的序存取Ҏ询效率可能生致命的影响。比如采用顺序存取策略,一个嵌?层的查询Q如果每层都查询1000行,那么q个查询p查询10亿行数据。避免这U情늚主要Ҏ是对连接的列进行烦引。例如,两个表:学生表(学号、姓名、年?#8230;…Q和选课表(学号、课E号、成l)。如果两个表要做q接Q就要在“学号”q个q接字段上徏立烦引?
q可以用ƈ集来避免序存取。尽在所有的查列上都有烦引,但某些Ş式的where子句优化器用顺序存取。下面的查询强q对orders表执行顺序操作:
SELECT Q?FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
虽然在customer_num和order_num上徏有烦引,但是在上面的语句中优化器q是使用序存取路径扫描整个表。因个语句要索的是分ȝ行的集合Q所以应该改为如下语句:
SELECT Q?FROM orders WHERE customer_num=104 AND order_num>1001
UNION
SELECT Q?FROM orders WHERE order_num=1008
q样p利用索引路径处理查询?
5Q避免困隄正规表达?
MATCHES和LIKE关键字支持通配W匹配,技术上叫正规表辑ּ。但q种匚w特别耗费旉。例如:SELECT Q?FROM customer WHERE zipcode LIKE “98_ _ _”
即在zipcode字段上徏立了索引Q在q种情况下也q是采用序扫描的方式。如果把语句改ؓSELECT Q?FROM customer WHERE zipcode >“98000”Q在执行查询时就会利用烦引来查询Q显然会大大提高速度?
另外Q还要避免非开始的子串。例如语句:SELECT Q?FROM customer WHERE zipcode[2Q?] >“80”Q在where子句中采用了非开始子Ԍ因而这个语句也不会使用索引?
6Q用时表加速查?
把表的一个子集进行排序ƈ创徏临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:
SELECT cust.nameQrcvbles.balanceQ?#8230;…other columns
FROM custQrcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果q个查询要被执行多次而不止一ơ,可以把所有未付款的客h出来攑֜一个时文件中Qƈ按客L名字q行排序Q?
SELECT cust.nameQrcvbles.balanceQ?#8230;…other columns
FROM custQrcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
INTO TEMP cust_with_balance
然后以下面的方式在时表中查询:
SELECT Q?FROM cust_with_balance
WHERE postcode>“98000”
临时表中的行要比主表中的行少Q而且物理序是所要求的顺序,减少了磁盘I/OQ所以查询工作量可以得到大幅减少?
注意Q时表创徏后不会反映主表的修改。在主表中数据频J修改的情况下,注意不要丢失数据?
7Q用排序来取代非序存取
非顺序磁盘存取是最慢的操作Q表现在盘存取臂的来回Ud。SQL语句隐藏了这一情况Q得我们在写应用程序时很容易写求存取大量非序늚查询?
有些时候,用数据库的排序能力来替代非顺序的存取能改q查询?
下面一文章比较详l?写得?
x:来自http://itexam.csai.cn/oracle/no0088.htm
通过分析SQL语句的执行计划优化SQL |
作者: 不详 来源Q?ChinaITLab http://www.csai.cn 2006q?3?8?/td> |
W??ORACLE的执行计?/strong> 背景知识Q? Z更好的进行下面的内容我们必须了解一些概忉|的术语Q? ׃nsql语句 Z不重复解析相同的SQL语句(因ؓ解析操作比较费资源,会导致性能下降)Q在W一ơ解析之后,ORACLESQL语句及解析后得到的执行计划存攑֜内存中。这块位于系l全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享。因此,当你执行一个SQL语句(有时被称Z个游?Ӟ如果该语句和之前的执行过的某一语句完全相同Qƈ且之前执行的该语句与其执行计划仍然在内存中存在,则ORACLE׃需要再q行分析Q直接得到该语句的执行\径。ORACLE的这个功能大大地提高了SQL的执行性能q大大节省了内存的用。用这个功能的关键是将执行q的语句可能放到内存中Q所以这要求有大的共享池(通过讄shared buffer pool参数?和尽可能的用绑定变量的Ҏ执行SQL语句? 当你向ORACLE 提交一个SQL语句QORACLE会首先在׃n内存中查找是否有相同的语句。这里需要注明的是,ORACLE对两者采取的是一U严格匹配,要达成共享,SQL语句必须完全相同(包括I格,换行{?? 下面是判断SQL语句是否与共享内存中某一SQL相同的步骤: 1) Ҏ发出语句的文本串q行hashed。如果hashg已在׃n池中SQL语句的hash值相同,则进行第2步; 2) 所发出语句的文本串Q包括大写、空白和注释Q与在第Q步中识别的所有已存在的SQL语句相比较? 例如Q? SELECT * FROM emp WHERE empno = 1000; 和下列每一个都不同 SELECT * from emp WHERE empno = 1000; SELECT * FROM EMP WHERE empno = 1000; SELECT * FROM emp WHERE empno = 2000; 在上面的语句中列值都是直接SQL语句中的Q今后我们将q类sql成ؓ编码SQL或字面值SQL 使用l定变量的SQL语句中必M用相同的名字的绑定变?bind variables) Q? 例如Q? a. ?个sql语句被认为相? select pin , name from people where pin = :blk1.pin; select pin , name from people where pin = :blk1.pin; b. ?个sql语句被认Z相同 select pin , name from people where pin = :blk1.ot_ind; select pin , name from people where pin = :blk1.ov_ind; 今后我们上面的q类语句UCؓl定变量SQL? 3) 所发出语句中涉及的对象与第Q步中识别的已存在语句所涉及对象相比较? 例如: 如用户user1与用户user2下都有EMP表,则用户user1发出的语句:SELECT * FROM EMP; ? 用户user2发出的语句:SELECT * FROM EMP; 被认为是不相同的语句Q因Z个语句中引用的EMP不是指同一个表? 4) 在SQL语句中用的捆绑变量的捆l类型必M致? 如果语句与当前在׃n池中的另一个语句是{同的话QOracleq不对它q行语法分析。而直接执行该语句Q提高了执行效率Q因法分析比较耗费资源? 注意的是Q从oracle 8i开始,新引入了一个CURSOR_SHARING参数Q该参数的主要目的就是ؓ了解军_~程q程中已大量使用的硬~码SQL问题。因为在实际开发中Q很多程序h员ؓ了提高开发速度Q而采用类g面的开发方法: str_sql string; int_empno int; int_empno = 2000; str_sql = ‘SELECT * FROM emp WHERE empno = ‘ + int_empno; ………… int_empno = 1000; str_sql = ‘SELECT * FROM emp WHERE empno = ‘ + int_empno; 上面的代码实际上使用了硬~码SQLQ我们不能使用׃nSQL的功能,l果是数据库效率不高。但是从上面?个语句来看,产生的硬~码SQL只是列g同,其它部分都是相同的,如果仅仅因ؓ列g同而导致这2个语句不能共享是很可惜的Qؓ了解册个问题,引入了CURSOR_SHARING参数Qɘq类问题也可以用共享SQLQ从而ɘq样的开发也可以利用׃nSQL功能。听h不错QORACLE真ؓ用户着惻I使用户在不改变代码的情况下还可以利用׃nSQL的功能。真的如此吗Q天上不会无~无故的掉一个馅饼的QORACLE对该参数的用做了说明,在经q实际测试后再改该参数的?~省情况下,该参数的gؓEXACTQ语句完全一致才使用׃nSQL)。因为有可能该变该值后Q你的硬~码SQL是可以用共享SQL了,但数据库的性能反而会下降?我在实际应用中已l遇到这U情c所以徏议编写需要稳定运行程序的开发h员最好还是一开始就使用l定变量的SQL? Rowid的概念: rowid是一个伪列,既然是伪列,那么q个列就不是用户定义Q而是pȝ自己l加上的。对每个表都有一个rowid的伪列,但是表中q不物理存储ROWID列的倹{不q你可以像用其它列那样使用它,但是不能删除改列Q也不能对该列的D行修攏V插入。一旦一行数据插入数据库Q则rowid在该行的生命周期内是唯一的,卛_使该行生行q移Q行的rowid也不会改变? Z么用ROWID rowid对访问一个表中的l定的行提供了最快的讉KҎQ通过ROWID可以直接定位到相应的数据块上Q然后将其读到内存。我们创Z个烦引时Q该索引不但存储索引列的|而且也存储烦引值所对应的行的ROWIDQ这h们通过索引快速找到相应行的ROWID后,通过该ROWIDQ就可以q速将数据查询出来。这也就是我们用烦引查询时Q速度比较快的原因? 在ORACLE8以前的版本中QROWID由FILE 、BLOCK、ROW NUMBER构成。随着oracle8中对象概늚扩展QROWID发生了变化,ROWID由OBJECT、FILE、BLOCK、ROW NUMBER构成。利用DBMS_ROWID可以rowid分解成上q的各部分,也可以将上述的各部分l成一个有效的rowid? Recursive SQL概念 有时Z执行用户发出的一个sql语句QOracle必须执行一些额外的语句Q我们将q些额外的语句称之ؓ'recursive calls'?recursive SQL statements'。如当一个DDL语句发出后,ORACLEL隐含的发Z些recursive SQL语句Q来修改数据字典信息Q以便用户可以成功的执行该DDL语句。当需要的数据字典信息没有在共享内存中Ӟl常会发生Recursive callsQ这些Recursive calls会将数据字典信息从硬盘读入内存中。用户不比关心这些recursive SQL语句的执行情况,在需要的时候,ORACLE会自动的在内部执行这些语句。当然DML语句与SELECT都可能引起recursive SQL。简单的_我们可以触发器视ؓrecursive SQL? Row Source(行源) 用在查询中,׃一操作q回的符合条件的行的集合Q即可以是表的全部行数据的集合;也可以是表的部分行数据的集合Q也可以为对?个row sourceq行q接操作(如joinq接)后得到的行数据集合? Predicate(谓词) 一个查询中的WHERE限制条g Driving Table(驱动? 该表又称为外层表(OUTER TABLE)。这个概는于嵌套与HASHq接中。如果该row sourceq回较多的行数据Q则Ҏ有的后箋操作有负面媄响。注意此处虽然翻译ؓ驱动表,但实际上译为驱动行?driving row source)更ؓ切。一般说来,是应用查询的限制条g后,q回较少行源的表作ؓ驱动表,所以如果一个大表在WHERE条g有有限制条g(如等值限?Q则该大表作为驱动表也是合适的Q所以ƈ不是只有较小的表可以作ؓ驱动表,正确说法应该为应用查询的限制条g后,q回较少行源的表作ؓ驱动表。在执行计划中,应该为靠上的那个row sourceQ后面会l出具体说明。在我们后面的描qCQ一般将该表UCؓq接操作的row source 1? Probed Table(被探查表) 该表又称为内层表(INNER TABLE)。在我们从驱动表中得到具体一行的数据后,在该表中LW合q接条g的行。所以该表应当ؓ大表(实际上应该ؓq回较大row source的表)且相应的列上应该有烦引。在我们后面的描qCQ一般将该表UCؓq接操作的row source 2? l合索引(concatenated index) 由多个列构成的烦引,如create index idx_emp on emp(col1, col2, col3, ……)Q则我们Uidx_emp索引为组合烦引。在l合索引中有一个重要的概念Q引导列(leading column)Q在上面的例子中Qcol1列ؓ引导列。当我们q行查询时可以?#8221;where col1 = ? ”Q也可以使用”where col1 = ? and col2 = ?”Q这L限制条g都会使用索引Q但?#8221;where col2 = ? ”查询׃会用该索引。所以限制条件中包含先导列时Q该限制条g才会使用该组合烦引? 可选择?selectivity)Q? 比较一下列中唯一键的数量和表中的行数Q就可以判断该列的可选择性。如果该列的”唯一键的数量/表中的行?#8221;的比D接近1Q则该列的可选择性越高,该列p适合创徏索引Q同L引的可选择性也高。在可选择性高的列上进行查询时Q返回的数据p,比较适合使用索引查询? 有了q些背景知识后就开始介l执行计划。ؓ了执行语句,Oracle可能必须实现许多步骤。这些步骤中的每一步可能是从数据库中物理检索数据行Q或者用某种Ҏ准备数据行,供发句的用户使用。Oracle用来执行语句的这些步骤的l合被称之ؓ执行计划。执行计划是SQL优化中最为复杂也是最为关键的部分Q只有知道了ORACLE在内部到底是如何执行该SQL语句后,我们才能知道优化器选择的执行计划是否ؓ最优的。执行计划对于DBA来说Q就象胦务报表对于胦务h员一样重要。所以我们面临的问题主要是:如何得到执行计划Q如何分析执行计划,从而找出媄响性能的主要问题。下面先从分析树型执行计划开始介l,然后介绍如何得到执行计划Q再介绍如何分析执行计划? 举例Q这个例子显C关于下面SQL语句的执行计划? SELECT ename, job, sal, dname FROM emp, dept WHERE emp.deptno = derpt.deptno AND NOT EXISTS ( SELECT * FROM salgrade WHERE emp.sal BETWEEN losal AND hisal ); 此语句查询薪水不在Q何徏议薪水范围内的所有雇员的名字Q工作,薪水和部门名?br /> 讉K路径(Ҏ) -- access path 优化器在形成执行计划旉要做的一个重要选择是如何从数据库查询出需要的数据。对于SQL语句存取的Q何表中的M行,可能存在许多存取路径(存取Ҏ)Q通过它们可以定位和查询出需要的数据。优化器选择其中自认为是最优化的\径? 在物理层Qoracled数据Q一ơ读取的最单位ؓ数据库块(由多个连l的操作pȝ块组?Q一ơ读取的最大值由操作pȝ一ơI/O的最大gmultiblock参数共同军_Q所以即使只需要一行数据,也是该行所在的数据库块d内存。逻辑上,oracle用如下存取方法访问数据: 1) 全表扫描QFull Table Scans, FTSQ? 为实现全表扫描,Oracled表中所有的行,q检查每一行是否满句的WHERE限制条g。Oracle序地读取分配给表的每个数据块,直到d表的最高水U处(high water mark, HWMQ标识表的最后一个数据块)。一个多块读操作可以使一ơI/O能读取多块数据块(db_block_multiblock_read_count参数讑֮)Q而不是只d一个数据块Q这极大的减了I/OL敎ͼ提高了系l的吞吐量,所以利用多块读的方法可以十分高效地实现全表扫描Q而且只有在全表扫描的情况下才能用多块读操作。在q种讉K模式下,每个数据块只被读一ơ。由于HWM标识最后一块被d的数据,而delete操作不媄响HWM|所以一个表的所有数据被delete后,其全表扫描的旉不会有改善,一般我们需要用truncate命o来HWM值归?。幸q的是oracle 10G后,可以人工收羃HWM的倹{? 由FTS模式d的数据被攑ֈ高速缓存的Least Recently Used (LRU)列表的尾部,q样可以使其快速交换出内存Q从而不使内存重要的数据被交换出内存。用FTS的前提条Ӟ在较大的表上不徏议用全表扫描,除非取出数据的比较多Q超q总量?% -- 10%Q或你想使用q行查询功能时? 使用全表扫描的例子: ~~~~~~~~~~~~~~~~~~~~~~~~ SQL> explain plan for select * from dual; Query Plan ----------------------------------------- SELECT STATEMENT [CHOOSE] Cost= TABLE ACCESS FULL DUAL 2) 通过ROWID的表存取QTable Access by ROWID或rowid lookupQ? 行的ROWID指出了该行所在的数据文g、数据块以及行在该块中的位置Q所以通过ROWID来存取数据可以快速定位到目标数据上,是Oracle存取单行数据的最快方法。ؓ了通过ROWID存取表,Oracle 首先要获取被选择行的ROWIDQ或者从语句的WHERE子句中得刎ͼ或者通过表的一个或多个索引的烦引扫描得到。Oracle然后以得到的ROWIDZ据定位每个被选择的行? q种存取Ҏ不会用到多块L作,一ơI/O只能d一个数据块。我们会l常在执行计划中看到该存取方法,如通过索引查询数据? 使用ROWID存取的方法: SQL> explain plan for select * from dept where rowid = 'AAAAyGAADAAAAATAAF'; Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID DEPT [ANALYZED] 3Q烦引扫描(Index Scan或index lookupQ? 我们先通过index查找到数据对应的rowid?对于非唯一索引可能q回多个rowid?Q然后根据rowid直接从表中得到具体的数据Q这U查找方式称为烦引扫描或索引查找(index lookup)。一个rowid唯一的表CZ行数据,该行对应的数据块是通过一ơi/o得到的,在此情况下该ơi/o只会d一个数据库块? 在烦引中Q除了存储每个烦引的值外Q烦引还存储h此值的行对应的ROWID倹{烦引扫描可以由2步组成: (1) 扫描索引得到对应的rowid倹{? (2) 通过扑ֈ的rowid从表中读出具体的数据。每步都是单独的一ơI/OQ但是对于烦引,׃l常使用Q绝大多数都已经CACHE到内存中Q所以第1步的I/Ol常是逻辑I/OQ即数据可以从内存中得到。但是对于第2步来_如果表比较大Q则其数据不可能全在内存中,所以其I/O很有可能是物理I/OQ这是一个机械操作,相对逻辑I/O来说Q是极其Ҏ间的。所以如果多大表q行索引扫描Q取出的数据如果大于总量?% -- 10%Q用烦引扫描会效率下降很多? 如下列所C: SQL> explain plan for select empno, ename from emp where empno=10; Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX UNIQUE SCAN EMP_I1 注意TABLE ACCESS BY ROWID EMP部分Q这表明q不是通过FTS存取路径讉K数据Q而是通过rowid lookup存取路径讉K数据的。在此例中,所需要的rowid是由于在索引查找empno列的值得到的Q这U方式是INDEX UNIQUE SCAN查找Q后面给予介l,EMP_I1Z用的q行索引查找的烦引名字? 但是如果查询的数据能全在索引中找刎ͼ可以避免进行第2步操作,避免了不必要的I/OQ此时即佉K过索引扫描取出的数据比较多Q效率还是很高的Q因只会在烦引中d。所以上面我在介l基于规则的优化器时Q用了select count(id) from SWD_BILLDETAIL where cn <'6'Q而没有用select count(cn) from SWD_BILLDETAIL where cn <'6'。因为在实际情况中,只查询被索引列的值的情况极ؓ,所以,如果我在查询中用count(cn)Q则不具有代表性? SQL> explain plan for select empno from emp where empno=10; -- 只查询empno列? Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 INDEX UNIQUE SCAN EMP_I1 q一步讲Q如果sql语句中对索引列进行排序,因ؓ索引已经预先排序好了Q所以在执行计划中不需要再对烦引列q行排序 SQL> explain plan for select empno, ename from emp where empno > 7876 order by empno; Query Plan -------------------------------------------------------------------------------- SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX RANGE SCAN EMP_I1 [ANALYZED] 从这个例子中可以看到Q因为烦引是已经排序了的Q所以将按照索引的顺序查询出W合条g的行Q因此避免了q一步排序操作? Ҏ索引的类型与where限制条g的不同,?U类型的索引扫描Q? 索引唯一扫描(index unique scan) 索引范围扫描(index range scan) 索引全扫?index full scan) 索引快速扫?index fast full scan) (1) 索引唯一扫描(index unique scan) 通过唯一索引查找一个数值经常返回单个ROWID。如果该唯一索引有多个列l成(即组合烦?Q则臛_要有l合索引的引导列参与到该查询中,如创Z个烦引:create index idx_test on emp(ename, deptno, loc)。则select ename from emp where ename = ‘JACK' and deptno = ‘DEV'语句可以使用该烦引。如果该语句只返回一行,则存取方法称为烦引唯一扫描。而select ename from emp where deptno = ‘DEV'语句则不会用该索引Q因为where子句U没有引导列。如果存在UNIQUE 或PRIMARY KEY U束Q它保证了语句只存取单行Q的话,Oraclel常实现唯一性扫描? 使用唯一性约束的例子Q? SQL> explain plan for select empno,ename from emp where empno=10; Query Plan ------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX UNIQUE SCAN EMP_I1 (2) 索引范围扫描(index range scan) 使用一个烦引存取多行数据,同上面一P如果索引是组合烦引,?1)所C,而且select ename from emp where ename = ‘JACK' and deptno = ‘DEV'语句q回多行数据Q虽然该语句q是使用该组合烦引进行查询,可此时的存取ҎUCؓ索引范围扫描。在唯一索引上用烦引范围扫描的典型情况下是在谓?where限制条g)中用了范围操作W??gt;?lt;?lt;>?gt;=?lt;=、between) 使用索引范围扫描的例子: SQL> explain plan for select empno,ename from emp where empno > 7876 order by empno; Query Plan -------------------------------------------------------------------------------- SELECT STATEMENT [CHOOSE] Cost=1 TABLE ACCESS BY ROWID EMP [ANALYZED] INDEX RANGE SCAN EMP_I1 [ANALYZED] 在非唯一索引上,谓词col = 5可能q回多行数据Q所以在非唯一索引上都使用索引范围扫描? 使用index rang scan?U情况: (a) 在唯一索引列上使用了range操作W?> < <> >= <= between) (b) 在组合烦引上Q只使用部分列进行查询,D查询出多? (c) 寚w唯一索引列上q行的Q何查询? (3) 索引全扫?index full scan) 与全表扫描对应,也有相应的全索引扫描。在某些情况下,可能q行全烦引扫描而不是范围扫描,需要注意的是全索引扫描只在CBO模式下才有效。CBOҎl计数值得知进行全索引扫描比进行全表扫描更有效Ӟ才进行全索引扫描Q而且此时查询出的数据都必M索引中可以直接得到? 全烦引扫描的例子Q? An Index full scan will not perform single block i/o's and so it may prove to be inefficient. e.g. Index BE_IX is a concatenated index on big_emp (empno, ename) SQL> explain plan for select empno, ename from big_emp order by empno,ename; Query Plan -------------------------------------------------------------------------------- SELECT STATEMENT [CHOOSE] Cost=26 INDEX FULL SCAN BE_IX [ANALYZED] (4) 索引快速扫?index fast full scan) 扫描索引中的所有的数据块,?index full scan很类|但是一个显著的区别是它不Ҏ询出的数据进行排序,x据不是以排序序被返回。在q种存取Ҏ中,可以使用多块d能,也可以用ƈ行读入,以便获得最大吞吐量与羃短执行时间? 索引快速扫描的例子Q? BE_IX索引是一个多列烦引:big_emp (empno,ename) SQL> explain plan for select empno,ename from big_emp; Query Plan ------------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 INDEX FAST FULL SCAN BE_IX [ANALYZED] 只选择多列索引的第2列: SQL> explain plan for select ename from big_emp; Query Plan ------------------------------------------ SELECT STATEMENT [CHOOSE] Cost=1 INDEX FAST FULL SCAN BE_IX [ANALYZED] 表之间的q接 Join是一U试囑ְ两个表结合在一L谓词Q一ơ只能连?个表Q表q接也可以被UCؓ表关联。在后面的叙qCQ我们将会?#8221;row source”来代?#8221;?#8221;Q因Z用row source更严谨一些,q且参与连接的2个row source分别UCؓrow source1和row source 2。Joinq程的各个步骤经常是串行操作Q即使相关的row source可以被ƈ行访问,卛_以ƈ行的d做joinq接的两个row source的数据,但是在将表中W合限制条g的数据读入到内存形成row source后,join的其它步骤一般是串行的。有多种Ҏ可以?个表q接hQ当然每U方法都有自q优缺点,每种q接cd只有在特定的条g下才会发挥出其最大优ѝ? row source(?之间的连接顺序对于查询的效率有非常大的媄响。通过首先存取特定的表Q即该表作为驱动表Q这样可以先应用某些限制条gQ从而得C个较的row sourceQɘq接的效率较高,q也是我们常说的要先执行限制条件的原因。一般是在将表读入内存时Q应用where子句中对该表的限制条件? Ҏ2个row source的连接条件的中操作符的不同,可以连接分为等D?如WHERE A.COL3 = B.COL4)、非{D?WHERE A.COL3 > B.COL4)、外q接(WHERE A.COL3 = B.COL4(+))。上面的各个q接的连接原理都基本一P所以ؓ了简单期_下面以等D接ؓ例进行介l。在后面的介l中Q都Ԍ SELECT A.COL1, B.COL2 FROM A, B WHERE A.COL3 = B.COL4; Zq行说明Q假设A表ؓRow Soruce1Q则其对应的q接操作兌列ؓCOL 3QB表ؓRow Soruce2Q则其对应的q接操作兌列ؓCOL 4Q? q接cdQ? 目前为止Q无接操作符如何Q典型的q接cd共有3U: 排序 - - 合ƈq接(Sort Merge Join (SMJ) ) 嵌套循环(Nested Loops (NL) ) 哈希q接(Hash Join) 排序 - - 合ƈq接(Sort Merge Join, SMJ) 内部q接q程Q? 1) 首先生成row source1需要的数据Q然后对q些数据按照q接操作兌?如A.col3)q行排序? 2) 随后生成row source2需要的数据Q然后对q些数据按照与sort source1对应的连接操作关联列(如B.col4)q行排序? 3) 最后两边已排序的行被放在一h行合q操作,卛_2个row source按照q接条gq接h 下面是连接步骤的囑Ş表示Q? MERGE / \ SORT SORT | | Row Source 1 Row Source 2 如果row source已经在连接关联列上被排序Q则该连接操作就不需要再q行sort操作Q这样可以大大提高这U连接操作的q接速度Q因为排序是个极其费资源的操作,特别是对于较大的表?预先排序的row source包括已经被烦引的?如a.col3或b.col4上有索引)或row source已经在前面的步骤中被排序了。尽合q两个row source的过E是串行的,但是可以q行讉Kq两个row source(如ƈ行读入数据,q行排序). SMJq接的例子: SQL> explain plan for select /*+ ordered */ e.deptno, d.deptno from emp e, dept d where e.deptno = d.deptno order by e.deptno, d.deptno; Query Plan ------------------------------------- SELECT STATEMENT [CHOOSE] Cost=17 MERGE JOIN SORT JOIN TABLE ACCESS FULL EMP [ANALYZED] SORT JOIN TABLE ACCESS FULL DEPT [ANALYZED] 排序是一个费时、费资源的操作,特别对于大表。基于这个原因,SMJl常不是一个特别有效的q接ҎQ但是如?个row source都已l预先排序,则这U连接方法的效率也是蛮高的? 嵌套循环(Nested Loops, NL) q个q接Ҏ有驱动表(外部?的概c其实,该连接过E就是一?层嵌套@环,所以外层@环的ơ数少好Q这也就是我们ؓ什么将表或返回较row source的表作ؓ驱动?用于外层循环)的理Z据。但是这个理论只是一般指导原则,因ؓ遵@q个理论q不能M证语句产生的I/Oơ数最。有时不遵守q个理论依据Q反而会获得更好的效率。如果用这U方法,军_使用哪个表作为驱动表很重要。有时如果驱动表选择不正,会D语句的性能很差、很差? 内部q接q程Q? Row source1的Row 1 -------------- -- Probe -> Row source 2 Row source1的Row 2 -------------- -- Probe -> Row source 2 Row source1的Row 3 -------------- -- Probe -> Row source 2 ……. Row source1的Row n -------------- -- Probe -> Row source 2 从内部连接过E来看,需要用row source1中的每一行,d配row source2中的所有行Q所以此时保持row source1可能的与高效的访问row source2(一般通过索引实现)是媄响这个连接效率的关键问题。这只是理论指导原则Q目的是使整个连接操作生最的物理I/Oơ数Q而且如果遵守q个原则Q一般也会ȝ物理I/O数最。但是如果不遵从q个指导原则Q反而能用更的物理I/O实现q接操作Q那管q反指导原则吧!因ؓ最的物理I/Oơ数才是我们应该遵从的真正的指导原? 在上面的q接q程中,我们URow source1为驱动表或外部表。Row Source2被称探查表或内部表? 在NESTED LOOPSq接中,Oracledrow source1中的每一行,然后在row sourc2中检查是否有匚w的行Q所有被匚w的行都被攑ֈl果集中Q然后处理row source1中的下一行。这个过E一直l,直到row source1中的所有行都被处理。这是从q接操作中可以得到第一个匹配行的最快的Ҏ之一Q这U类型的q接可以用在需要快速响应的语句中,以响应速度Z要目标? 如果driving row source(外部?比较,q且在inner row source(内部?上有唯一索引Q或有高选择性非唯一索引Ӟ使用q种Ҏ可以得到较好的效率。NESTED LOOPS有其它连接方法没有的的一个优ҎQ可以先q回已经q接的行Q而不必等待所有的q接操作处理完才q回数据Q这可以实现快速的响应旉? 如果不用ƈ行操作,最好的驱动表是那些应用了where 限制条g后,可以q回较少行数据的的表Q所以大表也可能UCؓ驱动表,关键看限制条件。对于ƈ行查询,我们l常选择大表作ؓ驱动表,因ؓ大表可以充分利用q行功能。当Ӟ有时Ҏ询用ƈ行操作ƈ不一定会比查询不使用q行操作效率高,因ؓ最后可能每个表只有很少的行W合限制条gQ而且q要看你的硬仉|是否可以支持ƈ?如是否有多个CPUQ多个硬盘控制器)Q所以要具体问题具体对待? NLq接的例子: SQL> explain plan for select a.dname,b.sql from dept a,emp b where a.deptno = b.deptno; Query Plan ------------------------- SELECT STATEMENT [CHOOSE] Cost=5 NESTED LOOPS TABLE ACCESS FULL DEPT [ANALYZED] TABLE ACCESS FULL EMP [ANALYZED] 哈希q接(Hash Join, HJ) q种q接是在oracle 7.3以后引入的,从理Z来说比NL与SMJ更高效,而且只用在CBO优化器中。较的row source被用来构建hash table与bitmapQ第2个row source被用来被hansedQƈ与第一个row source生成的hash tableq行匚wQ以便进行进一步的q接。Bitmap被用来作ZU比较快的查找方法,来检查在hash table中是否有匚w的行。特别的Q当hash table比较大而不能全部容U_内存中时Q这U查找方法更为有用。这U连接方法也有NLq接中所谓的驱动表的概念Q被构徏为hash table与bitmap的表为驱动表Q当被构建的hash table与bitmap能被容纳在内存中Ӟq种q接方式的效率极高? HASHq接的例子: SQL> explain plan for select /*+ use_hash(emp) */ empno from emp, dept where emp.deptno = dept.deptno; Query Plan ---------------------------- SELECT STATEMENT [CHOOSE] Cost=3 HASH JOIN TABLE ACCESS FULL DEPT TABLE ACCESS FULL EMP 要哈希q接有效Q需要设|HASH_JOIN_ENABLED=TRUEQ缺省情况下该参CؓTRUEQ另外,不要忘了q要讄hash_area_size参数Q以使哈希连接高效运行,因ؓ哈希q接会在该参数指定大的内存中运行,q小的参C使哈希连接的性能比其他连接方式还要低? ȝ一下,在哪U情况下用哪U连接方法比较好Q? 排序 - - 合ƈq接(Sort Merge Join, SMJ)Q? a) 对于非等D接,q种q接方式的效率是比较高的? b) 如果在关联的列上都有索引Q效果更好? c) 对于?个较大的row source做连接,该连接方法比NLq接要好一些? d) 但是如果sort mergeq回的row sourceq大Q则又会D使用q多的rowid在表中查询数据时Q数据库性能下降Q因多的I/O? 嵌套循环(Nested Loops, NL)Q? a) 如果driving row source(外部?比较,q且在inner row source(内部?上有唯一索引Q或有高选择性非唯一索引Ӟ使用q种Ҏ可以得到较好的效率? b) NESTED LOOPS有其它连接方法没有的的一个优ҎQ可以先q回已经q接的行Q而不必等待所有的q接操作处理完才q回数据Q这可以实现快速的响应旉? 哈希q接(Hash Join, HJ)Q? a) q种Ҏ是在oracle7后来引入的,使用了比较先q的q接理论Q一般来_其效率应该好于其?U连接,但是q种q接只能用在CBO优化器中Q而且需要设|合适的hash_area_size参数Q才能取得较好的性能? b) ?个较大的row source之间q接时会取得相对较好的效率,在一个row source较小时则能取得更好的效率? c) 只能用于{D接中 W卡儿乘U?Cartesian Product) 当两个row source做连接,但是它们之间没有兌条gӞ׃在两个row source中做W卡儿乘U,q通常q写代码疏漏造成(即程序员忘了写关联条?。笛卡尔乘积是一个表的每一行依ơ与另一个表中的所有行匚w。在Ҏ情况下我们可以用笛卡儿乘积Q如在星形连接中Q除此之外,我们要尽量用笛卡儿乘积Q否则,自己想结果是什么吧Q? 注意在下面的语句中,?个表之间没有q接? SQL> explain plan for select emp.deptno,dept,deptno from emp,dept Query Plan ------------------------------ SLECT STATEMENT [CHOOSE] Cost=5 MERGE JOIN CARTESIAN TABLE ACCESS FULL DEPT SORT JOIN TABLE ACCESS FULL EMP CARTESIAN关键字指Z?个表之间做笛卡尔乘积。假如表emp有n行,dept表有m行,W卡乘U的l果是得到n * m行结果? |
posted on 2006-04-29 11:06 青苹?/a> 阅读(2983) 评论(3) ~辑 收藏