这些经常被忽视的SQL错误用法,你有没有踩过坑?
之前已经讲过mysql的性能优化,感兴趣的朋友可以看看之前的文章,《史上最全的MySQL高性能优化实战总结!》。但是有些问题其实是我们自身的SQL语句有问题导致的。今天就来总结哪些经常被我们忽视的SQL错误写法,看看你都踩过哪些坑?
一、LIMIT语句
Limit是分页查询是最常用的场景之一,但也通常也是最容易出问题的地方。比如对于下面简单的语句,一般我们觉得在type, name, create_time字段上加组合索引。这样条件排序都能有效的利用到索引,性能迅速提升。
SELECT *
FROM operation
WHERE type = 'xxx'
AND name = 'xxx'
ORDER BY create_time
LIMIT 1000, 10;
但是当数据量很大的时候,当我们查询最后几页数据时,分页会越来越慢。这就是我们经常碰到的海量数据的分页问题。这是为什么呢?
优化方案
因为数据库也并不知道第1000000条记录从什么地方开始,即使有索引也需要从头计算一次,即进行全表扫描。出现这种性能问题,主要还是我们没有考虑到大量数据的情况。
其实在前端数据浏览翻页时,是可以将上一页的最大值作为查询条件传给后台的。SQL 重新设计如下:
select *
from operation
where id>1000000
AND type = 'xxx'
AND name = 'xxx'
ORDER BY create_time
limit 10
经过这种优化,可以保证系统不会随着数据量的增长而变慢。
二、隐式转换
SQL语句中查询变量和字段定义类型不匹配是另一个常见的错误。比如下面的语句:
explain extended
select *
from my_balance b
where b.bpn = 14000000123
and b.isverified is null;
字段 bpn 的定义为 varchar 类型,而查询条件传入的却是int 类型。MySQL 会将字符串转换为数字之后再比较。函数作用于表字段,导致所以索引失效。如下图所示:
这个坑我们以前也遇见过,花了好半天才发现是这个问题。 所以程序员在开发的过程中,一定要认真仔细,确保查询变量和字段类型匹配。
优化方案
保证传入的参数类型和字段定义的类型一致。
所以,上面的sql语句改为如下即可:
explain extended
select *
from my_balance b
where b.bpn = '14000000123'
and b.isverified is null;
三、关联更新、删除
MySQL5.6之后有个新特性,会自动把SQL语句中的嵌套子查询优化为关联查询(join),所以有些时候你会发现嵌套子查询的效率和关联查询的效率差不多。但是需要特别注意mysql目前仅仅针对查询语句的优化。对于更新或删除需要手工重写成 JOIN。
比如下面 UPDATE 语句,MySQL 实际执行的还是嵌套子查询(DEPENDENT SUBQUERY),其执行时间可想而知。
explain extended
UPDATE operation o
SET status = 'applying'
WHERE o.id IN (SELECT id
FROM (SELECT o.id,o.status
FROM operation o
WHERE o.group = 123
AND o.status NOT IN ('done')
ORDER BY o.parent,o.id
LIMIT 1) t);
执行计划:
优化方案
改为 JOIN 之后,子查询的选择模式从嵌套子查询(DEPENDENT SUBQUERY) 变成了关联查询(DERIVED),执行速度大大加快
UPDATE operation o
JOIN (SELECT o.id,
o.status
FROM operation o
WHERE o.group = 123
AND o.status NOT IN ('done')
ORDER BY o.parent,o.id
LIMIT 1) t
ON o.id = t.id
SET status = 'applying1
执行计划简化为:
四、Order by
MySQL中的两种排序方式:
1、通过有序索引顺序扫描直接返回有序数据,因为索引的结构是B+树,索引中的数据是按照一定顺序进行排列的,所以在排序查询中如果能利用索引,就能避免额外的排序操作。EXPLAIN分析查询时,Extra显示为Using index。
2、Filesort排序,对返回的数据进行排序,所有不是通过索引直接返回排序结果的操作都是Filesort排序,也就是说进行了额外的排序操作。EXPLAIN分析查询时,Extra显示为Using filesort。
优化方案
一般排序的原则就是:尽量减少额外的排序,通过索引直接返回有序数据。
所以我们需要注意以下这些情况:
1、排序字段在多个索引中,无法使用索引排序,查询一次只能使用一个索引:
explain
select store_id,email,last_name
from my_user
order by store_id,email,last_name;
查询计划显示,没有走所以直接返回有序数据,额外排序放回结果:
2、排序字段顺序与索引列顺序不一致,同样也无法利用索引排序。这个就不举例了跟where条件是一样的。
需要注意的是:这些都是细节的东西,经常会在开发过程中忽略。然后SQL就莫名其妙的不走索引了。
五、混合排序
索引中的数据是按照一定顺序进行排列的,所以在排序查询中如果能利用索引直接返回数据,就能避免额外的排序操作。但是如果出现这种混合了升序和降序的情况,MySQL 无法利用索引直接返回排序结果的。
SELECT *
FROM my_order o
INNER JOIN my_appraise a ON a.orderid = o.id
ORDER BY a.is_reply ASC,
a.appraise_time DESC
LIMIT 0, 20
执行计划显示为全表扫描:
优化方案
此类问题根据实际常见优化,原则就是应该避免这种排序的情况。如果确实有多种排序的需求,最好使用其他的方法提升性能。
六、EXISTS语句
MySQL 对待 EXISTS 子句时,会采用嵌套子查询的执行方式。如下面的 SQL 语句:
explain
SELECT *
FROM my_order n
LEFT JOIN my_appraise sra
ON n.id = sra.orderid WHERE 1=1
AND EXISTS(SELECT 1
FROM my_user m
WHERE n.user_id = m.id
AND m.usercode = '111' )
AND n.id <> 5
执行计划为:
优化方案
去掉 exists 更改为 join,能够避免嵌套子查询,这样会大大提高查询效率。
SELECT *
FROM my_neighbor n
LEFT JOIN my_neighbor_apply sra
ON n.id = sra.neighbored
AND sra.user_id = 'xxx'
INNER JOIN message_info m
on n.id = m.neighbor_id
AND m.inuser = 'xxx'
WHERE n.topic_status < 4
AND n.topictype <> 5
新的执行计划显示没有了嵌套子查询:
七、条件下推
外部查询条件不能够下推到复杂的视图或子查询的情况有:
聚合子查询;
含有 LIMIT 的子查询;
UNION 或 UNION ALL 子查询;
输出字段中的子查询;
如下面的语句,从执行计划可以看出其条件作用于聚合子查询之后
SELECT *
FROM(SELECT target,
Count(*)
FROM operation
GROUPBY target) t
WHERE target = 'rm-xxxx'
优化方案
确定从语义上查询条件可以直接下推后,重写如下:
SELECT target,
Count(*)
FROM operation
WHERE target = 'rm-xxxx'
GROUPBY target
执行计划变为:
八、提前缩小数据范围
先上初始 SQL 语句:
SELECT *
FROM my_order o
LEFT JOIN my_userinfo u
ON o.uid = u.uid
LEFT JOIN my_productinfo p
ON o.pid = p.pid
WHERE o.display = 0
AND o.ostaus = 1
ORDER BY o.selltime DESC
LIMIT 0, 15
数为90万,时间消耗为12秒。
优化方案
由于最后 WHERE 条件以及排序均针对最左主表,因此可以先对 my_order 排序提前缩小数据量再做左连接。SQL 重写后如下,执行时间缩小为1毫秒左右。
SELECT *
FROM (SELECT *
FROM my_order o
WHERE o.display = 0
AND o.ostaus = 1
ORDER BY o.selltime DESC LIMIT 0, 15 )o
LEFT JOIN my_userinfo u
ON o.uid = u.uid
LEFT JOIN my_productinfo p
ON o.pid = p.pid
ORDER BY o.selltime DESC
limit 0, 15
再检查执行计划:
子查询物化后(select_type=DERIVED)参与 JOIN,虽然估算行扫描仍然为90万,但是利用了索引以及 LIMIT 子句后,实际执行时间变得很小。
九、中间结果集下推
再来看下面这个已经初步优化过的例子(左连接中的主表优先作用查询条件):
SELECT a.*
c.allocated
FROM (
SELECT resourceid
FROM my_distribute d
WHERE isdelete = 0
AND cusmanagercode = '1234567'
ORDER BY salecode limit 20
) a
LEFT JOIN
(
SELECT resourcesid, sum(allocation) allocated
FROM my_resources
GROUP BY resourcesid
) c
ON a.resourceid = c.resourcesid
那么该语句还存在其它问题吗?不难看出子查询 c 是全表聚合查询,在表数量特别大的情况下会导致整个语句的性能下降。
其实对于子查询 c,左连接最后结果集只关心能和主表 resourceid 能匹配的数据。因此我们可以重写语句如下,执行时间大大降低 。
SELECT a.*,
c.allocated
FROM (
SELECT resourceid
FROM my一distribute d
WHERE isdelete = 0
AND cusmanagercode = '1234567*
ORDER BY salecode limit 20) a
LEFT JOIN
(
SELECT resourcesid, sum(ifnull(allocation, 0) * 12345) allocated
FROM my_resources r,
(
SELECT resourceid
FROM my_distribute d
WHERE isdelete = 0
AND cusmanagercode = '1234567'
ORDER BY salecode limit 20
) a
WHERE r.resourcesid = a.resourcesid
GROUP BY resourcesid
) c
ON a.resourceid = c.resourcesid
最后
以上总结了一些sql语句常见的坑。里面很多都是不仔细导致的。只有仔细认真尽量考虑一些大数据的情况,这样才能写出高性能的SQL语句。
同时,程序员在设计数据模型以及编写SQL语句时,要把索引及性能提前考虑进去,这样才能避免后期遇到的一些坑。
这些经常被忽视的SQL错误用法,你有没有踩过坑?的更多相关文章
- 8 种常被忽视的 SQL 错误用法
来源:db匠 yq.aliyun.com/articles/72501 sql语句的执行顺序: FROM <left_table> ON <join_condition> ...
- 8种常被忽视的SQL错误用法
作者:一杯甜酒 原文:https://blog.csdn.net/u012562943/article/details/71403500 sql语句的执行顺序: FROM <left_table ...
- 8种经常被忽视的SQL错误用法,你有没有踩过坑?
1.LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句,一般 DBA 想到的办法是在 type, name, create_time 字段上加组合索引 ...
- 8 种经常被忽视的 SQL 错误用法,你有没有踩过坑?
1.LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句,一般 DBA 想到的办法是在 type, name, create_time 字段上加组合索引 ...
- 8种常被忽视的SQL错误用法,你中招了吗?
前言 MySQL在近几年仍然保持强劲的数据库流行度增长趋势.越来越多的客户将自己的应用建立在 MySQL 数据库之上,甚至是从 Oracle 迁移到 MySQL上来.但也存在部分客户在使用 MySQL ...
- 一直被用错的6种SQL 错误用法
一直被用错的6种SQL 错误用法 1.LIMIT 语句 2.隐式转换 3.关联更新.删除 4.EXISTS语句 5.条件下推 6.提前缩小范围 sql语句的执行顺序: FROM ON JOIN WHE ...
- 8种常见的SQL错误用法
常见SQL错误用法 1. LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句,一般DBA想到的办法是在type, name, create_time字 ...
- MySQL常见的8种SQL错误用法
MySQL常见的8种SQL错误用法 前言 MySQL在2016年仍然保持强劲的数据库流行度增长趋势.越来越多的客户将自己的应用建立在MySQL数据库之上,甚至是从Oracle迁移到MySQL上来.但也 ...
- MySQL · 性能优化 · MySQL常见SQL错误用法(转自-阿里云云栖社区)
作者:阿里云云栖社区链接:https://zhuanlan.zhihu.com/p/26043916来源:知乎著作权归作者所有,转载请联系作者获得授权. 前言 MySQL在2016年仍然保持强劲的数据 ...
随机推荐
- 创建Rdemo项目
1.创建项目工作目录 mkdir /home/sesa464509/R/demo cd /home/sesa464509/R/demo vi sayHello.R ------------------ ...
- [HAOI2012]外星人 题解
人类智慧题. 首先,只有 \(\varphi(1)=\varphi(2)=1\).再考虑题目中给的提示: \[\varphi\left(\prod_{i = 1}^m p_i^{q_i}\right) ...
- 【Java基础上】一、简述Java
一.简述Java Java是一种高级的面向对象的程序语言,在此处,不需要了解什么叫做面向对象,因为后面的文章中自然会谈到这方面的论述.那么,Java就是一个计算机的编程语言. 1.1 Java的历 ...
- 第4天 JavaDoc生成文档&Java流程控制(第一节:用户交互Scanner)
JavaDoc生成文档 javadoc命令是用来生成自己的API文档 参数信息: @author 作者名 @version 版本号 @since 指明需要最早使用的jdk版本 @param 参数名 @ ...
- jquery.autocomplete 使用解析
页面引用 <script type="text/javascript" src="${base}/autocom/jquery-1.8.2.min.js" ...
- 在阿里云上单机部署k8s1.18
系统:CentOS Linux release 8.1.1911 配置主机名 [root@iZwz9e3t4tj14jzewdtvj8Z ~]# hostnamectl set-hostname la ...
- CRC校验原理和verilog实现方法(三)
1 代码生成 verilog实现CRC校验,可以充分发挥FPGA的硬件特性,即并行运算的能力. 具体实现方式,可以参考我上一篇博客,关键是用线性反馈移位寄存器表示出多项式,另外注意校验数据高位在先.然 ...
- ASP.NET Corec初步使用Quartz.NET
一.什么是Quartz.NET? Quartz.NET 是一个功能齐全的开源作业调度系统,可用于从最小的应用程序到大型企业系统. Quartz.NET是纯净的,它是一个.Net程序集,是非常流行的Ja ...
- [GXYCTF2019]Ping Ping Ping(ping命令执行绕过Waf)
记一道ping注入的题.过滤了很多字符. 分析 简单的测了一下,很容易就拿到了flag.php和index.php. 但是存在waf无法直接查看.直接?ip=127.0.0.1|cat flag.ph ...
- IntelliJ IDEA 中为Maven 配置阿里云镜像源
idea中查看位置,应该都是一样的这个位置 在此位置下新建一个.xml文件 内容如下: <?xml version="1.0" encoding="UTF-8&qu ...