数据库设计是应用程序设计的基础其性能直接影响应用程序的性能数据库性能包括存储空间需求量的大小和查询响应时间的长短两个方面为了优化数据库性能需要对数据库中的表进行规范化规范化的范式可分为第一范式第二范式第三范式BCNF范式第四范式和第五范式一般来说逻辑数据库设计会满足规范化的前级标准但由于满足第三范式的表结构容易维护且基本满足实际应用的要求因此实际应用中一般都按照第三范式的标准进行规范化但是规范化也有缺点由于将一个表拆分成为多个表在查询时需要多表连接降低了查询速度
由于规范化有可能导致查询速度慢的缺点考虑到一些应用需要较快的响应速度在设计表时应同时考虑对某些表进行反规范化反规范化可以采用以下几种方法
分割表
分割表包括水平分割和垂直分割
水平分割是按照行将一个表分割为多个表这可以提高每个表的查询速度但查询更新时要选择不同的表统计时要汇总多个表因此应用程序会更复杂
垂直分割是对于一个列很多的表若某些列的访问频率远远高于其它列就可以将主键和这些列作为一个表将主键和其它列作为另外一个表通过减少列的宽度增加了每个数据页的行数一次I/O就可以扫描更多的行从而提高了访问每一个表的速度但是由于造成了多表连接所以应该在同时查询或更新不同分割表中的列的情况比较少的情况下使用
保留冗余列
当两个或多个表在查询中经常需要连接时可以在其中一个表上增加若干冗余的列以避免表之间的连接过于频繁由于对冗余列的更新操作必须对多个表同步进行所以一般在冗余列的数据不经常变动的情况下使用
增加派生列
派生列是由表中的其它多个列计算所得增加派生列可以减少统计运算在数据汇总时可以大大缩短运算时间
二应用程序性能的优化
应用程序的优化通常可分为两个方面源代码和SQL语句由于涉及到对程序逻辑的改变源代码的优化在时间成本和风险上代价很高而对数据库系统性能的提升收效有限因此应用程序的优化应着重在SQL语句的优化对于海量数据劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍可见对于一个系统不是简单地能实现其功能就行而是要写出高质量的SQL语句提高系统的可用性
下面就某些SQL语句的where子句编写中需要注意的问题作详细介绍在这些where子句中即使某些列存在索引但是由于编写了劣质的SQL系统在运行该SQL语句时也不能使用该索引而同样使用全表扫描这就造成了响应速度的极大降低
IS NULL 与 IS NOT NULL
不能用null作索引任何包含null值的列都将不会被包含在索引中即使索引有多列的情况下只要这些列中有一列含有null该列就会从索引中排除也就是说如果某列存在空值即使对该列建索引也不会提高性能
任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的
联接列
对于有联接的列即使最后的联接值为一个静态值优化器不会使用索引的例如假定有一个职工表(employee)对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME)现在要查询一个叫乔治·布什(George Bush)的职工 下面是一个采用联接查询的SQL语句
select * from employee where first_name||
||last_name =
George Bush
;
上面这条语句完全可以查询出是否有George Bush这个员工但是这里需要注意系统优化器对基于last_name创建的索引没有使用
当采用下面这种SQL语句的编写Oracle系统就可以采用基于last_name创建的索引
Select * From employee where first_name =
George
and last_name =
Bush
;
遇到下面这种情况又如何处理呢?如果一个变量(name)中存放着George Bush这个员工的姓名对于这种情况我们又如何避免全程遍历使用索引呢?可以使用一个函数将变量name中的姓和名分开就可以了但是有一点需要注意这个函数是不能作用在索引列上下面是SQL查询脚本
select *
from employee where first_name = SUBSTR
(&&nameINSTR(&&name ))
and last_name = SUBSTR(&&nameINSTR
(&&name )+) ;
带通配符(%)的like语句
同样以上面的例子来看这种情况目前的需求是这样的要求在职工表中查询名字中包含Bush的人可以采用如下的查询SQL语句
select * from employee where last_name like %Bush%;
这里由于通配符(%)在搜寻词首出现所以Oracle系统不使用last_name的索引在很多情况下可能无法避免这种情况但是一定要心中有底通配符如此使用会降低查询速度然而当通配符出现在字符串其他位置时优化器就能利用索引例如在下面的查询中索引得到了使用
select * from employee where last_name like
c%
;
NOT
我们在查询时经常在where子句使用一些逻辑表达式如大于小于等于以及不等于等等也可以使用and(与)or(或)以及not(非)NOT可用来对任何逻辑运算符号取反下面是一个NOT子句的例子
where not (status =
VALID
)
如果要使用NOT则应在取反的短语前面加上括号并在短语前面加上NOT运算符NOT运算符包含在另外一个逻辑运算符中这就是不等于(<>)运算符换句话说即使不在查询where子句中显式地加入NOT词NOT仍在运算符中见下例
where status <>
INVALID
;
再看下面这个例子
select * from employee wheresalary<>
;
对这个查询可以改写为不使用NOT的语句
select * from employee wheresalary<
or salary>
;
虽然这两种查询的结果一样但是第二种查询方案会比第一种查询方案更快些第二种查询允许Oracle对salary列使用索引而第一种查询则不能使用索引