当前位置: 首页 > 科技观察

数据库性能优化之SQL语句优化(上篇)

时间:2023-03-18 23:10:54 科技观察

一、问题的提出在应用系统开发初期,由于开发数据库的数据量比较小,各种方式理解起来比较困难查询SQL语句的编写SQL语句和复杂视图的编写。性能有好有坏,但是如果将应用系统提交到实际应用中,随着数据库中数据的增加,系统的响应速度成为目前系统需要解决的最重要的问题之一。系统优化的一个很重要的方面就是SQL语句的优化。对于海量数据,低质量SQL语句与高质量SQL语句的速度差异可达数百倍。由此可见,对于一个系统来说,仅仅实现其功能是不够的,还需要编写高质量的SQL语句来提高系统的可用性。.在大多数情况下,Oracle使用索引来更快地遍历表,优化器主要是根据定义的索引来提高性能。但是,如果SQL语句的where子句中写的SQL代码不合理,就会导致优化器删除索引,使用全表扫描。一般来说,这种SQL语句就是所谓的劣质SQL语句。在编写SQL语句的时候,我们应该知道优化器是根据什么原理来删除索引的,这有助于编写出高性能的SQL语句。2、SQL语句编写注意事项下面详细介绍一些SQL语句的where子句在编写时需要注意的问题。在这些where子句中,即使某些列有索引,由于SQL写得不好,系统在运行SQL语句时无法使用索引,还使用了全表扫描,导致响应极慢。减量很大。1.算子优化(a)IN算子使用IN写SQL。优点是比较好写,清晰易懂,更适合现代软件开发的风格。但是带IN的SQL性能总是比较低。从Oracle对带IN的SQL和不带IN的SQL进行分析的步骤来看,有以下区别:ORACLE试图将其转化为多表的连接。如果转换不成功,先在IN中执行子查询,再查询外表记录。如果转换成功,直接使用多表的连接方式查询。可以看出,INSQL至少多了一个转换过程。一般的SQL可以转换成功,但是包含分组和统计的SQL不能转换。推荐方案:业务密集型SQL尽量不要使用IN运算符,改用EXISTS方案。(b)NOTIN操作符强烈不推荐这个操作,因为它不能应用表的索引。推荐方案:使用NOTEXISTS方案代替(c)ISNULL或ISNOTNULL操作(判断字段是否为空)判断字段是否为空,一般不会应用索引,因为索引不索引空值。Null不能作为索引,任何包含空值的列都不会被索引。即使索引有多个列,只要其中一列包含null,该列就会从索引中排除。也就是说,如果某列中存在空值,即使对该列建立索引,也不会提高性能。任何在where子句中使用isnull或isnotnull的语句优化器都不允许使用索引。推荐方案:换成其他功能相同的操作,比如:aisnotnull改成a>0ora>''等,该字段不允许为空,使用默认值替换空值。比如application中的status字段不允许为空,默认为application。(d)>和<运算符(大于或小于运算符)对于大于或小于运算符一般不需要调整,因为如果有索引就会使用索引搜索,但可以在中优化有些情况,比如A表有100万条记录,一个数值字段A,A=0表示30万条记录,A=1表示30万条记录,A=2表示39万条记录,A=3表示1万条记录。那么执行A>2和A>=3的效果就大不一样了,因为当A>2时,ORACLE会先找出2的记录索引再进行比较,而当A>=3时,ORACLE会直接找=记录索引为3。(e)LIKE运算符LIKE运算符可以应用于通配符查询,里面的通配符组合几乎可以到达任何查询,但是如果使用不当,会导致性能问题,比如LIKE'%5400%'查询不会引用索引,而LIKE'X5400%'将引用范围索引。实际例子:用YW_YHJBQK表中商号后面的账户标识号查询商号YY_BHLIKE'%5400%'这个条件会产生全表扫描,如果改成YY_BHLIKE'X5400%'ORYY_BHLIKE'B5400%'YY_BH的索引会被用来查询两个范围,性能肯定会有很大的提升。Likestatementwithwildcard(%):也用上面的例子来看这种情况。目前的需求是:查询employee表中name包含cliton的员工。可以使用如下查询SQL语句:select*fromemployeewherelast_namelike'%cliton%';这里,因为通配符(%)出现在搜索词的开头,所以Oracle系统不使用last_name索引。在很多情况下,这种情况可能是不可避免的,但是要知道,这样使用通配符会减慢查询速度。但是,当通配符出现在字符串的其他地方时,优化器可以利用索引。该索引用于以下查询:select*fromemployeewherelast_namelike'c%';(f)UNION运算符UNION会在联表后过滤掉重复的记录,所以在联表后对结果集生成排序操作,删除重复记录并返回结果。在大多数应用中,不会产生重复记录,最常见的是进程表和历史表的UNION。例如:select*fromgc_dfysunionselect*fromls_jg_dfy??s,这条SQL在运行时取两张表的结果,然后利用排序空间对重复记录进行排序删除,最后返回结果集。如果表的数据量很大,可能会导致使用磁盘进行排序。推荐解决方案:使用UNIONALL运算符而不是UNION,因为UNIONALL运算只是将两个结果合并并返回。select*fromgc_dfysunionallselect*fromls_jg_dfy??s(g)joincolumns对于join列,即使最后的join值是静态值,优化器也不会使用索引。我们一起来看一个例子。假设有一张员工表(employee),员工的名字和姓氏存储在两列(FIRST_NAME和LAST_NAME)中。现在我们要查询一位名叫BillClinton的员工。以下是使用连接查询的SQL语句:select*fromemploysswherefirst_name||''||last_name='BeillCliton';上面的语句可以完全检查是否有员工BillCliton,但是这里需要注意的是,系统优化器是基于last_name创建的索引,并没有使用。当使用如下SQL语句时,Oracle系统可以使用基于last_name创建的索引。wherefirst_name='Beill'andlast_name='Cliton';(h)Orderby语句ORDERBY语句决定了Oracle如何对返回的查询结果进行排序。Orderby语句对要排序的列没有特殊限制,还可以在列中添加函数(如连接或添加等)。Orderby语句中的任何非索引项或计算表达式都会减慢查询速度。仔细检查非索引项或表达式的orderby语句,这会降低性能。解决这个问题的方法是重写orderby语句使用索引,或者为使用的列创建另一个索引,并且绝对避免在orderby子句中使用表达式。(i)NOT我们在查询的时候经常会在where子句中使用一些逻辑表达式,比如大于,小于,等于,不等于等,也可以使用and(和),or(或)andnot(不是)。NOT可用于反转任何逻辑运算符。以下是NOT子句的示例:wherenot(status='VALID')如果要使用NOT,则应在否定短语前添加括号,并在短语前添加NOT运算符。NOT运算符包含在另一个逻辑运算符中,即不等于(<>)运算符。换句话说,即使查询的where子句中没有显式添加NOT字,NOT仍然在运算符中,见下面的例子:wherestatus<>'INVALID';对于这个查询,它可以重写为不使用NOT:select*fromemployeewheresalary<3000orsalary>3000;虽然这两次查询的结果是一样的,但是第二种查询方案会比第一种查询方案快。第二个查询允许Oracle在salary列上使用索引,而第一个查询不能使用索引。2.SQL编写的影响(a)相同功能和性能的不同SQL编写方式的影响。比如程序员A写的SQL是Select*fromzl_yhjbqk,程序员B写成Select*fromdlyx.zl_yhjbqk(带表主前缀),程序员C写成Select*fromDLYX.ZLYHJBQK(大写)表名)程序员D写了Select*fromDLYX.ZLYHJBQK(中间多了个空格)。以上4条SQL经过ORACLE分析排序后的结果和执行时间是一样的,但是从ORACLE共享内存SGA的原理可以看出ORACLE分析每条SQL都是占用共享内存的。如果SQL字符串和格式写的一模一样,ORACLE只会分析一次,共享内存只会留下一次分析结果,这样既可以减少分析SQL的时间,也可以减少SQL中重复的??信息共享内存。ORACLE还可以准确统计SQL的执行频率。(b)WHERE后的条件顺序影响WHERE子句后的条件顺序,将对大数据量表的查询产生直接影响。例如:Select*fromzl_yhjbqkwheredy_dj='below1KV'andxh_bz=1Select*fromzl_yhjbqkwherexh_bz=1anddy_dj='below1KV'上面两条SQL中dy_dj(电压等级)和xh_bz(删号)这两个字段是没有索引的,所以执行始终扫描整个表。第一条SQL中dy_dj='below1KV'的条件在记录集中的比例为99%,而xh_bz=1的比例仅为0.5%。第一条SQL执行时,99%的行所有记录都与dy_dj和xh_bz进行比较,而第二条SQL执行时,0.5%的记录与dy_dj和xh_bz进行比较,因此可以得出CPU使用率第二个SQL明显低于第一个。(c)查询表顺序的影响FROM后面的表中的列表顺序会影响SQL的执行性能。如果没有索引,ORACLE不对表进行统计分析,ORACLE会按照表出现的先后顺序进行链接,当这个可见表的顺序错误时,数据的交叉会非常消耗服务器资源。(注意:如果对表进行统计分析,ORACLE会自动先进行小表的链接,再进行大表的链接)3.SQL语句索引的利用(a)优化函数处理的字段条件字段不能使用索引,如:substr(hbs_bh,1,4)='5400'优化处理:hbs_bhlike'5400%'trunc(sk_rq)=trunc(sysdate)优化处理:sk_rq>=trunc(sysdate)andsk_rq已经显式或隐式例如ss_df+20>50优化处理:ss_df>30'X'||hbs_bh>'X5400021452'优化处理:hbs_bh>'5400021542'sk_rq+5=sysdate优化处理:sk_rq=sysdate-5hbs_bh=5401002554优化处理:hbs_bh='5401002554'注:该条件对hbs_bh进行隐式to_number转换,因为hbs_bh字段是字符类型。当条件包括该表的多个字段时,无法进行索引,如:hbs_bh=5401002554优化处理:hbs_bh='5401002554'注意:该条件对hbs_bh进行隐式to_number转换,因为hbs_bh字段是字符类型。当条件包括该表的多个字段时无法进行索引,如:ys_df>cx_df无法优化qc_bh||kh_bh='5400250000'优化处理:qc_bh='5400'andkh_bh='250000'