SQL语句性能优化策略
1. 为 WHERE 及 ORDER BY 涉及的列上建立索引
对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引
2. where中使用默认值代替null
应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,创建表时 NULL 是默认值,但大多数时候应该使用 NOT NULL,或者使用一个特殊的值,如 0,-1 作为默认值。
为啥建议where中使用默认值代替null,四个原因:
- 并不是说使用了is null或者 is not null就会不走索引了,这个跟mysql版本以及查询成本都有关;
- 如果mysql优化器发现,走索引比不走索引成本还要高,就会放弃索引,这些条件 !=,<>,is null,is not null经常被认为让索引失效;
- 其实是因为一般情况下,查询的成本高,优化器自动放弃索引的;
- 如果把null值,换成默认值,很多时候让走索引成为可能,同时,表达意思也相对清晰一点;
3. 慎用 != 或 <> 操作符。
MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE。所以:应尽量避免在 WHERE 子句中使用 != 或 <> 操作符, 会导致全表扫描。
4. 慎用 OR 来连接条件
使用or可能会使索引失效,从而全表扫描; 应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。
5. 慎用 IN 和 NOT IN
IN 和 NOT IN 也要慎用,否则会导致全表扫描。对于连续的数值,能用 BETWEEN 就不要用 IN:select id from t where num between 1 and 3。
6. 慎用 左模糊like ‘%…’
模糊查询,程序员最喜欢的就是使用like,like很可能让索引失效。比如:
select id from t where name like‘%abc%’
select id from t where name like‘%abc’
而select id from t where name like‘abc%’才用到索引。
所以:
- 首先尽量避免模糊查询,如果必须使用,不采用全模糊查询,也应尽量采用右模糊查询, 即like ‘…%’,是会使用索引的;
- 左模糊like ‘%…’无法直接使用索引,但可以利用reverse + function index的形式,变化成 like ‘…%’;
- 全模糊查询是无法优化的,一定要使用的话建议使用搜索引擎,比如 ElasticSearch。
7. WHERE条件使用参数会导致全表扫描。
如下面语句将进行全表扫描:
select id from t where num=@num
因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推 迟到 运行时;
它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。
所以, 可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8. 应避免WHERE 表达式操作/对字段进行函数操作
任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,
应尽量避免在 WHERE 子句中对字段进行表达式操作,应尽量避免在 WHERE 子句中对字段进行函数操作。如:
select id from t where num/5=100
应改为:
select id from t where num=100*5
应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。 如:
select id from t where substring(name,1,3)=‘abc’select id from t where datediff(day,createdate,‘2022-11-30’)=0应改为:select id from t where name like ‘abc%’select id from t where createdate>=‘2022-11-30’ and createdate<‘2022-12-1’
9. 用 EXISTS 代替 IN 是一个好的选择
很多时候用exists 代替in 是一个好的选择
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
10. 查询SQL尽量不要使用select *,而是具体字段
最好不要使用返回所有:select * from t ,用具体的字段列表代替 “*”,不要返回用不到的任何字段。select *的弊端:
(1)增加很多不必要的消耗,比如CPU、IO、内存、网络带宽;
(2)增加了使用覆盖索引的可能性;
(3)增加了回表的可能性;
(4)当表结构发生变化时,前端也需要更改;
(5)查询效率低;