干货!SQL性能优化,书写高质量SQL语句
写SQL语句的时候我们往往关注的是SQL的执行结果,但是是否真的关注了SQL的执行效率,是否注意了SQL的写法规范?
以下的干货分享是在实际开发过程中总结的,希望对大家有所帮助!
1. 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;
复制代码
如果我们结合order by使用。很快,0.04秒就OK。 因为使用了id主键做索引!当然,是否能够使用索引还需要根据业务逻辑来定,这里只是为了提醒大家,在分页的时候还需谨慎使用!
方案二
select id from A order by id between 90000 and 90010;
复制代码
2.利用limit 1 、top 1 取得一行
有些业务逻辑进行查询操作时(特别是在根据某一字段DESC,取最大一笔).可以使用limit 1 或者 top 1 来终止[数据库索引]继续扫描整个表或索引。
反例
SELECT id FROM A LIKE 'abc%'
复制代码
正例
SELECT id FROM A LIKE 'abc%' limit 1
复制代码
3. 任何情况都不要用 select * from table ,用具体的字段列表替换"*",不要返回用不到的字段,避免全盘扫描!
4. 批量插入优化
反例
INSERT into person(name,age) values('A',24)
INSERT into person(name,age) values('B',24)
INSERT into person(name,age) values('C',24)
复制代码
正例
INSERT into person(name,age) values('A',24),('B',24),('C',24),
复制代码
sql语句的优化主要在于对索引的正确使用,而我们在开发中经常犯的错误便是对表进行全盘扫描,一来影响性能,而来耗费时间!
5.like语句的优化
反例
SELECT id FROM A WHERE name like '%abc%'
复制代码
由于abc前面用了“%”,因此该查询必然走全表查询,除非必要(模糊查询需要包含abc),否则不要在关键词前加%
正例
SELECT id FROM A WHERE name like 'abc%'
复制代码
6.where子句使用 in 或 not in 的优化
sql语句中 in 和 not in 的使用请慎用!使用in 或者 not in 会丢弃索引,从而进行全盘扫描!
方案一:between 替换 in
反例
SELECT id FROM A WHERE num in (1,2,3)
复制代码
正例
SELECT id FROM A WHERE num between 1 and 3
复制代码
方案二:exist 替换 in
注:关于exist和in的用法,片尾有彩蛋~
反例
SELECT id FROM A WHERE num in (select num from B)
复制代码
正例
SELECT num FROM A WHERE num exists (select 1 from B where B.num = A.num)
复制代码
方案三:left join 替换 in
反例
SELECT id FROM A WHERE num in (select num from B)
复制代码
正例
SELECT id FROM A LEFT JOIN B ON A.num = B.num
复制代码
7.where子句使用or的优化
通常使用 union all 或 union 的方式替换“or”会得到更好的效果。where子句中使用了or关键字,索引将被放弃使用。
反例
SELECT id FROM A WHERE num = 10 or num = 20
复制代码
正例
SELECT id FROM A WHERE num = 10 union all SELECT id FROM A WHERE num=20
复制代码
8.where子句中使用 IS NULL 或 IS NOT NULL 的优化
反例
SELECT id FROM A WHERE num IS NULL
复制代码
在where子句中使用 IS NULL 或 IS NOT NULL 判断,索引将被放弃使用,会进行全表查询。
正例
优化成num上设置默认值0,确保表中num没有null值, IS NULL 的用法在实际业务场景下SQL使用率极高,我们应注意避免全表扫描
SELECT id FROM A WHERE num=0
复制代码
9.where子句中对字段进行表达式操作的优化
不要在where子句中的“=”左边进行函数、算数运算或其他表达式运算,否则系统将可能无法正确使用索引。
- 1
SELECT id FROM A WHERE datediff(day,createdate,'2019-11-30')=0
复制代码
优化为
SELECT id FROM A WHERE createdate>='2019-11-30' and createdate<'2019-12-1'
复制代码
- 2
SELECT id FROM A WHERE year(addate) <2020
复制代码
优化为
SELECT id FROM A where addate<'2020-01-01'
复制代码
10.排序的索引问题
mysql查询只是用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引。因此数据库默认排序可以符合要求情况下不要使用排序操作;
尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。
11. 尽量用 union all 替换 union
union和union all的差异主要是前者需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的cpu运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用union all而不是union
12.Inner join 和 left join、right join、子查询
- 第一:inner join内连接也叫等值连接是,left/rightjoin是外连接。
SELECT A.id,A.name,B.id,B.name FROM A LEFT JOIN B ON A.id =B.id;
SELECT A.id,A.name,B.id,B.name FROM A RIGHT JOIN ON B A.id= B.id;
SELECT A.id,A.name,B.id,B.name FROM A INNER JOIN ON A.id =B.id;
复制代码
经过来之多方面的证实 inner join性能比较快,因为inner join是等值连接,或许返回的行数比较少。但是我们要记得有些语句隐形的用到了等值连接,如:
SELECT A.id,A.name,B.id,B.name FROM A,B WHERE A.id = B.id;
推荐:能用inner join连接尽量使用inner join连接
- 第二:子查询的性能又比外连接性能慢,尽量用外连接来替换子查询。
反例
mysql是先对外表A执行全表查询,然后根据uuid逐次执行子查询,如果外层表是一个很大的表,我们可以想象查询性能会表现比这个更加糟糕。
Select* from A where exists (select * from B where id>=3000 and A.uuid=B.uuid);
复制代码
执行时间:2s左右
正例
Select* from A inner join B ON A.uuid=B.uuid where b.uuid>=3000; 这个语句执行测试不到一秒;
复制代码
执行时间:1s不到
- 第三:使用JOIN时候,应该用小的结果驱动大的结果
left join 左边表结果尽量小,如果有条件应该放到左边先处理,right join同理反向。如:
反例
Select * from A left join B A.id=B.ref_id where A.id>10
复制代码
正例
select * from (select * from A wehre id >10) T1 left join B on T1.id=B.ref_id;
复制代码
13.exist 代替 in
反例
SELECT * from A WHERE id in ( SELECT id from B )
复制代码
正例
SELECT * from A WHERE id EXISTS ( SELECT 1 from A.id= B.id )
复制代码
分析:
in 是在内存中遍历比较
exist 需要查询数据库,所以当B的数据量比较大时,exists效率优于in**
in()只执行一次,把B表中的所有id字段缓存起来,之后检查A表的id是否与B表中的id相等,如果id相等则将A表的记录加入到结果集中,直到遍历完A表的所有记录。
In 操作的流程原理如同一下代码
List resultSet={};
Array A=(select * from A);
Array B=(select id from B);
for(int i=0;i<A.length;i++) {
for(int j=0;j<B.length;j++) {
if(A[i].id==B[j].id) {
resultSet.add(A[i]);
break;
}
}
}
return resultSet;
复制代码
可以看出,当B表数据较大时不适合使用in(),因为会把B表数据全部遍历一次
如:A表有10000条记录,B表有1000000条记录,那么最多有可能遍历10000*1000000次,效率很差。
再如:A表有10000条记录,B表有100条记录,那么最多有可能遍历10000*100次,遍历次数大大减少,效率大大提升。
结论:in()适合B表比A表数据小的情况
exist()会执行A.length()次,执行过程代码如下
List resultSet={};
Array A=(select * from A);
for(int i=0;i<A.length;i++) {
if(exists(A[i].id) { //执行select 1 from B where B.id=A.id是否有记录返回
resultSet.add(A[i]);
}
}return resultSet;
复制代码
当B表比A表数据大时适合使用exists(),因为它没有那么多遍历操作,只需要再执行一次查询就行。
如:A表有10000条记录,B表有1000000条记录,那么exists()会执行10000次去判断A表中的id是否与B表中的id相等。
如:A表有10000条记录,B表有100000000条记录,那么exists()还是执行10000次,因为它只执行A.length次,可见B表数据越多,越适合exists()发挥效果。
再如:A表有10000条记录,B表有100条记录,那么exists()还是执行10000次,还不如使用in()遍历10000*100次,因为in()是在内存里遍历比较,而exists()需要查询数据库,
我们都知道查询数据库所消耗的性能更高,而内存比较很快。
结论:exists()适合B表比A表数据大的情况
干货!SQL性能优化,书写高质量SQL语句的更多相关文章
- SQL Server SQL性能优化之--通过拆分SQL提高执行效率,以及性能高低背后的原因
复杂SQL拆分优化 拆分SQL是性能优化一种非常有效的方法之一, 具体就是将复杂的SQL按照一定的逻辑逐步分解成简单的SQL,借助临时表,最后执行一个等价的逻辑,已达到高效执行的目的 一直想写一遍通过 ...
- 后端程序员必备:书写高质量SQL的30条建议
前言 本文将结合实例demo,阐述30条有关于优化SQL的建议,多数是实际开发中总结出来的,希望对大家有帮助. 1.查询SQL尽量不要使用select *,而是select具体字段. 反例子: sel ...
- Oracle 11g 中SQL性能优化新特性之SQL性能分析器(SQLPA)
Oracle11g中,真实应用测试选项(the Real Application Testing Option)提供了一个有用的特点,叫SQL性能分析器(SQL Performance Analyze ...
- 书写高质量sql的一些建议
It's better to light a candle than to curse the darkness 老生常谈的不要使用select * 如果硬要使用select *,那么就请忍受一下以下 ...
- SQL性能优化前期准备-清除缓存、开启IO统计
文章来至:https://www.cnblogs.com/Ren_Lei/p/5669662.html 如果需要进行SQl Server下的SQL性能优化,需要准备以下内容: 一.SQL查询分析器设置 ...
- Oracle SQL性能优化技巧大总结
http://wenku.baidu.com/link?url=liS0_3fAyX2uXF5MAEQxMOj3YIY4UCcQM4gPfPzHfFcHBXuJTE8rANrwu6GXwdzbmvdV ...
- SQL性能优化技巧
作者:IT王小二 博客:https://itwxe.com 这里就给小伙伴们带来工作中常用的一些 SQL 性能优化技巧总结,包括常见优化十经验.order by 与 group by 优化.分页查询优 ...
- SQL性能优化案例分析
这段时间做一个SQL性能优化的案例分析, 整理了一下过往的案例,发现一个比较有意思的,拿出来给大家分享. 这个项目是我在项目开展2期的时候才加入的, 之前一期是个金融内部信息门户, 里面有个功能是收集 ...
- ORACLE数据库学习之SQL性能优化详解
Oracle sql 性能优化调整 ...
随机推荐
- Vue 设置class样式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- java 内存操作流
操作内存流的时候(从读取出来,注意一定要把真正的数据用toByteArray或者toCharArray将数据读出来) 之前的文件操作流是以文件的输入输出为主的,当输出的位置变成了内存,那么就称为内存操 ...
- eclipse中maven报错--Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable and mvn script match.
-Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable a ...
- 使用cnpm i -S axios 遇到报错Install fail! Error: EISDIR: illegal operation on a directory, symlink..........的解决办法
“今天本来想在cnpm 环境下安装axios,但是在安装axios的时候出现了一些问题.使用cnpm淘宝镜像库下载安装axios的时候报错 Install fail! Error: EISDIR: i ...
- JS闭包机制实现为DOM元素循环添加事件
HTML代码: <button type='button' class='btn' id='1'>按钮1</button> <button type='button' c ...
- SAPI(PHP常见的四种运行模式)
SAPI(Server Application Programming Interface)服务器应用程序编程接口,即PHP与其他应用交互的接口,PHP脚本要执行有很多方式,通过Web服务器,或者直接 ...
- Wanafly 挑战赛 14 E 无效位置 (线性基+并查集)
Wanafly 挑战赛 14 E 无效位置 (线性基+并查集) 传送门:https://ac.nowcoder.com/acm/contest/81/#question 题意: n个数,m次操作 一个 ...
- wireshark使用心得 centos7安装wireshark: yum install wireshark wireshark-gnome
centos7 安装wireshark 安装 yum install wireshark yum install wireshark-gnome 关于pcap文件的文件解析网上资料有很多,我在这就不说 ...
- java 嵌入式数据库H2
H2作为一个嵌入型的数据库,它最大的好处就是可以嵌入到我们的Web应用中,和我们的Web应用绑定在一起,成为我们Web应用的一部分.下面来演示一下如何将H2数据库嵌入到我们的Web应用中. 一.搭建测 ...
- C语言中的符号总结
1.注释符号 //和/* ...*/ 2.续行符号 \ 3.转义符号 常用:\ ...