sql优化经典例子
场景
我用的数据库是mysql5.6,下面简单的介绍下场景
课程表
create table Course( c_id int PRIMARY KEY, name varchar(10) )
数据100条
学生表:
create table Student( id int PRIMARY KEY, name varchar(10) )
数据70000条
学生成绩表SC
CREATE table SC( sc_id int PRIMARY KEY, s_id int, c_id int, score int )
数据70w条
查询目的:
查找语文考100分的考生
查询语句:
select s.* from Student s where s.s_id in (select s_id from SC sc where sc.c_id = 0 and sc.score = 100 )
执行时间:30248.271s
晕,为什么这么慢,先来查看下查询计划:
EXPLAIN select s.* from Student s where s.s_id in (select s_id from SC sc where sc.c_id = 0 and sc.score = 100 )
发现没有用到索引,type全是ALL,那么首先想到的就是建立一个索引,建立索引的字段当然是在where条件的字段。
先给sc表的c_id和score建个索引
CREATE index sc_c_id_index on SC(c_id);
CREATE index sc_score_index on SC(score);
再次执行上述查询语句,时间为: 1.054s
快了3w多倍,大大缩短了查询时间,看来索引能极大程度的提高查询效率,看来建索引很有必要,很多时候都忘记建
索引了,数据量小的的时候压根没感觉,这优化感觉挺爽。
但是1s的时间还是太长了,还能进行优化吗,仔细看执行计划:
查看优化后的sql:
SELECT
`YSB`.`s`.`s_id` AS `s_id`,
`YSB`.`s`.`name` AS `name`
FROM
`YSB`.`Student` `s`
WHERE
< in_optimizer > (
`YSB`.`s`.`s_id` ,< EXISTS > (
SELECT
1
FROM
`YSB`.`SC` `sc`
WHERE
(
(`YSB`.`sc`.`c_id` = 0)
AND (`YSB`.`sc`.`score` = 100)
AND (
< CACHE > (`YSB`.`s`.`s_id`) = `YSB`.`sc`.`s_id`
)
)
)
)
补充:这里有网友问怎么查看优化后的语句
方法如下:
在命令窗口执行
有type=all
按照我之前的想法,该sql的执行的顺序应该是先执行子查询
select s_id from SC sc where sc.c_id = 0 and sc.score = 100
耗时:0.001s
得到如下结果:
然后再执行
select s.* from Student s where s.s_id in(7,29,5000)
耗时:0.001s
这样就是相当快了啊,Mysql竟然不是先执行里层的查询,而是将sql优化成了exists子句,并出现了EPENDENT SUBQUERY,
mysql是先执行外层查询,再执行里层的查询,这样就要循环70007*11=770077次。
那么改用连接查询呢?
SELECT s.* from Student s INNER JOIN SC sc on sc.s_id = s.s_id where sc.c_id=0 and sc.score=100
这里为了重新分析连接查询的情况,先暂时删除索引sc_c_id_index,sc_score_index
执行时间是:0.057s
效率有所提高,看看执行计划:
这里有连表的情况出现,我猜想是不是要给sc表的s_id建立个索引
CREATE index sc_s_id_index on SC(s_id);
show index from SC
在执行连接查询
时间: 1.076s,竟然时间还变长了,什么原因?查看执行计划:
优化后的查询语句为:
SELECT
`YSB`.`s`.`s_id` AS `s_id`,
`YSB`.`s`.`name` AS `name`
FROM
`YSB`.`Student` `s`
JOIN `YSB`.`SC` `sc`
WHERE
(
(
`YSB`.`sc`.`s_id` = `YSB`.`s`.`s_id`
)
AND (`YSB`.`sc`.`score` = 100)
AND (`YSB`.`sc`.`c_id` = 0)
)
貌似是先做的连接查询,再执行的where过滤
回到前面的执行计划:
这里是先做的where过滤,再做连表,执行计划还不是固定的,那么我们先看下标准的sql执行顺序:
正常情况下是先join再where过滤,但是我们这里的情况,如果先join,将会有70w条数据发送join做操,因此先执行where
过滤是明智方案,现在为了排除mysql的查询优化,我自己写一条优化后的sql
SELECT
s.*
FROM
(
SELECT
*
FROM
SC sc
WHERE
sc.c_id = 0
AND sc.score = 100
) t
INNER JOIN Student s ON t.s_id = s.s_id
即先执行sc表的过滤,再进行表连接,执行时间为:0.054s
和之前没有建s_id索引的时间差不多
查看执行计划:
先提取sc再连表,这样效率就高多了,现在的问题是提取sc的时候出现了扫描表,那么现在可以明确需要建立相关索引
CREATE index sc_c_id_index on SC(c_id);
CREATE index sc_score_index on SC(score);
再执行查询:
SELECT
s.*
FROM
(
SELECT
*
FROM
SC sc
WHERE
sc.c_id = 0
AND sc.score = 100
) t
INNER JOIN Student s ON t.s_id = s.s_id
执行时间为:0.001s,这个时间相当靠谱,快了50倍
执行计划:
我们会看到,先提取sc,再连表,都用到了索引。
那么再来执行下sql
SELECT s.* from Student s INNER JOIN SC sc on sc.s_id = s.s_id where sc.c_id=0 and sc.score=100
执行时间0.001s
执行计划:
这里是mysql进行了查询语句优化,先执行了where过滤,再执行连接操作,且都用到了索引。
总结:
1.mysql嵌套子查询效率确实比较低
2.可以将其优化成连接查询
3.建立合适的索引
4.学会分析sql执行计划,mysql会对sql进行优化,所以分析执行计划很重要
由于时间问题,这篇文章先写到这里,后续再分享其他的sql优化经历。
sql优化经典例子
sql优化经典例子的更多相关文章
- SQL优化 · 经典案例 · 索引篇
Introduction 在这些年的工作之中,由于SQL问题导致的数据库故障层出不穷,下面将过去六年工作中遇到的SQL问题总结归类,还原问题原貌,给出分析问题思路和解决问题的方法,帮助用户在使用数据库 ...
- SQL优化基础 使用索引(一个小例子)
按照本文操作和体会,会对sql优化有个基本最简单的了解,其他深入还需要更多资料和实践的学习: 1. 建表: 复制代码代码如下: create table site_user ( id int IDEN ...
- oracle 11g亿级复杂SQL优化一例(数量级性能提升)
自从16年之后,因为工作原因,项目中就没有再使用oracle了,最近最近支持一个项目,又要开始负责这块事情了.最近在跑性能测试,配置全部调好之后,不少sql还存在性能低下的问题,主要涉及执行计划的不合 ...
- 性能优化之永恒之道(实时sql优化vs业务字段冗余vs离线计算)
在项目中,随着时间的推移,数据量越来越大,程序的某些功能性能也可能会随之下降,那么此时我们不得不需要对之前的功能进行性能优化.如果优化方案不得当,或者说不优雅,那可能将对整个系统产生不可逆的严重影响. ...
- sql优化方法学习和总结
首先要问自己几个问题: 哪些类型的sql会散发出坏味道? sql优化的基本原理是什么,为什么有的sql快有的慢? sql优化和底层的存储引擎关系大么? 怎么看执行过程? 优化建议 1. 缓存查询,sq ...
- 《收获,不止SQL优化》读书笔记
整体性能分析 AWR.ASH.ADDM.AWRDD 整体分析调优工具 AWR:关注数据库的整体性能的报告: ASH:数据库中的等待事件与哪些SQL具体对应的报告: ADDM:oracle给出的一些建议 ...
- 工作中遇到的99%SQL优化,这里都能给你解决方案
前几篇文章介绍了mysql的底层数据结构和mysql优化的神器explain.后台有些朋友说小强只介绍概念,平时使用还是一脸懵,强烈要求小强来一篇实战sql优化,经过周末两天的整理和总结,sql优化实 ...
- sql 优化
1.选择最有效率的表名顺序(只在基于规则的优化器中有效): oracle的解析器按照从右到左的顺序处理 from 子句中的表名,from子句中写在最后的表(基础表driving table)将被最先处 ...
- SQL 优化总结
SQL 优化总结 (一)SQL Server 关键的内置表.视图 1. sysobjects SELECT name as '函数名称',xtype as XType FROM s ...
随机推荐
- 【TMF eTOM】eTOM的概念和术语
eTOM的概念 为了有效地理解和使用eTOM业务流程框架,我们首先要理解构成eTOM的关键概念.这些概念使eTOM成为集成业务流程设计/评估与传统过程的一个非常有效的工具.在这些概念中使用了在本文中详 ...
- return 、break和continue的区别和作用
1.return关键字并不是专门用于跳出循环的,return的功能是结束一个方法. 一旦在循环体内执行到一个return语句,return语句将会结束该方法,循环自然也随之结束.与continue和b ...
- gitlab 502解决方案
gitlab突然出现502 尝试gitlab-ctl restart.gitlab-ctl reconfigure 无效. 之后做法: #查看git日志 gitlab-ctl tail发现日志报错:端 ...
- mysql失效的几种情况
1.如果查询条件中有or,即使查询的条件中带有索引也会失效,如果想使用or,又不想让索引失效,只能将or条件中的所有列都加上索引 2.like 查询一%开头用不上索引, 3.隐式转换会使索引失效 比如 ...
- CF 980D Perfect Groups(数论)
CF 980D Perfect Groups(数论) 一个数组a的子序列划分仅当这样是合法的:每个划分中的任意两个数乘积是完全平方数.定义a的权值为a的最小子序列划分个数.现在给出一个数组b,问权值为 ...
- uva 1608 不无聊的序列
uva 1608 不无聊的序列 紫书上有这样一道题: 如果一个序列的任意连续子序列中都至少有一个只出现一次的元素,则称这个序列时不无聊的.输入一个n个元素的序列,判断它是不是无聊的序列.n<=2 ...
- 防止表单submit或按钮button多次连续点击提交
如上例子:当我点击提交按钮触发submitQuartz()函数 防止用户连续点击提交操作 方法一:获取当时点击时间,根据时间差判断 $scope.submitQuartz=function () { ...
- 基于CentOS系统下的Oracle的安装
背景 最近的数据库的实验课,要求利用虚拟机安装CentOS系统,并在此系统上安装Oracle_11g软件实现监听,在windows系统上安装SQL Developer软件作为客户端 ,从而可以在SQL ...
- CF213E Two Permutations 线段树维护哈希值
当初竟然看成子串了$qwq$,不过老师的$ppt$也错了$qwq$ 由于子序列一定是的排列,所以考虑插入$1$到$m$到$n-m+1$到$n$; 如何判断呢?可以用哈希$qwq$: 我们用线段树维护哈 ...
- 记一次内存溢出java.lang.OutOfMemoryError: unable to create new native thread
一.问题: 春节将至,系统访问量进入高峰期.随之系统出现了异常:java.lang.OutOfMemoryError: unable to create new native thread.在解决这个 ...