生死看淡,不服就干。SQL常见的一些优化。

我们要做到不但会写SQL,还要做到写出性能优良的SQL语句。

索引问题

  经常发现有很多后台程序的性能问题是因为缺少何时索引造成的,有的表甚至没有索引。这种情况往往是因为在设计表时,没有定义索引,而开发初期,由于表中的数据不多,所以感觉不到索引对性能的影响,但是一旦项目发布,表中数据多了之后,缺少索引对性能的影响便会越来越大。

还有不要在建立索引的数据列上进行一下操作:

避免对索引字段进行计算操作;

避免在索引字段上使用not、<>、!=;

避免在索引字段单使用 IS NULL、IS NOT NULL;

避免在索引字段单出现数据类型转换;

避免在索引字段上使用函数;

避免建立索引的列中使用空值。

 

SQL语句模型结构优化

a. ORDER BY + LIMIT组合的索引优化

 

如果一个SQL语句形如:
SELECT [column1],[column2],…. 
FROM[TABLE] ORDER BY [sort] 
LIMIT [offset],[LIMIT];

这个SQL语句优化比较简单,在[sort]这个栏位上建立索引即可。

 

b. WHERE + ORDER BY + LIMIT组合的索引优化

 

如果一个SQL语句形如:
SELECT [column1],[column2],…. 
FROM [TABLE] WHERE [columnX] = [VALUE]
ORDER BY [sort]
LIMIT [offset],[LIMIT];

这个语句,如果你仍然采用第一个例子中建立索引的方法,
虽然可以用到索引,但是效率不高。更高效的方法是建立一
个联合索引(columnX,sort)

 

c. WHERE+ORDER BY多个栏位+LIMIT

 

如果一个SQL语句形如:
SELECT * FROM [table]
WHERE uid=1 
ORDER x,y 
LIMIT 0,10;

对于这个语句,大家可能是加一个这样的索引:
(x,y,uid)。但实际上更好的效果是(uid,x,y)。
这是由MySQL处理排序的机制造成的。

 

where子句使用IN 或 NOT IN的优化

 

in和not in 也要慎用,否则也会导致全表扫描。

 

方案一:between替换in

如SQL:
SELECT id FROM A WHERE numin(1,2,3) 
优化成:SELECT id FROM A WHEREnum between 1 and 3

方案二:exist替换in

如SQL:
SELECT id FROM A WHERE numin(select num from b ) 
优化成:SELECT num FROM A 
WHERE num exists(select 1 from B where B.num = A.num)

方案三:left join替换in

如SQL:
SELECT id FROM A WHERE num in(selectnum from B) 
优化成:SELECT id FROM A LEFT JOIN BON A.num = B.num

 

limit分页优化

 

当偏移量特别时,limit效率会非常低

SELECT id FROM A LIMIT 1000,10   很快

SELECT id FROM A LIMIT 90000,10 很慢

优化方法:

方法一:
select id from A order by id limit 90000,10; 
很快,0.04秒就OK。 因为用了id主键做索引当然快

方法二:
select id,title from A
where id>=(select id from collect order byid limit 90000,1) 
limit 10;

方法三:
select id from A order by id  between 10000000 and 10000010;

尽量避免在 where 子句中使用 or 来连接条件,
否则将导致引擎放弃使用索引而进行全表扫描,如: 

select id from t wherenum=10 or num=20 
可以这样查询: 
select id from t where num=10 
union all 
select id from t where num=20

 

删除重复记录:

 

最高效的删除重复记录方法 ( 因为使用了ROWID)例子:

 

DELETE FROM EMP E 
WHERE E.ROWID > (SELECT MIN(X.ROWID)
FROM EMP X WHERE X.EMP_NO = E.EMP_NO);

 

用TRUNCATE替代DELETE: 

当删除表中的记录时,在通常情况下, 回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢复的信息。当命令运行后,数据不能被恢复.因此很少的资源被调用,执行时间也会很短。(TRUNCATE只在删除全表适 用,TRUNCATE是DDL不是DML)。

 

使用表的别名

当在SQL语句中有连接多个表的时候,请使用表的别名并把别名的前缀置于每个列名上,这样就可以减少解析的时间并减少那些有列名歧义引起的语法错误。

使用用户自定义变量

用户自定义变量是一个用来存储内容的临时容器,在 MySQL 连接的整个过程都存在,可以使用 set 和 select 来定义:

set @columnName :=
(select column_name from tableName where id =column_id);
set @columnId :=888;

 

之后可以在任何使用表达式的地方使用自定义变量:

Select * from tableName 
where column_id >@columnId and column_name = @columnName;

 

在下列情况下不能使用自定义变量:

a) 使用自定义变量的查询,不能使用缓

b) 不能在使用常量或标识符的地方使用自定义变量,如表名,列名,limit 子句

c) 自定义变量只在一个连接中有效,不能作为连接间的通信

d) MySQL优化器在某些场景下会将这些变量优化掉,会导致不同的预期结果

1:自定义变量可以在给变量赋值的同时使用这个变量,如java的a =a+1 操作。

如实现行号的功能

set @rownum:=0;
select @rownum:=@rownum+1 as num, resource as ip 
from tableName limit 10;

 

 

2:避免重复查询刚刚更新过的数据

 

如果想在更新行的同时又希望获取该行的信息,则可以使用自定义变量实现

方式一:update 之后,在 通过 select 查询

 

update tableName set userName =’Herbert’ where column_id =1;

select userName from tableName where column_id =1;

 

方式二:定义自定义变量

updata tableName set userName = ‘Herbert’ 
where id =1 and @desc:= “Herbert”;

select @desc;

 

这两种方式仍然需要两次网络来回,但是第二种方式不需要访问数据表,因此会很快。

posted @ 2020-03-25 20:27  路大师_XA  阅读(180)  评论(0编辑  收藏  举报