SQL优化
– 写SQL语句用大写字母:
因为Oracle总是先解析SQL语句,把小写的字母转换成大写字母再执行。
– 尽量不要使用SELECT * 查询:
数据库在解析的过程中会将 * 依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
– 用 >= 替代 >:
高效: SELECT * FROM emp WHERE deptno >=4
低效: SELECT * FROM emp WHERE deptno >3
说明:两者的区别在于高效直接跳到deptno等于4的记录而低效将首先定位到deptno=3的记录并且向前扫描到第一个deptno大于3的记录。
– 用WHERE子句替换HAVING子句:
避免使用HAVING子句,HAVING 会在检索出所有记录之后才对结果集进行过滤,这个处理需要排序,总计等操作,如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。
在多表联接查询时,ON 比 WHERE 更早起作用,系统首先根据各个表之间的联接条件,把多个表合成一个临时表后再由 WHERE 进行过滤,然后再计算,计算完后再由 HAVING 进行过滤。
– 使用索引提高查询效率:
索引用来提高检索数据的效率,Oracle使用了一个复杂自平衡B-tree结构,虽然使用索引能够提高查询效率,但是我们也必须注意到它的代价,索引需要空间来存储,也需要定期维护, 每当有记录在表中增删或索引字段被修改时,索引本身也会被修改,这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出 4,5 次的磁盘I/O, 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢,所以定期的重构索引是有必要的。
– 避免在索引字段上使用函数或进行计算操作:
说明:WHERE子句中,如果索引列使用函数,优化器将不使用索引而使用全表扫描。
例一:
低效:SELECT 字段 FROM DEPT WHERE SAL * 12 > 25000;
高效: SELECT 字段 FROM DEPT WHERE SAL > 25000/12;
例二:
低效:SELECT * FROM users WHERE YEAR(date)<2007; 高效:SELECT * FROM users WHERE date<’2007-01-01’;
– 避免在索引列上使用IS NULL和IS NOT NULL:
低效: (索引失效) SELECT * FROM department WHERE dept_code IS NOT NULL;
高效: (索引有效) SELECT * FROM department WHERE dept_code >= 0;
– 总是使用索引的第一个列:
如果索引是建立在多个列上,只有在它的第一个列被 WHERE 子句引用时,优化器才会选择使用该索引,这也是一条简单而重要的规则,当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引。
– 不要以字符格式声明数字,要以数字格式声明字符值。(日期同样)否则会使索引无效,产生全表扫描。
– 保证连接的索引是相同的类型,
说明:a 表和 b 表相关联的字段,必须是同类型的,这些类型都建立了索引,这样才能两个表都能使用索引,如果类型不一样,至少有一个表使用不了索引。
– 使用 LIKE 查询有索引的字段注意点:
SELECT * FROM tablename name LIKE ‘xxx%’;
这个SQL会使用name的索引(前提name建立了索引),而下面的语句就使用不了索引
SELECT * FORM tablename name LIKE ‘%xxx’;
因为‘%’代表任何字符,%xxx不知道怎么去匹配索引,所以不能使用索引。
– 排序的索引问题
Mysql查询只能用一个索引,因此如果WHERE子句中已经使用了索引的话,那么ORDER BY中的列是不会使用索引的,因此数据库默认排序可以符合要求情况下不要使用排序操作,尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
– 复合索引
比如有一条语句这样的:SELECT * FROM users WHERE name =’freddy’ AND age=26;
如果我们是在area和age上分别创建索引的话,由于Mysql查询每次只能使用一个索引,虽然这样已经相对于不创建索引时全表扫描提高了很多效率,但是如果area,age两列上创建复合索引的话将带来更高的效率,如果我们创建了(area,age,salary)的复合索引,那么其实相当于创建了(area,age,salary),(area,age),(area)三个索引,这样称为最佳左前缀特性。因此我们在创建复合索引的应该将最常用作限制条件的列放在最左边,依次递减。
– 能用inner join连接尽量使用inner join连接,因为inner join是等值连接。
– 尽量少用OR
当 WHERE 子句中存在多个条件以“或”并存的时候,Mysql的优化器并没有很好的解决其执行计划优化问题,再加上Mysql特有的SQL与Storage分层架构方式,造成了其性能比较底下,很多时候使用 UNION ALL或者UNION (必要的时候)的方式代替 OR 会得到更好的效果。
– 尽量用UNION ALL 代替UNION
两者的差异主要是 UNION 需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的cpu运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用union all而不是union.
– 用EXISTS替代IN、用NOT EXISTS替代NOT IN:
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接,在这种情况下使用EXISTS(或NOT EXISTS)通常将提高查询的效率。
在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS.
例子:
(高效)SELECT * FROM emp WHERE empno > 0 AND EXISTS (SELECT ‘x’ FROM dept WHERE dept.deptno = emp.deptno AND loc = ‘melb’)
(低效) SELECT * FROM emp WHERE empno > 0 AND deptno IN(SELECT deptno FROM dept WHERE loc = ‘melb’)
– 尽量不要使用NOT IN和<>操作
A. NOT IN和<>操作都不会使用索引,而是将会进行全表扫描。NOT IN可以NOT EXISTS代替,id<>3则可以使用id>3 or id <3;如果NOT EXISTS是子查询,还可以尽量转化为外连接或者等值连接,要看具体sql的业务逻辑。 B.把NOT IN转化为LEFT JOIN如: SELECT * FROM customerinfo WHERE CustomerIDNOT in (SELECT CustomerID FROM salesinfo ); 优化: SELECT * FROM customerinfo LEFT JOINsalesinfoON customerinfo.CustomerID=salesinfo. CustomerID WHEREsalesinfo.CustomerID IS NULL;
– 对多表关联的查询,建立视图
对多表的关联可能会有性能上的问题,我们可以对多表建立视图,这样操作简单,增加数据安全性,通过视图,用户只能查询和修改指定的数据,且提高表的逻辑独立性,视图可以屏蔽原有表结构变化带来的影响。