写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语句的更多相关文章

  1. SQL Server SQL性能优化之--通过拆分SQL提高执行效率,以及性能高低背后的原因

    复杂SQL拆分优化 拆分SQL是性能优化一种非常有效的方法之一, 具体就是将复杂的SQL按照一定的逻辑逐步分解成简单的SQL,借助临时表,最后执行一个等价的逻辑,已达到高效执行的目的 一直想写一遍通过 ...

  2. 后端程序员必备:书写高质量SQL的30条建议

    前言 本文将结合实例demo,阐述30条有关于优化SQL的建议,多数是实际开发中总结出来的,希望对大家有帮助. 1.查询SQL尽量不要使用select *,而是select具体字段. 反例子: sel ...

  3. Oracle 11g 中SQL性能优化新特性之SQL性能分析器(SQLPA)

    Oracle11g中,真实应用测试选项(the Real Application Testing Option)提供了一个有用的特点,叫SQL性能分析器(SQL Performance Analyze ...

  4. 书写高质量sql的一些建议

    It's better to light a candle than to curse the darkness 老生常谈的不要使用select * 如果硬要使用select *,那么就请忍受一下以下 ...

  5. SQL性能优化前期准备-清除缓存、开启IO统计

    文章来至:https://www.cnblogs.com/Ren_Lei/p/5669662.html 如果需要进行SQl Server下的SQL性能优化,需要准备以下内容: 一.SQL查询分析器设置 ...

  6. Oracle SQL性能优化技巧大总结

    http://wenku.baidu.com/link?url=liS0_3fAyX2uXF5MAEQxMOj3YIY4UCcQM4gPfPzHfFcHBXuJTE8rANrwu6GXwdzbmvdV ...

  7. SQL性能优化技巧

    作者:IT王小二 博客:https://itwxe.com 这里就给小伙伴们带来工作中常用的一些 SQL 性能优化技巧总结,包括常见优化十经验.order by 与 group by 优化.分页查询优 ...

  8. SQL性能优化案例分析

    这段时间做一个SQL性能优化的案例分析, 整理了一下过往的案例,发现一个比较有意思的,拿出来给大家分享. 这个项目是我在项目开展2期的时候才加入的, 之前一期是个金融内部信息门户, 里面有个功能是收集 ...

  9. ORACLE数据库学习之SQL性能优化详解

                                                                                    Oracle  sql 性能优化调整 ...

随机推荐

  1. 【codeforces 764B】Timofey and cubes

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  2. 【u232】围棋游戏

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 为了增强幼儿园小朋友的数数能力,小虎老师给了一个家庭游戏作业.让小虎那一块空的围棋盘,随机在一些方格中 ...

  3. urlencode()与urldecode()

    urlencode()函数原理就是首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%. urldecode()函数与urlencode()函数原理相反,用于解码已编码的 URL 字符串,其 ...

  4. Chrome 里的请求报错 " Provisional headers are shown"

    之所以会出现这个警告,是因为去获取该资源的请求其实并(还)没有真的发生; 背景:提交表单,按钮点击<button>标签,触发事件,ajax发送请求,服务器返回信息; <button& ...

  5. vue基于 element-ui 实现菜单动画效果,任意添加 li 个数均匀撑满 ul 宽度

    %)%)%%%))) .) .) .) .) .) .) .) .) .) .) .) .) .) .) .) .% %% %deg);}

  6. dotnet 如何调试某个文件是哪个代码创建

    我发现了自己的软件,会在桌面创建一个 1.txt 文件,但是我不知道是哪个代码创建的,那么如何进行快速的调试找到是哪个代码创建的 最简单的方法是使用 VisualStudio 全局搜 1.txt 看是 ...

  7. mysql5.7密码修改与报错分析

    1.修改密码 修改密码: vim /etc/my.cnf 的mysqld字段加入skip-grant-tables 重启MySQL,service mysqld restart 终端输入 mysql ...

  8. 前端——JS

    目录 JavaScript概述 ECMAScript和JavaScript的关系 ECMAScript的历史 JavaScript引入方式 Script标签内写代码 引入额外的JS文件 JavaScr ...

  9. Java面向对象程序设计第8章3-5

    Java面向对象程序设计第8章3-5 3.String类型有什么特点? 一旦赋值,便不能更改其指向的字符对象 如果更改,则会指向一个新的字符对象 不能为null 4.String什么时候进行值比较,什 ...

  10. Python_全局变量的定义

    1.在my套件下新建一个关键字systemkey并进行脚本的编写:创建一个${var1}变量,并赋值为aaaaaaaaaa Set Global Variable        ${var1}    ...