1.int型子查询陷阱

如下两个表:

mysql> desc user;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(22) | YES | | NULL | |
| class_id | int(11) | YES | MUL | NULL | |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.17 sec) mysql> desc class;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| class_id | int(11) | NO | PRI | NULL | auto_increment |
| class_name | varchar(20) | YES | | 000 | |
+------------+-------------+------+-----+---------+----------------+
2 rows in set (0.15 sec)

2表的数据量:

mysql> select count(id) from user;
+-----------+
| count(id) |
+-----------+
| 1120013 |
+-----------+
1 row in set (2.50 sec) mysql> select count(class_id) from class;
+-----------------+
| count(class_id) |
+-----------------+
| 5 |
+-----------------+
1 row in set (0.10 sec)

     要求查询班级名称是'3'的学生?

1.用in查询:

mysql> select id,name from user where class_id in (select class_id from class wh
ere class_name='')
-> ;
+-----+----------+
| id | name |
+-----+----------+
| 3 | QLQ3 |
| 9 | QLQ9 |
| 10 | QLQ10 |
| 12 | QLQ12 |
| 13 | QLQ13 |
| 102 | name2100 |
| 103 | name3100 |
| 104 | name4100 |
| 5 | QLQ5 |
+-----+----------+
9 rows in set (0.17 sec)

花费了0.17秒,用explain分析语句:发现也是class表是全局扫描,user表用了class_id索引。

mysql> explain select id,name from user where class_id in (select class_id from
class where class_name='')\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: class
partitions: NULL
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 5
filtered: 20.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: user
partitions: NULL
type: ref
possible_keys: class_id
key: class_id
key_len: 5
ref: maven.class.class_id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.10 sec)

  解释:我们理解的是先查询in里面的班级编号,然后利用班级编号去查询外面的id和name。两个表都是全表扫描。

2.改为连接查询:

mysql> SELECT
-> u.id,
-> u.name
-> FROM USER AS u
-> INNER JOIN (SELECT
-> class_id
-> FROM class
-> WHERE class_name = '') AS c
-> ON c.class_id = u.class_id;
+-----+----------+
| id | name |
+-----+----------+
| 3 | QLQ3 |
| 9 | QLQ9 |
| 10 | QLQ10 |
| 12 | QLQ12 |
| 13 | QLQ13 |
| 102 | name2100 |
| 103 | name3100 |
| 104 | name4100 |
| 5 | QLQ5 |
+-----+----------+
9 rows in set (0.11 sec)

0.11秒

分析上面语句:发现也是class表是全局扫描,user表用了class_id索引。

mysql> EXPLAIN
-> SELECT
-> u.id,
-> u.name
-> FROM USER AS u
-> INNER JOIN (SELECT
-> class_id
-> FROM class
-> WHERE class_name = '') AS c
-> ON c.class_id = u.class_id \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: class
partitions: NULL
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 5
filtered: 20.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: u
partitions: NULL
type: ref
possible_keys: class_id
key: class_id
key_len: 5
ref: maven.class.class_id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.10 sec)

总结:

    也就是说这个mysql5.7的in查询与我们想的一样,就是先查询里面的东西,然后利用里面查到的值去外面查询,所以外面的查询可以用到索引。

网上总结的经验如下:

题: 在ecshop商城表中,查询6号栏目的商品, (注,6号是一个大栏目)

  最直观的: mysql> select goods_id,cat_id,goods_name from goods where cat_id in (select cat_id from ecs_category where parent_id=6);

误区: 给我们的感觉是, 先查到内层的6号栏目的子栏目,如7,8,9,11

  然后外层, cat_id in (7,8,9,11)

事实: 如下图, goods表全扫描, 并逐行与category表对照,看parent_id=6是否成立

原因: mysql的查询优化器,针对In型做优化,被改成了exists的执行效果.

  当goods表越大时, 查询速度越慢.

改进: 用连接查询来代替子查询

explain select goods_id,g.cat_id,g.goods_name from goods as g

inner join (select cat_id from ecs_category where parent_id=6) as t

using(cat_id) \G

  内层 select cat_id from ecs_category where parent_id=6 ; 用到Parent_id索引, 返回4行

网上总结的经验是 in 查询不是我们想的那样,而是逐行扫描外层的数据去匹配里面的查询,因此查询效率低下。改进方法是利用inner  join改进。

      网上总结:也就是in不走索引?待思考的问题。。。。。。。。。。。。

        到底是我的实践是错的?还是因为mysql版本问题?

2 exists子查询    用法参考http://www.cnblogs.com/qlqwjy/p/8598091.html

  exists只是返回一个ture或false的结果(这也是为什么子查询里是select 'x'的原因 当然也可以select任何东西) 也就是它只在乎括号里的数据能不能查找出来,是否存在这样的记录。

题: 查询有商品的栏目.

按上面的理解,我们用join来操作,如下:

mysql> select c.cat_id,cat_name from ecs_category as c inner join goods as g

on c.cat_id=g.cat_id group by cat_name;

exists简单用法:

1.  查询id为5的数据:  (数据存在)

    SELECT *
FROM class AS c1
WHERE EXISTS(SELECT
class_id
FROM class AS c2
WHERE c1.class_id = 5);

  如果exists里面返回的结果行数大于1,则返回true,则外面的查询数据可以返回。

2.  查询id为10的数据:  (数据不存在)

    SELECT *
FROM class AS c1
WHERE EXISTS(SELECT
class_id
FROM class AS c2
WHERE c1.class_id = 10);

  因为exsits始终返回的是false,所以外层查询始终无效,也就不会产生数据。

自己的实践:in  用到了索引,exists没用到索引。。。。。。。。。。。。。

mysql> explain select id,name from user as u where exists(select class_id from c
lass as c where c.class_id=u.class_id and c.class_name='')\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: u
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1116336
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: c
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: maven.u.class_id
rows: 1
filtered: 20.00
Extra: Using where
2 rows in set, 2 warnings (0.00 sec) mysql> explain select id,name from user as u where class_id in(select class_id f
rom class as c where c.class_id=u.class_id and c.class_name='')\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: c
partitions: NULL
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 5
filtered: 20.00
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: u
partitions: NULL
type: ref
possible_keys: class_id
key: class_id
key_len: 5
ref: maven.c.class_id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 2 warnings (0.00 sec)

3.优化规则:

优化1: 在group时, 用带有索引的列来group, 速度会稍快一些,另外,用int型 比 char型 分组,也要快一些.

优化2: 在group时, 我们假设只取了A表的内容,group by 的列,尽量用A表的列,会比B表的列要快.

优化3: 从语义上去优化

  select cat_id,cat_name from ecs_category where exists(select *from goods where goods.cat_id=ecs_category.cat_id)

优化4:from 型子查询:

  注意::内层from语句查到的临时表, 是没有索引的.所以: from的返回内容要尽量少.

优化5:min()   max()优化

  例如:如下表结构:

mysql> show create table user\G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` int(11) NOT NULL,
`name` varchar(22) DEFAULT NULL,
`class_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> select count(id) from user;
+-----------+
| count(id) |
+-----------+
| 1120013 |
+-----------+
1 row in set (2.32 sec)

  现在查询班级编号为5的最小的id:

mysql> select min(id) from user where class_id=5;
+---------+
| min(id) |
+---------+
| 5 |
+---------+
1 row in set (2.37 sec) mysql> explain select min(id) from user where class_id=5\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: user
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1116336
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

  分析之后是全表扫描,需要扫描11W多行,因为class_id没有索引。

优化思路:

  (1)在class_id上加上索引可以加快查询思路

  (2)在不加索引的情况下优化:

    

    试想 id是有顺序的,(默认索引是升续排列), 因此,如果我们沿着id的索引方向走,那么 第1个 pclass_id=5的索引结点,他的id就正好是最小的id.

mysql> explain select min(id) from user where class_id=5\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: user
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1116336
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec) mysql> select id from user use index(primary) where class_id=5 limit 1;
+----+
| id |
+----+
| 5 |
+----+
1 row in set (0.10 sec) mysql> explain select id from user use index(primary) where class_id=5 limit 1\G *************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: user
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1116336
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

  虽然说也没用到索引,可是扫描会在扫描到第一个就停下来,因此扫描的实际数据比上面少,因此查询速度快。

  总结:min,max中如果where条件没有索引可以使查询沿着有索引的方向走,找到第一个数据后停下来即满足条件。

优化6:  count()优化

  

1:myisam的count()非常快

答: 是比较快,.但仅限于查询表的”所有行”比较快, 因为Myisam对行数进行了存储.一旦有条件的查询, 速度就不再快了.尤其是where条件的列上没有索引.

  

2: 假如,id<100的商家都是我们内部测试的,我们想查查真实的商家有多少?

select count(*) from lx_com where id>=100; (1000多万行用了6.X秒)

小技巧:

select count(*) from lx_com; 快

select count(*) from lx_com where id<100; 快

select count(*) frol lx_com -select count(*) from lx_com where id<100; 快

select (select count(*) from lx_com) - (select count(*) from lx_com where id<100)

 优化7:巧用变量

  1:用变量排名

表结构如下:

mysql> show create table test\G
*************************** 1. row ***************************
Table: test
Create Table: CREATE TABLE `test` (
`name` varchar(5) DEFAULT NULL,
`score` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

表数据:

mysql> select * from test;
+------+-------+
| name | score |
+------+-------+
| qlq1 | 100 |
| qlq2 | 100 |
| qlq3 | 90 |
| qlq4 | 91 |
| ql5q | 91 |
+------+-------+
5 rows in set (0.00 sec)

打印名次。利用变量计算。

mysql> set @curr_cnt := 0,@prev_cnt := 0, @rank := 0;
Query OK, 0 rows affected (0.00 sec) mysql> select name,(@curr_cnt:=score) as score,@rank=if(@curr_cnt<>@prev_cnt,@ra
nk+1,@rank) as rank,@prev_cnt:=score from test order by score desc;
+------+-------+------+------------------+
| name | score | rank | @prev_cnt:=score |
+------+-------+------+------------------+
| qlq1 | 100 | 0 | 100 |
| qlq2 | 100 | 1 | 100 |
| qlq4 | 91 | 0 | 91 |
| ql5q | 91 | 1 | 91 |
| qlq3 | 90 | 0 | 90 |
+------+-------+------+------------------+
5 rows in set (0.00 sec)

  

  2.用变量计算真正影响的行数:

表结构:(name是主键)

mysql> show create table test\G
*************************** 1. row ***************************
Table: test
Create Table: CREATE TABLE `test` (
`name` varchar(5) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

这时候我们插入一个重复的主键,主键重复之后修改值,将分数减一:

mysql> insert into test values('qlq1',99) on DUPLICATE KEY update score=score-1;

Query OK, 2 rows affected (0.15 sec)
mysql> insert into test values('qlq1',99),('qlq2','') on DUPLICATE KEY update s
core=score-1;
Query OK, 4 rows affected (0.13 sec)
Records: 2 Duplicates: 2 Warnings: 0

上面显示影响的是两行,因为增、修改,但实际修改的是一行。这时候如果我们想知道实际修改的行数:(如下)

mysql> set @x:=0;
Query OK, 0 rows affected (0.00 sec) mysql> insert into test values('qlq1',99),('qlq2','') on DUPLICATE KEY update s
core=score-1+(@x:=@x+1)*0;
Query OK, 4 rows affected (0.15 sec)
Records: 2 Duplicates: 2 Warnings: 0 mysql> select @x;
+------+
| @x |
+------+
| 2 |
+------+
1 row in set (0.00 sec)

   通过变量可以标识实际影响的行数为2行。

 3.简化union

例如两个表数据:

mysql> select * from test;
+------+-------+
| name | score |
+------+-------+
| qlq1 | 97 |
| qlq2 | 98 |
| qlq3 | 90 |
| qlq4 | 91 |
| ql5q | 91 |
+------+-------+
5 rows in set (0.00 sec) mysql> select * from test2;
+------+-------+
| name | score |
+------+-------+
| qlq1 | 100 |
| qlq5 | 100 |
| qlq3 | 90 |
+------+-------+
3 rows in set (0.00 sec)

采用union取不重复的数据:(all不会去重)

mysql> select * from test union select * from test2;
+------+-------+
| name | score |
+------+-------+
| qlq1 | 97 |
| qlq2 | 98 |
| qlq3 | 90 |
| qlq4 | 91 |
| ql5q | 91 |
| qlq1 | 100 |
| qlq5 | 100 |
+------+-------+
7 rows in set (0.00 sec)

 explain分析上面语句:

mysql> explain select * from test union select * from test2\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: test
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: UNION
table: test2
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 3
filtered: 100.00
Extra: NULL
*************************** 3. row ***************************
id: NULL
select_type: UNION RESULT
table: <union1,2>
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Using temporary
3 rows in set, 1 warning (0.01 sec)

如何利用变量让后半句select不执行 ,也就是只查询一个表,两个表都有数据但是我们想让他取一个表;

set @find:=0;

select name,score,(@find := 1) from test where name='qlq2'

union

select name,score,(@find := 1) from test2 where name='qlq2'  and (@find <= 0)

union 

select 1,1,@find:=0 from test where 0;

  解释:先让find变量置为0,如果name=qlq2在test表找到数据,name会将find置为1,此时不会进入第二个select;如果第一个没有查到,会走到第二个select并查询,查到之后将find置为1,第三个select纯粹是为了将find置为0.

结果:

+------+-------+--------------+
| name | score | (@find := 1) |
+------+-------+--------------+
| qlq2 | 98 | 1 |
+------+-------+--------------+
1 row in set (0.00 sec)

【mysql优化】语句优化的更多相关文章

  1. 自制小工具大大加速MySQL SQL语句优化(附源码)

    引言 优化SQL,是DBA常见的工作之一.如何高效.快速地优化一条语句,是每个DBA经常要面对的一个问题.在日常的优化工作中,我发现有很多操作是在优化过程中必不可少的步骤.然而这些步骤重复性的执行,又 ...

  2. php面试专题---MYSQL查询语句优化

    php面试专题---MYSQL查询语句优化 一.总结 一句话总结: mysql的性能优化包罗甚广: 索引优化,查询优化,查询缓存,服务器设置优化,操作系统和硬件优化,应用层面优化(web服务器,缓存) ...

  3. MYSQL查询语句优化

    mysql的性能优化包罗甚广: 索引优化,查询优化,查询缓存,服务器设置优化,操作系统和硬件优化,应用层面优化(web服务器,缓存)等等.这里的记录的优化技巧更适用于开发人员,都是从网络上收集和自己整 ...

  4. 【转】mysql in语句优化

    mysql会对sql语句做优化, in 后面的条件不超过一定数量仍然会使用索引.mysql 会根据索引长度和in后面条件数量判断是否使用索引. 另外,如果是in后面是子查询,则不会使用索引. 一个文章 ...

  5. MYSQL SQL语句优化

    1.EXPLAIN 做MySQL优化,我们要善用EXPLAIN查看SQL执行计划. 下面来个简单的示例,标注(1.2.3.4.5)我们要重点关注的数据: type列,连接类型.一个好的SQL语句至少要 ...

  6. select * 为什么不好? limit 1 为什么好? --mysql SQL语句优化

    问题一: Select * from  student;  这种语句不好 我的理解:根据Innode存储引擎以及网上的各种资料所说的innodb的B+树索引结构可以分析出,当在非聚集索引列上搜索若用s ...

  7. MySQL - SQL语句优化方法

    1.使用 show status 了解各种 SQL 的执行频率 mysql> show status like 'Com%'; 该命令可以查询 sql 命令的执行次数. 2.定位执行效率较低的 ...

  8. MySQL 查询语句优化思路

    query 语句的优化思路和原则主要提现在以下几个方面:1. 优化更需要优化的Query:2. 定位优化对象的性能瓶颈:3. 明确的优化目标:4. 从 Explain 入手:5. 多使用profile ...

  9. 【转】Mysql查询语句优化策略

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索 ...

  10. MySQL基本语句优化10个原则

    在数据库应用中,程序员们通过不断的实践总结了很多经验,这些经验是一些普遍适用的规则.每一个程序员都应该了解并记住它们,在构造SQL语句时,养成良好的习惯.以下10条比较重要的原则供大家参考. 原则1: ...

随机推荐

  1. JDBC剖析篇(2):JDBC之PreparedStatement

    一次有人问我为什么要使用JDBC中的PreparedStatement,我说可以“防止SQL注入”,其他的却不能说出个一二三,现在来看看其中的秘密 参考文章: http://www.jb51.net/ ...

  2. js学习日记-new Object和Object.create到底干了啥

    function Car () { this.color = "red"; } Car.prototype.sayHi=function(){ console.log('你好') ...

  3. Google序列化库FlatBuffers 1.1发布,及与protobuf的比较

    个人总结: FlatBuffer相对于Protobuffer来讲,优势如下: 1. 由于省去了编解码的过程,所以从速度上快于Protobuffer,个人测试结果100w次编解码,编码上FlatBuff ...

  4. Viewer.js 图片预览插件使用

    一.简介 Viewer.js 是一款强大的图片查看器. Viewer.js 有以下特点: 支持移动设备触摸事件 支持响应式 支持放大/缩小 支持旋转(类似微博的图片旋转) 支持水平/垂直翻转 支持图片 ...

  5. 【SpringCloud】第二篇: 服务消费者(rest+ribbon)

    前言: 必需学会SpringBoot基础知识 简介: spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选. ...

  6. Java 无法初始化Connection的问题

    通过断点调试捕获错误消息:The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time ...

  7. Mac上利用Aria2加速百度网盘下载

    百度网盘下载东西的速度那叫一个慢,特别是大文件,看着所需时间几个小时以上,让人很不舒服,本文记录自己在mac上利用工具Aria2加速的教程,windows下思路也是一样! 科普(可以不看) 这里顺带科 ...

  8. StrutsResultSupport的使用

    在有特殊情况时:如果没有异常信息,但是有错误并且有错误信息等内容:此时也需要进行友好的错误处理的话,那么可以借助StrutsResultSupport 返回结果类型来实现特定处理.此种方式先需要继承S ...

  9. 附录A培训实习生-面向对象基础构造方法和带参数的构造方法(2)

    构造方法,又叫构造函数,其实就是对类进行实例化.构造方法与类同名,无返回值,也不需要void,在new时候调用.也就是说,就是调用构造方法的时候. 所有类都有构造方法,如果你不编码则系统默认生成空的的 ...

  10. BZOJ4591 SHOI2015超能粒子炮·改(卢卡斯定理+数位dp)

    注意到模数很小,容易想到使用卢卡斯定理,即变成一个2333进制数各位组合数的乘积.对于k的限制容易想到数位dp.可以预处理一发2333以内的组合数及组合数前缀和,然后设f[i][0/1]为前i位是否卡 ...