关于SQLSERVER数据库开发过程中的优化方案
取值范围
1~5
6
7
占总数据量的百分比
1%
98%
1%
而且,常用的查询中,查询DisposalCourseFlag<6?的情况既多又频繁,毫无疑问,如果能够建立索引,并且被应用,那么将大大提高这种情况的查询效率。因此,我们需要在该字段上建立索引。
第八?利用HINT强制指定索引在ORACLE优化器无法用上合理索引的情况下,利用HINT强制指定索引。
继续上面7的例子,ORACLE缺省认定,表中列的值是在所有数据行中均匀分布的,也就是说,在一百万数据量下,每种DisposalCourseFlag值各有12.5万数据行与之对应。假设SQL搜索条件DisposalCourseFlag=2,利用DisposalCourseFlag列上的索引进行数据搜索效率,往往不比全表扫描的高,ORACLE因此对索引“视而不见”,从而在查询路径的选择中,用其他字段上的索引甚至全表扫描。根据我们上面的分析,数据值的分布很特殊,严重的不均匀。为了利用索引提高效率,此时,一方面可以单独对该字段或该表用analyze语句进行分析,对该列搜集足够的统计数据,使ORACLE在查询选择性较高的值时能用上索引;另一方面,可以利用HINT提示,在SELECT关键字后面,加上“/*+ INDEX(表名称,索引名称)*/”的方式,强制ORACLE优化器用上该索引。
比如:?select * from?serviceinfo where?DisposalCourseFlag=1?;
上面的语句,实际执行中ORACLE用了全表扫描,加上蓝色提示部分后,用到索引查询。如下:
select?/*+?INDEX(SERVICEINFO,IX_S_DISPOSALCOURSEFLAG)?*/??*
from?serviceinfo where DisposalCourseFlag=1;
请注意,这种方法会加大代码维护的难度,而且该字段上索引的名称被改变之后,必须要同步所有指定索引的HINT代码,否则HINT提示将被ORACLE忽略掉。
第九?屏蔽无用索引继续上面8的例子,由于实际查询中,还有涉及到DisposalCourseFlag=6的查询,而此时如果用上该字段上的索引,将是非常不明智的,效率也极低。因此这种情况下,我们需要用特殊的方法屏蔽该索引,以便ORACLE选择其他字段上的索引。比如,如果字段为数值型的就在表达式的字段名后,添加“+ 0”,为字符型的就并上空串:“||""”
如:?select * from?serviceinfo where?DisposalCourseFlag+ 0 = 6?and workNo =?'36'?。
不过,不要把该用的索引屏蔽掉了,否则同样会产生低效率的全表扫描。
第十?分解复杂查询,用常量代替变量对于复杂的Where条件组合,Where中含有多个带索引的字段,考虑用IF语句分情况进行讨论;同时,去掉不必要的外来参数条件,减低复杂度,以便在不同情况下用不同字段上的索引。
继续上面9的例子,对于包含
Where (DisposalCourseFlag < v_DisPosalCourseFlag) or (v_DisPosalCourseFlag is null)?and ....的查询,(这里v_DisPosalCourseFlag为一个输入变量,取值范围可能为[NULL,0,1,2,3,4,5,6,7]),可以考虑分情况用IF语句进行讨论,类似:
IF v_DisPosalCourseFlag =1 THEN
Where DisposalCourseFlag = 1?and ....
ELSIF v_DisPosalCourseFlag =2 THEN
Where DisposalCourseFlag = 2?and ....?
。。。。。。
第十一?like子句尽量前端匹配因为like参数使用的非常频繁,因此如果能够对like子句使用索引,将很高的提高查询的效率。
例6:select * from city where?name like ‘%S%’
以上查询的执行计划用了全表扫描(TABLE ACCESS FULL),如果能够修改为:
select * from city where name like ‘S%’
那么查询的执行计划将会变成(INDEX RANGE SCAN),成功的利用了name字段的索引。这意味着Oracle SQL优化器会识别出用于索引的like子句,只要该查询的匹配端是具体值。因此我们在做like查询时,应该尽量使查询的匹配端是具体值,即使用like ‘S%’。
第十二?用Case语句合并多重扫描我们常常必须基于多组数据表计算不同的聚集。例如下例通过三个独立查询:
例8:1)select count(*) from emp where sal<1000;
???? 2)select count(*) from emp where sal between 1000 and 5000;
???? 3)select count(*) from emp where sal>5000;
这样我们需要进行三次全表查询,但是如果我们使用case语句:
select
count (sale when sal <1000
then 1 else null end)????????????? count_poor,
count (sale when between 1000 and 5000
then 1 else null end)????????????? count_blue_collar,
count (sale when sal >5000
then 1 else null end)????????????? count_poor
from emp;
这样查询的结果一样,但是执行计划只进行了一次全表查询。
第十三?使用nls_date_format例9:
select * from record where?to_char(ActionTime,'mm')='12'
这个查询的执行计划将是全表查询,如果我们改变nls_date_format,
SQL>alert session set nls_date_formate=’MM’;
现在重新修改上面的查询:
select * from record where?ActionTime='12'
这样就能使用actiontime上的索引了,它的执行计划将是(INDEX RANGE SCAN)。
第十四?使用基于函数的索引前面谈到任何对列的操作都可能导致全表扫描,例如:
select * from emp where substr(ename,1,2)=’SM’;
但是这种查询在客服系统又经常使用,我们可以创建一个带有substr函数的基于函数的索引,
create index emp_ename_substr on eemp (?substr(ename,1,2) );
?
这样在执行上面的查询语句时,这个基于函数的索引将排上用场,执行计划将是(INDEX RANGE SCAN)。
第十五?基于函数的索引要求等式匹配上面的例子中,我们创建了基于函数的索引,但是如果执行下面的查询:
select * from emp where substr(ename,1,1)=’S’
得到的执行计划将还是(TABLE ACCESS FULL),因为只有当数据列能够等式匹配时,基于函数的索引才能生效,这样对于这种索引的计划和维护的要求都很高。请注意,向表中添加索引是非常危险的操作,因为这将导致许多查询执行计划的变更。然而,如果我们使用基于函数的索引就不会产生这样的问题,因为Oracle只有在查询使用了匹配的内置函数时才会使用这种类型的索引。
第十六?使用分区索引在用分析命令对分区索引进行分析时,每一个分区的数据值的范围信息会放入Oracle的数据字典中。Oracle可以利用这个信息来提取出那些只与SQL查询相关的数据分区。
例如,假设你已经定义了一个分区索引,并且某个SQL语句需要在一个索引分区中进行一次索引扫描。Oracle会仅仅访问这个索引分区,而且会在这个分区上调用一个此索引范围的快速全扫描。因为不需要访问整个索引,所以提高了查询的速度。
第十七?使用位图索引位图索引可以从本质上提高使用了小于1000个唯一数据值的数据列的查询速度,因为在位图索引中进行的检索是在RAM中完成的,而且也总是比传统的B树索引的速度要快。对于那些少于1000个唯一数据值的数据列建立位图索引,可以使执行效率更快。
第十八?决定使用全表扫描还是使用索引和 所有的秘笈一样,最后一招都会又回到起点,最后我们来讨论一下是否需要建立索引,也许进行全表扫描更快。在大多数情况下,全表扫描可能会导致更多的物理磁 盘输入输出,但是全表扫描有时又可能会因为高度并行化的存在而执行的更快。如果查询的表完全没有顺序,那么一个要返回记录数小于10%的查询可能会读取表中大部分的数据块,这样使用索引会使查询效率提高很多。但是如果表非常有顺序,那么如果查询的记录数大于40%时,可能使用全表扫描更快。因此,有一个索引范围扫描的总体原则是:
1)对于原始排序的表?仅读取少于表记录数40%的查询应该使用索引范围扫描。反之,读取记录数目多于表记录数的40%的查询应该使用全表扫描。
2)对于未排序的表????仅读取少于表记录数7%的查询应该使用索引范围扫描。反之,读取记录数目多于表记录数的7%的查询应该使用全表扫描。
3???????总结以上的招式,是完全可以相互结合同时运用的。而且各种方法之间相互影响,紧密联系。这种联系既存在一致性,也可能带来冲突,当冲突发生时,需要根据实际情况进行选择,没有固定的模式。最后决定SQL优化功力的因素就是对ORACLE内功的掌握程度了。
另外,值得注意的是:随着时间的推移和数据的累计与变化,ORACLE对SQL语句的执行计划也会改变,比如:基于代价的优化方法,随着数据量的增大,优化器可能错误的不选择索引而采用全表扫描。这种情况可能是因为统计信息已经过时,在数据量变化很大后没有及时分析表;但如果对表进行分析之后,仍然没有用上合理的索引,那么就有必要对SQL语句用HINT提示,强制用合理的索引。但这种HINT提示也不能滥用,因为这种方法过于复杂,缺乏通用性和应变能力,同时也增加了维护上的代价;相对来说,基于函数右移、去掉“IN?,OR?,<>?,IS NOT NULL?”、分解复杂的SQL语句等等方法,却是“放之四海皆准”的,可以放心大胆的使用。
同时,优化也不是“一劳永逸”的,必须随着情况的改变进行相应的调整。当数据库设计发生变化,包括更改表结构:字段和索引的增加、删除或改名等;业务逻辑发生变化:如查询方式、取值范围发生改变等等。在这种情况下,也必须对原有的优化进行调整,以适应效率上的需求。
?
?
?
?
?
?在ASP开发之中如何避免对数据库操作的低效率,以下是在网上收集的一些方法,供大家参考。
方法一、尽量使用复杂的SQL来代替简单的一堆SQL.?
同样的事务,一个复杂的SQL完成的效率高于一堆简单SQL完成的效率。有多个查询时,要善于使用JOIN。?
oRs=oConn.Execute("Select * FROM Books")?
while not ors.Eof?
strSQL = "Select * FROM Authors Where AuthorID="&oRs("AuthorID") ors2=oConn.Execute(strSQL)?
Response.write ors("Title")&">>"&oRs2("Name")&"
&q uot;?
ors.MoveNext()?
wend?
要比下面的代码慢:?
strSQL="Select Books.Title,Authors.Name FROM Books JOIN Authors ON Authors.AuthorID=Books.AuthorID"?
ors=oConn.Execute(strSQL)?
while not ors.Eof?
Response.write ors("Title")&">>"&oRs("Name")&"
&qu ot;?
ors.MoveNext()?
wend?
方法二、尽量避免使用可更新Recordset?
oRs=oConn.Execute("Select * FROM Authors Where AuthorID=17",3,3)?
ors("Name")="DarkMan"?
ors.Update()?
要比下面的代码慢:?
strSQL = "Update Authors SET Name='DarkMan' Where AuthorID=17"?
oConn.Execute strSQL?
方法三、更新数据库时,尽量采用批处理更新?
将所有的SQL组成一个大的批处理SQL,并一次运行;这比一个一个地更新数据要有效率得多。这样也更加满足你进行事务处理 的需要:?
strSQL=""?
strSQL=strSQL&"SET XACT_ABORT ON\n";?
strSQL=strSQL&"BEGIN TRANSACTION\n";?
strSQL=strSQL&"Insert INTO orders(OrdID,CustID,OrdDat) VALUES('9999','1234',GETDATE())\n";?
strSQL=strSQL&"Insert INTO orderRows(OrdID,OrdRow,Item,Qty) VALUES('9999','01','G4385',5)\n";?
strSQL=strSQL&"Insert INTO orderRows(OrdID,OrdRow,Item,Qty) VALUES('9999','02','G4726',1)\n";?
strSQL=strSQL&"COMMIT TRANSACTION\n";?
strSQL=strSQL&"SET XACT_ABORT OFF\n";?
oConn.Execute(strSQL);?
其中,SET XACT_ABORT OFF 语句告诉SQL Server,如果下面的事务处理过程中,如果遇到错误,就取消已经完成的事务。?
方法四、数据库索引?
那些将在Where子句中出现的字段,你应该首先考虑建立索引;那些需要排序的字段,也应该在考虑之列 。?
在MS Access中建立索引的方法:在Access里面选择需要索引的表,点击“设计”,然后设置相应字段的索引.?
在MS SQL Server中建立索引的方法:在SQL Server管理器中,选择相应的表,然后“设计表”,点击右键,选择“Properties”,选择“indexes/keys”?
方法五、避免使Text字段太大?
当字符串的值大小不固定时,用varchar比用char的效果要好 些。我曾经看到一个例子程序,字段被定义为TEXT(255),但是他的取值经常只有20个字符。这个数据表有50k个记录,从而使这个数据库很大,大的数据库必然较慢。