论文部分内容阅读
[摘 要] 如何提高数据库的效率,是数据库使用过程中的一项重要任务,本文以oracle9i数据库为例,着重从改善用户的查询计划这方面,说明了改善查询技术的重要性,以及归纳出几种改善用户查询优化的方法。
[关键词] 数据库 查询优化 查询计划 oracle 9i
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、政府等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个积累到上千万条记录的商业数据库表,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,据统计,90%的性能问题是由于程序员或用户使用了不恰当的查询语句造成的,而不良的SQL语句往往来自于不恰当的索引设计、不充分的条件和不可优化的where子句。在进行恰当的优化后,运行速度就会明显的提高。因此,如何设计高效合理的查询语句就显得非常重要。本文以Oracle9i数据库应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
一、据统计90%的性能问题是由于程序员或用户使用了不恰当的查询语句造
许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,由于在项目时间紧迫等诸多方面的压力之下往往只关注所得的结果是否正确,以及用户的界面是否华丽并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,其实这是错误的。一个好的查询计划往往可以使程序性能提高数十倍甚至更多。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。
二、优化查询计划的方法
系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。下面以关系数据库系统Oracle9i为例,介绍改善用户查询计划的方法。
1.避免使用不兼容的数据类型。
如float和int、char和varchar是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。例如:
SELECT name FROM employee WHERE salary>10000
在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为10000是个整型数。我们应当在编程时将整型转化成为money型,而不要等到运行时再转化。
2.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
● 在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
● 在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
● 在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
● 如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
3.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
● 索引中不包括一个或几个待排序的列;
● group by或order by子句中列的次序与索引的次序不一样;
● 排序的列来自不同的表。为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
4.用外联接提高表连接的查询速度
在作表连接(常用于视图)时,常使用以下方法来查询数据:
SELECT PAY_NO,PROJECT_NAME
FROM A
WHERE A.PAY_NO NOT IN (SELECT PAY_
NO FROM B WHERE VALUE >=120000);
但是若表A有10000条记录,表B有10000条记录,则要用掉30分钟才能查完,主要因为NOT IN要进行一条一条的比较,共需要10000*10000次比较后,才能得到结果。该用外联接后,可以缩短到1分左右的时间:
SELECT PAY_NO,PROJECT_NAME
FROM A,B
WHERE A.PAY_NO=B.PAY_NO(+)
AND B.PAY_NO IS NULL
AND B.VALUE >=12000;
5.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:SELECT cust.name,rcvbles.balance,……other columns FROM cust,rcvbles WHERE cust.customer_id=rcvlbes.customer_id AND rcvblls.balance>0 AND cust.postcode>“98000”ORDER BY cust.name如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:SELECT cust.name,rcvbles.balance,……other columns FROM cust,rcvbles WHERE cust.customer_id=rcvlbes.customer_id AND rcvblls.balance>0 ORDER BY cust.name INTO TEMP cust_with_balance 然后以下面的方式在临时表中查询:SELECT*FROM cust_with_balance WHERE postcode>“98000”临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
7.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
8.用排序来取代非顺序存取
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
三、小结
数据库的查询在数据库的操作中占了相当大的比重,提高数据库的效率,主要在于提高数据库的查询。通过以上对数据库查询优化的探讨,我们知道对于数据库的查询优化重点就在于SQL语句的执行效率,因此抓住了这个重点,有针对性得进行进一步的实践,反复比较就可以得出最优的方案。
参 考 文 献
[1] 科里M.J.著,张福德译Oracle数据库性能优化技术,学苑出版社,1994
[2] 黄开枝,Oracle9i数据库性能调整与优化,清华大学出版社,2005年6月
[3] 唐汉明,翟振兴,兰丽华,关宝军,申宝柱,人民邮电出版社,2008年4月
[4] 李俊民,精通SQL—结构化查询语言详解(第2版),人民邮电出版社,2008年8月■
[关键词] 数据库 查询优化 查询计划 oracle 9i
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、政府等部门最为重要的计算机应用之一。从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。举例来说,如果数据的量积累到一定的程度,比如一个积累到上千万条记录的商业数据库表,全表扫描一次往往需要数十分钟,甚至数小时。如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,据统计,90%的性能问题是由于程序员或用户使用了不恰当的查询语句造成的,而不良的SQL语句往往来自于不恰当的索引设计、不充分的条件和不可优化的where子句。在进行恰当的优化后,运行速度就会明显的提高。因此,如何设计高效合理的查询语句就显得非常重要。本文以Oracle9i数据库应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
一、据统计90%的性能问题是由于程序员或用户使用了不恰当的查询语句造
许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,由于在项目时间紧迫等诸多方面的压力之下往往只关注所得的结果是否正确,以及用户的界面是否华丽并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。因此,如何设计高效合理的查询语句就显得非常重要。
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,其实这是错误的。一个好的查询计划往往可以使程序性能提高数十倍甚至更多。查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。DBMS处理查询计划的过程是这样的:在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。
二、优化查询计划的方法
系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。下面以关系数据库系统Oracle9i为例,介绍改善用户查询计划的方法。
1.避免使用不兼容的数据类型。
如float和int、char和varchar是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。例如:
SELECT name FROM employee WHERE salary>10000
在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为10000是个整型数。我们应当在编程时将整型转化成为money型,而不要等到运行时再转化。
2.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:
● 在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
● 在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
● 在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
● 如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
3.避免或简化排序
应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:
● 索引中不包括一个或几个待排序的列;
● group by或order by子句中列的次序与索引的次序不一样;
● 排序的列来自不同的表。为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
4.用外联接提高表连接的查询速度
在作表连接(常用于视图)时,常使用以下方法来查询数据:
SELECT PAY_NO,PROJECT_NAME
FROM A
WHERE A.PAY_NO NOT IN (SELECT PAY_
NO FROM B WHERE VALUE >=120000);
但是若表A有10000条记录,表B有10000条记录,则要用掉30分钟才能查完,主要因为NOT IN要进行一条一条的比较,共需要10000*10000次比较后,才能得到结果。该用外联接后,可以缩短到1分左右的时间:
SELECT PAY_NO,PROJECT_NAME
FROM A,B
WHERE A.PAY_NO=B.PAY_NO(+)
AND B.PAY_NO IS NULL
AND B.VALUE >=12000;
5.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:SELECT cust.name,rcvbles.balance,……other columns FROM cust,rcvbles WHERE cust.customer_id=rcvlbes.customer_id AND rcvblls.balance>0 AND cust.postcode>“98000”ORDER BY cust.name如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:SELECT cust.name,rcvbles.balance,……other columns FROM cust,rcvbles WHERE cust.customer_id=rcvlbes.customer_id AND rcvblls.balance>0 ORDER BY cust.name INTO TEMP cust_with_balance 然后以下面的方式在临时表中查询:SELECT*FROM cust_with_balance WHERE postcode>“98000”临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。
7.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
8.用排序来取代非顺序存取
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
三、小结
数据库的查询在数据库的操作中占了相当大的比重,提高数据库的效率,主要在于提高数据库的查询。通过以上对数据库查询优化的探讨,我们知道对于数据库的查询优化重点就在于SQL语句的执行效率,因此抓住了这个重点,有针对性得进行进一步的实践,反复比较就可以得出最优的方案。
参 考 文 献
[1] 科里M.J.著,张福德译Oracle数据库性能优化技术,学苑出版社,1994
[2] 黄开枝,Oracle9i数据库性能调整与优化,清华大学出版社,2005年6月
[3] 唐汉明,翟振兴,兰丽华,关宝军,申宝柱,人民邮电出版社,2008年4月
[4] 李俊民,精通SQL—结构化查询语言详解(第2版),人民邮电出版社,2008年8月■