SQL语言的查询效率.docx
- 文档编号:5390462
- 上传时间:2023-05-08
- 格式:DOCX
- 页数:26
- 大小:34.55KB
SQL语言的查询效率.docx
《SQL语言的查询效率.docx》由会员分享,可在线阅读,更多相关《SQL语言的查询效率.docx(26页珍藏版)》请在冰点文库上搜索。
SQL语言的查询效率
如何提高SQL语言的查询效率
由于SQL是面向结果而不是面向过程的查询语言,所以一般支持SQL语言的大型关系型数据库都使用一个基于查询成本的优化器,为即时查询提供一个最佳的执行策略。
对于优化器,输入是一条查询语句,输出是一个执行策略。
一条SQL查询语句可以有多种执行策略,优化器将估计出全部执行方法中所需时间最少的所谓成本最低的那一种方法。
所有优化都是基于用记所使用的查询语句中的where子句,优化器对where子句中的优化主要用搜索参数(SerachArgument)。
搜索参数的核心思想就是数据库使用表中字段的索引来查询数据,而不必直接查询记录中的数据。
带有=、<、<=、>、>=等操作符的条件语句可以直接使用索引,如下列是搜索参数:
emp_id="10001"或salary>3000或a=1andc=7
而下列则不是搜索参数:
salary=emp_salary或dep_id!
=10或salary*12>=3000或a=1orc=7
应当尽可能提供一些冗余的搜索参数,使优化器有更多的选择余地。
请看以下3种方法:
第一种方法:
selectemployee.emp_name,department.dep_namefromdepartment,employeewhere(employee.dep_id=department.dep_id)and(department.dep_code='01')and(employee.dep_code='01');
它的搜索分析结果如下:
Estimate2I/Ooperations
Scandepartmentusingprimarykey
forrowswheredep_codeequals'01'
Estimategettinghere1times
Scanemployeesequentially
Estimategettinghere5times
第二种方法:
selectemployee.emp_name,department.dep_namefromdepartment,employeewhere(employee.dep_id=department.dep_id)and(department.dep_code='01');
它的搜索分析结果如下:
Estimate2I/Ooperations
Scandepartmentusingprimarykey
forrowswheredep_codeequals'01'
Estimategettinghere1times
Scanemployeesequentially
Estimategettinghere5times
第一种方法与第二种运行效率相同,但第一种方法最好,因为它为优化器提供了更多的选择机会。
第三种方法:
selectemployee.emp_name,department.dep_namefromdepartment,employeewhere(employee.dep_id=department.dep_id)and(employee.dep_code='01');
这种方法最不好,因为它无法使用索引,也就是无法优化……
使用SQL语句时应注意以下几点:
1、避免使用不兼容的数据类型。
例如,Float和Integer,Char和Varchar,Binary和LongBinary不兼容的。
数据类型的不兼容可能使优化器无法执行一些本可以进行的优化操作。
例如:
selectemp_nameformemployeewheresalary>3000;
在此语句中若salary是Float类型的,则优化器很难对其进行优化,因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。
2、尽量不要使用表达式,因它在编绎时是无法得到的,所以SQL只能使用其平均密度来估计将要命中的记录数。
3、避免对搜索参数使用其他的数学操作符。
如:
selectemp_namefromemployeewheresalary*12>3000;
应改为:
selectemp_namefromemployeewheresalary>250;
4、避免使用!
=或<>等这样的操作符,因为它会使系统无法使用索引,而只能直接搜索表中的数据。
目前,几乎所有的应用程序都要和数据库打交道。
通过查询数据库可很容易地获得想要的数据。
但是,令人不满意的是:
某些查询时间长,响应速度慢。
究其原因,一是硬件设备(如CPU、磁盘)的存取速度跟不上,内存容量不够大,这需要计算机制造商的努力;另一方面是没有进行查询优化。
本文就查询优化问题,谈点实践体会。
分解查询
这种方法是把查询分解执行,根据付出开销的多少来决定如何分解,如何执行。
为方便叙述,先给出一个例子。
关系:
SUPPLIER(S#,SNAME,CITY):
S(关键字)
PARTS(P#,PNAME,SIZE):
P(关键字)
PROJECT(J#,JNAME,COLOR):
J(关键字)
INVENTORY(S#,P#,QO):
V(关键字)
SUPPLY(S#,J#,P#,QU):
Y(关键字)
其中,QO:
现有数量QU:
要用的数量
这个查询是找出某城市能提供黑色轿车,且供应量大于1000的供应商名单。
1.一般查询方法
(1)形成卡氏积S×P×J×V×Y;
(2)从卡氏积中选择出满足条件的元组;
(3)在S.SNAME上投影。
这是个5元查询。
当查询涉及到卡氏积时,卡氏积的元组数将组合性增长,这样不仅需要大量的存储空间,而且执行查询时间很长。
2.优化查询方法
该方法是把查询分解处理。
这里介绍两种方法:
(1)一元子查询提取任一N元查询Q(X1,X2......Xn)被替换为一个一元查询Q1和一个在其后执行的Q2,即Q→(Q1,Q2)。
(2)化简Q被替换为两个查询Q1和Q2,Q2在Q1执行后执行,它们只有一个变化,即Q1(X1,X2......Xm),Q2(Xm,Xm+1......Xn)。
例如上例的查询可以分成两个一元查询
SELECTP#
FROMP
WHEREP.PNAME='轿车'ANDP.COLOR='黑色'
和SELECTS#,J#,P#,QO
FROMY
WHEREV.QO>1000
另一部分查询为:
SELECTS.SNAME
FROMS,P,J,V,Y
WHERE(S.S#=V.S#ANDS.S#=Y.S#AND
S.CITY=J.CITYANDP.P#=V.P#AND
T.P#=V.P#ANDJ.J#=Y.J#)
上面例子的查询也很容易化简化为一个涉及(P,V)的查询和在其后执行的涉及(S,J,Y,V)的查询:
SELECTS.SNAME
FORMS,J,V,Y
WHERES.CITY=J.CITYANDS.S#=Y.S#AND
J.J#=Y.J#ANDV.QO>1000ANDP.#=Y.P#
ANDV.S#=Y.S#AND
V.P#=(SELECTV.P#
FROMV,P
WHEREV.P#=PP#ANDP.PNAME='轿车'ANDP.COLOR='黑色')
3.综上所述
一元子查询提取几乎总会得到好处,因为在关系运算之前尽可能减少关系的体积对减少相应的系统开销起很大的作用;·通常会得到期待的优化结果,但也并不绝对如此。
选择最优存取路径
在计算查询表达式值时要充分考虑索引、数据的存储分布等存取路径,以进一步提高查询效率。
例如,选择字段、连接字段上是否有索引,利用索引和HASHING算法可快速地存取包含索引属性特定值的记录。
建立索引,用户可按顺序读文件中的记录,依照接近于物理顺序的顺序读文件中的记录是非常有效的。
这种接近的物理顺序读取文件中记录的索引称为聚簇索引。
聚簇索引使我们可利用存储块中的记录物理聚簇的优点,加快查询速度。
下面具体谈一点实践体会。
前不久,笔者参加了一个在国内开发的国外数据库应用系统的编程工作。
该系统后台使用Oracle7.3数据库,Oracle的DBMS处理SQL执行语句的顺序如下:
(1)根据WHERE子句选择行;
(2)根据GROUPBY子句对这些行进行聚合;
(3)对每一分组用组函数计算结果;
(4)根据HAVING子句选择和排除分组;
(5)根据ORDERBY子句中的组函数所得的结果对组进行排序。
这是一个体现查询优化思路的执行顺序,它对查询的性能具有直接影响。
一般来说,被WHERE子句滤去的记录越多,查询速度就越快。
因为减少了在GROUPBY运算中必须处理的行数量。
在这次工作中笔者的体会如下。
1.尽量避免连接
例如:
PowerBuilder5.0数据窗口在选择库表时自动把各个表中的相同属性名(域也相同)连接起来。
这种自动连接多数情况下是有益的,但有些情况却需要取消连接。
如查询条件为P2000、P3000、P4000的有效区分都为1,这时有两种方法:
(1)WHERE(P2000.有效区分=P3000.有效区分
P3000.有效区分=P4000.有效区分
P2000.有效区分=1
(2)WHERE(P2000.有效区分=1
P3000.有效区分=1
P4000.有效区分=1
第一种方法在自动生成的基础上添加P2000.有效区分=1就可以了,第二种方法先要取消连接,然后再重新写WHERE语句。
表面看,第一种方法简单,其实第一种方法大大降低了执行效率,因为它有不必要的连接。
所以笔者在这里提醒使用者,不要为了一时省事而降低了系统的效率。
2.选择最佳的解决方案
解决同一问题的方法固然很多,但应用中应该选择最佳的解决方法。
例如,对某一问题的数据库查询有两种方法,执行结果一样,效率却不同。
查询要求是:
如果在程序运行界面上输入了负责人代码(放到code中),那么将查询P2000表中负责人代码等于code的负责人名;如果没有输入负责人代码,那么查询P2000表中所有负责人名。
负责人代码的取值范围是0~9999,两种解决方法分别是:
(1)IF没有输入负责人代码THEN
code1=0
code2=9999
ELSE
code1=code2=负责人代码
ENDIF
执行SQL语句为:
SELECT负责人名
FROMP2000
WHERE负责人代码>=:
code1AND负责人代码
<=:
code2
(2)IF没有输入负责人代码THEN
执行SQL语句为:
SELECT负责人名
FROMP2000
ELSE
code=负责人代码
执行SQL语句为:
SELECT负责人代码
FROMP2000
WHERE负责人代码=:
code
ENDIF
第一种方法只用了一条SQL语句,第二种方法用了两条SQL语句。
在没有输入负责人代码时,第二种方法显然比第一种方法执行效率高,因为它没有限制条件;在输入了负责人代码时,第二种方法仍然比第一种方法效率高,不仅是少了一个限制条件,还因相等运算是最
快的查询运算。
分析到这里,读者优劣自明。
此外,Oracle提供存储过程功能,它是编译好、优化过、且存储在数据库中的SQL语句和控制流语言的集合,如果利用好存储过程,可极大地增强SQL语言的功能、效率和灵活性。
以上着重从实现的角度讨论了查询优化,实际上要想根本解决查询优化问题,还需从设计上进行优化,如尽量使用大的内存,数据可适度冗余,库结构优化,对于频繁使用的表建立索引,面向对象的数据库设计方法等等。
数据库的查询优化技术
(1)
数据库系统是管理信息系统的核心,基于数据库的联机事务处理(OLTP)以及联机分析处理(OLAP)是银行、企业、政府等部门最为重要的计算机应用之一。
从大多数系统的应用实例来看,查询操作在各种数据库操作中所占据的比重最大,而查询操作所基于的SELECT语句在SQL语句中又是代价最大的语句。
举例来说,如果数据的量积累到一定的程度,比如一个银行的账户数据库表信息积累到上百万甚至上千万条记录,全表扫描一次往往需要数十分钟,甚至数小时。
如果采用比全表扫描更好的查询策略,往往可以使查询时间降为几分钟,由此可见查询优化技术的重要性。
笔者在应用项目的实施中发现,许多程序员在利用一些前端数据库开发工具(如PowerBuilder、Delphi等)开发数据库应用程序时,只注重用户界面的华丽,并不重视查询语句的效率问题,导致所开发出来的应用系统效率低下,资源浪费严重。
因此,如何设计高效合理的查询语句就显得非常重要。
本文以应用实例为基础,结合数据库理论,介绍查询优化技术在现实系统中的运用。
分析问题
许多程序员认为查询优化是DBMS(数据库管理系统)的任务,与程序员所编写的SQL语句关系不大,这是错误的。
一个好的查询计划往往可以使程序性能提高数十倍。
查询计划是用户所提交的SQL语句的集合,查询规划是经过优化处理之后所产生的语句集合。
DBMS处理查询计划的过程是这样的:
在做完查询语句的词法、语法检查之后,将语句提交给DBMS的查询优化器,优化器做完代数优化和存取路径的优化之后,由预编译模块对语句进行处理并生成查询规划,然后在合适的时间提交给系统处理执行,最后将执行结果返回给用户。
在实际的数据库产品(如Oracle、Sybase等)的高版本中都是采用基于代价的优化方法,这种优化能根据从系统字典表所得到的信息来估计不同的查询规划的代价,然后选择一个较优的规划。
虽然现在的数据库产品在查询优化方面已经做得越来越好,但由用户提交的SQL语句是系统优化的基础,很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效,因此用户所写语句的优劣至关重要。
系统所做查询优化我们暂不讨论,下面重点说明改善用户查询计划的解决方案。
解决问题
下面以关系数据库系统Informix为例,介绍改善用户查询计划的方法。
1.合理使用索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。
现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。
索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行groupby或orderby操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。
比如在雇员表的"性别"列上只有"男"与"女"两个不同值,因此就无必要建立索引。
如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compoundindex)。
●使用系统工具。
如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。
在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。
另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序
应当简化或避免对大型表进行重复的排序。
当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。
以下是一些影响因素:
●索引中不包括一个或几个待排序的列;
●groupby或orderby子句中列的次序与索引的次序不一样;
●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。
如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取
在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。
比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。
避免这种情况的主要方法就是对连接的列进行索引。
例如,两个表:
学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。
如果两个表要做连接,就要在"学号"这个连接字段上建立索引。
还可以使用并集来避免顺序存取。
尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。
下面的查询将强迫对orders表执行顺序操作:
SELECT*FROMordersWHERE(customer_num=104ANDorder_num>1001)ORorder_num=1008
虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。
因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:
SELECT*FROMordersWHEREcustomer_num=104ANDorder_num>1001
UNION
SELECT*FROMordersWHEREorder_num=1008
这样就能利用索引路径处理查询。
4.避免相关子查询
一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。
查询嵌套层次越多,效率越低,因此应当尽量避免子查询。
如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式
MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。
但这种匹配特别耗费时间。
例如:
SELECT*FROMcustomerWHEREzipcodeLIKE"98___"
即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。
如果把语句改为SELECT*FROMcustomerWHEREzipcode>"98000",在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。
例如语句:
SELECT*FROMcustomerWHEREzipcode[2,3]>"80",在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询
把表的一个子集进行排序并创建临时表,有时能加速查询。
它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。
例如:
SELECTcust.name,rcvbles.balance,……othercolumns
FROMcust,rcvbles
WHEREcust.customer_id=rcvlbes.customer_id
ANDrcvblls.balance>0
ANDcust.postcode>"98000"
ORDERBYcust.name
如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:
SELECTcust.name,rcvbles.balance,……othercolumns
FROMcust,rcvbles
WHEREcust.customer_id=rcvlbes.customer_id
ANDrcvblls.balance>0
ORDERBYcust.name
INTOTEMPcust_with_balance
然后以下面的方式在临时表中查询:
SELECT*FROMcust_with_balance
WHEREpostcode>"98000"
临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:
临时表创建后不会反映主表的修改。
在主表中数据频繁修改的情况下,注意不要丢失数据。
7.用排序来取代非顺序存取
非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。
SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。
数据库的查询优化技术
(2)
实例分析
下面我们举一个制造公司的例子来说明如何进行查询优化。
制造公司数据库中包括3个表,模式如下所示:
1.part表
零件号零件描述其他列
(part_num)(part_desc)(othercolumn)
102,032Seageat30Gdisk……
500,049Novel10Mnetworkcard……
2.vendor表
厂商号厂商名其他列
(vendor_num)(vendor_name)(othercolumn)
910,257SeageatCorp……
523,045IBMCorp……
3.parven表
零件号厂商号零件数量
(part_num)(vendor_num)(part_amount)
102,032910,2573,450,000
234,423321,0014,000,000
下面的查询将在这些表上定期运行,并产生关于所有零件数量的报表:
SELECTpart_desc,vendor_name,part_amount
FROMpart,vendor,parven
WHEREpart.part_num=parven.part_num
ANDparven.vendor_num=vendor.vendor_num
ORDERBYpart.part_num
如果不建立索引,上述查询代码的开销将十分巨大。
为此,我们在零件号和厂商号上建立索引。
索引的建立避免了在嵌套中反复扫描。
关于表与索引的统计信息如下:
表行尺寸行数量每
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- SQL 语言 查询 效率