SQL代码性能优化
在传统的数据库优化理论中存在着一些有争议的问题,例如:
传统上认为,数据库通过使用索引就一定可以更快地遍历表,而且在实际的数据库系统中的优化器都是根据定义的索引来提高性能。
但是,如果在SQL语句的where子句中写的SQL代码不合理,就会造成优化器删去索引而使用全表扫描,一般这种SQL语句就是所谓的劣质SQL语句。在编写SQL语句时我们应清楚优化器根据何种原则来删除索引,这有助于写出高性能的SQL语句。
研究SQL调整的目标就是要确定执行计划对于语句是最优的。最直接的是采用set timing on命令来检验SQL执行的速度,并且计算查询需要花费的时间。
除了分析与试验相结合的考证这些有争议的数据库优化理论与方法外,在实际的运用中我进一步总结出了一些新的优化技巧和方法,总结如下:
一、避免索引失效
如果对where子句的写法不注意,即使某些列存在索引,但是由于编写了劣质的SQL,系统在运行该SQL语句时也不能使用该索引,而同样使用全表扫描,这就造成了响应速度的极大降低。
1. IS NULL 与 IS NOT NULL
不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。
任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。
2. 联接列
对于有联接的列,即使最后的联接值为一个静态值,优化器还是不会使用索引的。我们一起来看一个例子,假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。
下面是一个采用联接查询的SQL语句,
select * from employee
where first_name||''||last_name ='Beill Cliton';
上面这条语句完全可以查询出是否有Bill Cliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。
当采用下面这种SQL语句的编写时,Oracle系统就可以采用基于last_name创建的索引,从而提高检索速度。
Select * from employee
Where first_name ='Beill' and last_name ='Cliton';
3. 搜索词的词首出现通配符(%)
避免使用在搜索词的词首出现通配符(%)的like语句。
如果要求在职工表中查询名字中包含cliton的人。可以采用如下的查询SQL语句:
select * from employee where last_name like '%cliton%';
这里由于通配符(%)在搜寻词首出现,所以Oracle系统不能使用last_name的索引。在很多情况下可能无法避免这种情况,但是一定要心中有底,通配符如此使用会降低查询速度。然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用:
select * from employee where last_name like 'c%';
4. Order by语句
Order by语句决定了Oracle如何将返回的查询结果排序。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。
5. NOT
在进行查询时经常需要在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,同时也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一个NOT子句的例子:
where not (status ='VALID')
如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中显式地加入NOT词,NOT仍在运算符中,见下例:
where status <>'INVALID';
再看下面这个例子:
Select * from employee where salary<>3000;
对这个查询,可以改写为不使用NOT:
Select * from employee where salary<3000 or salary>3000;
虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许Oracle对salary列使用索引,而第一种查询由于使用了NOT运算符所以不能使用索引。
二、保证索引的高效
1. 等式比较和范围比较
当WHERE子句中有的索引列非等式表达式时,ORACLE是不能合并它们的,ORACLE不得不采取逐一比较的方式。
举例:
DEPT_NO上有一个非唯一性索引,EMP_CAT也有一个非唯一性索引。
select ENAME from EMP
where DEPT_NO > 20 and EMP_CAT = ‘A';
这里只有EMP_CAT索引被用到,然后所有的记录将逐条与DEPT_NO条件进行比较。执行路径如下:
TABLE ACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON CAT_IDX
2.避免在索引列上使用计算.
WHERE子句中,如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描。
低效:
select … from DEPT where SAL * 12 > 25000;
高效:
select … from DEPT where SAL > 25000/12;
3.用 >= 替代 >
如果DEPT_NO上有一个索引。
高效:
select * from EMP where DEPT_NO >=4
低效:
select * from EMP where DEPT_NO >3
两者的区别在于:前者DBMS将直接跳到第一个 DEPT_NO 等于4的记录而后者将首先定位到 DEPT_NO=3 的记录并且向前扫描到第一个DEPT_NO大于3的记录。
4.避免在索引列上使用IS NULL和IS NOT NULL
应该避免在索引中使用任何可以为空的列,因为这样ORACLE将不会使用该索引。对于单列索引,如果列包含空值,索引中将不存在此记录。而对于复合索引,如果每个列都为空,索引中同样不存在此记录。如果至少有一个列不为空,则记录存在于索引中。
举例:
如果唯一性索引建立在表的A列和B列上,并且表中存在一条记录的A,B值为(123,null),ORACLE将不接受下一条具有相同A,B值(123,null)的记录(插入)。然而如果所有的索引列都为空,ORACLE将认为整个键值为空,根据数据库的空不等于空(null <> null)约定,新插入是合法的。 因此甚至可以插入1000条具有相同键值的记录,当然它们都必须是空!
因为索引列中是不存在空值的,所以WHERE子句中对索引列进行空值比较将使ORACLE停用该索引。
低效:(索引失效)
select … from DEPARTMENT
where DEPT_CODE is not null;
高效:(索引有效)
select … from DEPARTMENT
where DEPT_CODE >=0;
5.总是使用索引的第一个列
如果索引是建立在多个列上的,那么,只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引。
SQL> create table multiindexusage ( inda number , indb number , descr varchar2(10));
Table created.
SQL> create index multindex on multiindexusage(inda,indb);
Index created.
SQL> set autotrace traceonly
SQL> select * from multiindexusage where inda = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MULTIINDEXUSAGE'
2 1 INDEX (RANGE SCAN) OF 'MULTINDEX' (NON-UNIQUE)
SQL> select * from multiindexusage where indb = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE'
很明显,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引。
6.避免改变索引列的类型.
当比较不同数据类型的数据时,ORACLE自动对列进行简单的类型转换。假设 EMP_NO是一个数值类型的索引列。
select … from EMP where EMP_NO = ‘123'
实际上,经过ORACLE类型转换,语句转化为:
select … from EMP where EMP_NO = to_number(‘123')
幸运的是类型转换没有发生在索引列上,索引的用途没有被改变。
现在,假设EMP_TYPE是一个字符类型的索引列:
select … from EMP where EMP_TYPE = 123
这个语句被ORACLE转换为:
select … from EMP where to_number(EMP_TYPE)=123
因为内部发生的类型转换作用在索引列上,这个索引将不会被使用!
为了避免ORACLE对SQL进行隐式的类型转换,最好把类型转换用显式表现出来。注意当字符和数值比较时,ORACLE会优先转换数值类型到字符类型。
三、选择最有效率的表名顺序
由于ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理。在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。当ORACLE处理多个表时,会运用排序及合并的方式连接它们。首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。
例如:
表 TAB1 16384 条记录
表 TAB2 1 条记录
选择TAB2作为基础表 (最好的方法)
select count(*) from tab1,tab2 执行时间0.96秒
选择TAB2作为基础表 (不佳的方法)
select count(*) from tab2,tab1 执行时间26.09秒
如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.
例如:
EMP表描述了LOCATION表和CATEGORY表的交集.
select *
from LOCATION l , CATEGORY c, EMP e
where e.EMP_NO between 1000 and 2000
and e.CAT_NO = c.CAT_NO
and e.LOCN = l.LOCN
将比下列SQL更有效率
select *
from EMP e , LOCATION l , CATEGORY c
where e.CAT_NO = c.CAT_NO
and e.LOCN = l.LOCN
and e.EMP_NO between 1000 and 2000
当然,如果使用的是CBO (COST BASED OPTIMIZER)模式,优化器会检查SQL语句中的每个表的物理大小,索引的状态,然后选用花费最低的执行路径。但是如果在RBO (RULE BASED OPTIMIZER)模式下,并且所有的连接条件都有索引对应,在这种情况下,必须人为指定基础表来提高效率。因为所有操作都是要耗费系统资源和时间的,所以最好的方法是在RBO模式下人为的指定表名的顺序,而不需要优化器进行额外的工作。
四、WHERE子句中的连接顺序
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。也就是时间要求越多的操作必须对应越小的数据量。
例如:
(低效,执行时间156.3秒)
select …
from EMP e
where SAL > 50000
and JOB = ‘MANAGER'
and 25 < (select count(*) from EMP
where MGR = e.EMPNO);
(高效,执行时间10.6秒)
select …
from EMP e
where 25 < (select count(*) from EMP
where MGR=e.EMPNO)
and SAL > 50000
and JOB = ‘MANAGER';
五、SELECT子句中避免使用‘*’
当想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用’*’是一个方便的方法。然而这是一个非常低效的方法。在ORACLE的解析过程中,会将'*'依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
因此,对于需要列出所有的COLUMN的情况:
select * from table_name;
比起
select column1,column2,…,columnN from table_name;
要慢的多。
六、用表连接替换EXISTS
通常来说,采用表连接的方式比EXISTS更有效率。因为Oracle优化器在见到EXISTS的时候,会在SQL的执行路径上包含NESTED LOOP,而使用表连接的时候则会在SQL的执行路径上包括FILTER,可见效率的差别有多大。
使用EXISTS:
SELECT ENAME
FROM EMP E
WHERE EXISTS (SELECT ‘X'
FROM DEPT
WHERE DEPT_NO = E.DEPT_NO
AND DEPT_CAT = ‘A');
使用表连接:
SELECT ENAME
FROM DEPT D,EMP E
WHERE E.DEPT_NO = D.DEPT_NO
AND DEPT_CAT = ‘A' ;
七、用EXISTS替换DISTINCT
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT。一般可以考虑用EXIST替换
例如:
低效:
select distinct DEPT_NO, DEPT_NAME
from DEPT d, EMP e
where d.DEPT_NO = e.DEPT_NO
高效:
select DEPT_NO, DEPT_NAME
from DEPT d
where exists ( select ‘X'
from EMP e
where e.DEPT_NO = d.DEPT_NO);
EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果。
八、尽量多使用COMMIT
在保证事务完整性的基础上,只要有可能,在程序中应尽量多使用COMMIT。这样,不再使用的资源会被尽快释放出来,系统的资源负担会减轻许多:
一旦执行COMMIT操作之后,系统所释放的资源如下:
a. 回滚段上用于恢复数据的信息。
b. 被程序语句获得的锁。
c. redo log buffer 中的空间。
d. ORACLE为管理上述3种资源而导致的内部花费。
分享到:
相关推荐
在SQL Server数据库管理系统中,性能优化与管理是至关重要的任务,因为这直接影响到系统的响应速度、资源利用率以及整体系统的稳定性。"SQL Server性能优化及管理艺术"这一主题涵盖了多个方面,包括查询优化、索引...
大话sqlserver性能优化专题PPT和sql脚本,该压缩包里一共包含43课的PPT和sql脚本示例代码,欢迎下载使用
这些工具不仅提供了上述功能,还专门针对Oracle数据库的特性进行了优化,如支持PL/SQL代码分析、Exadata特定优化建议等。 使用SQL优化工具时,要注意结合实际业务场景和数据库架构,避免盲目优化导致的副作用,如...
SQL Server性能优化是数据库管理员和开发人员必须掌握的重要技能,它对于确保数据库系统的稳定性和响应速度至关重要。在生产环境中,正确的优化方法可以显著提升数据库的处理能力和效率,减少资源消耗,并避免潜在的...
"SQL Server 2008查询性能优化源代码"这一资源提供了一种深入学习和实践的方法,帮助我们理解并提升SQL查询的执行效率。下面将详细探讨SQL Server 2008中查询性能优化的关键知识点。 1. **索引优化**: - **非聚簇...
在SQL Server 2008查询性能优化的主题中,我们主要关注如何提高SQL查询的速度,减少资源消耗,以及提升数据库的整体...通过深入理解和实践这些技巧,开发者可以显著提升其SQL代码的运行效率,从而提高整个系统的性能。
3. 优化建议:提供性能优化建议,如索引建议或查询改写。 4. 数据库对比:比较不同版本或环境的数据库结构,找出差异并生成更新脚本。 5. 反向工程:从现有数据库中生成建表脚本,便于备份或重构。 使用SQL代码生成...
1. **SQL代码性能优化**:文档可能涵盖了SQL查询的基本原理,如选择合适的索引、避免全表扫描、减少子查询以及正确使用JOIN操作。可能还会讨论到如何避免使用SELECT *,而是具体指定需要的列,以减少数据传输量。...
"SQL性能优化示例"这个资源专为开发人员和高级DBA设计,旨在提供实用的优化策略和代码实例。以下是关于SQL性能优化的一些关键知识点: 1. **查询优化**:SQL查询优化是最基础的性能提升手段。通过重构查询语句,...
SQL性能优化技巧分享 SQL性能优化技巧是指在数据库中优化SQL语句的执行效率,以提高数据库的性能和响应速度。Oracle数据库提供了多种优化技术和工具,以帮助开发者和DBA更好地优化SQL语句的执行。 1. 理解执行计划...
- 使用高级性能优化技术,例如使用查询提示、编写高效T-SQL代码等。 综上所述,SQL Server性能优化是一个系统工程,它要求数据库管理员不仅要有扎实的理论知识,还要具备丰富的实践经验。通过正确分析和优化,可以...
《SQL Server 2008查询性能优化》,由于文件比较大,压缩成两个文件,下载的朋友注意了。 《SQL Server 2008查询性能优化》为你提供了处理查询性能所需要的工具。建立、维护数据库和数据库服务器可能是个困难的工作...
### SQL Server 性能优化的原则 #### 一、SQL Server 自调整特性 SQL Server 的版本迭代不断优化其自我管理和自我调整的能力。特别是从 SQL Server 7.0 开始,这一趋势更加明显。此版本提供了更高级别的自配置与自...
SQL Server性能优化是...总之,SQL Server性能优化是一个综合性的过程,涉及代码编写、查询设计、索引利用、数据类型选择等多个方面。通过遵循上述原则,可以显著提升数据库的性能,降低资源消耗,提高整体系统效率。
《收获,不止SQL优化》是一本专注于数据库性能优化的书籍,尤其关注Oracle数据库系统的SQL调优。这本书通过实例和深入的解析,帮助读者理解和掌握如何提升SQL查询的效率,从而优化整个数据库系统的性能。在阅读这...
在SQL Server性能优化领域,有许多关键点和技术可以显著提升数据库系统的效率。以下是对这些知识点的详细阐述: 1. **查询优化**:SQL Server查询优化器是数据库性能的关键部分,它负责选择执行查询的最佳路径。...