性能分析

Explain

使用EXPLAIN关键字可以模拟优化器(不改变查询结果前提下,调整查询顺序,生成执行计划)执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈

功能:

  • 表的读取顺序
  • 哪些索引可以使用
  • 数据读取操作的操作类型
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被物理查询

格式:Explain + SQL语句

执行出来字段如下:

id

select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

  • id相同,执行顺序由上至下
  • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

  • id相同不同,同时存在,id如果相同,可以认为是一组,从上往下顺序执行
    在所有组中,id值越大,优先级越高,越先执行,衍生 = DERIVED(虚拟表)

id号每个号码,表示一趟独立的查询。一个sql 的查询趟数越少越好。

select_type

  • PRIMARY,主查询(最外围);
  • DERIVED,衍生查询(From后的查询,子部分);
  • SIMPLE 简单的 select 查询;
  • SUBQUERY(在SELECT或WHERE列表中包含了子查询);
  • DEPENDENT SUBQUERY(in子查询);
  • 若第二个SELECT出现在UNION之后,则被标记为UNION
    若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED;
  • UNION RESULT,从UNION表获取结果的SELECT;
  • UNCACHEABLE SUBQUREY(子查询用到了系统变量)

table&partitions

table,显示这一行的数据是关于哪张表的

partitions,代表分区表中的命中情况,非分区表,该项为null

type

显示查询使用了何种类型

从最好到最差依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

system>const>eq_ref>ref>range>index>ALL

一般来说,得保证查询至少达到range级别,最好能达到ref

  • system,表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计,@@系统变量就是system,@是在编写存储过程或者触发器程序自定义变量用的

  • const,表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快如将主键置于where列表中,MySQL就能将该查询转换为一个常量

  • eq_ref,唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。

  • ref,非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体

  • range,只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引,一般就是在你的where语句中出现了between、<、>、in等的查询,这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。

  • index,出现index是sql使用了索引但是没用通过索引进行过滤(Where后没用到),一般是使用了覆盖索引或者是利用索引进行了排序分组。

  • all,Full Table Scan,将遍历全表以找到匹配的行

  • index_merge,在查询过程中需要多个索引组合使用,通常出现在有 or 的关键字的sql中

  • ref_or_null,对于某个字段既需要关联条件,也需要null值得情况下。查询优化器会选择用ref_or_null连接查询。

  • index_subquery,利用索引来关联子查询,不再全表扫描。

  • unique_subquery ,该联接类型类似于index_subquery。 子查询中的唯一索引

possible_keys&key&key_len

possible_keys,可以使用的索引,只能选1个。

key,实际使用的索引。如果为NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引和查询的select字段重叠。

key_len,表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。 长度越大越好,定位的数据,key_len字段能够帮你检查是否充分的利用上了索引

如何计算:
1 、先看索引上字段的类型+长度比如 int=4 ; varchar(20) =20 ; char(20) =20
2 、如果是varchar或者char这种字符串字段,视字符集要乘不同的值,比如utf-8,要乘 3,GBK要乘2
3 、varchar这种动态字符串要加2个字节
4、 允许为空的字段要加1个字节

第一组
key_len=age的字节长度+name的字节长度=4+1 + ( 20*3+2)=5+62=67
第二组
key_len=age的字节长度=4+1=5

ref&rows&filtered

ref,显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。

rows,rows列显示MySQL认为它执行查询时必须检查的行数。越少越好

filtered,这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。

Extra

包含不适合在其他列中显示但十分重要的额外信息

  • Using filesort ,指手工排序查询,效率极低,排序的字段,排序字段若通过索引去访问将大大提高排序速度(order by字段没索引,很慢)

  • Using temporary ,未使用索引,效率极低,使用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。

    group by字段没使用索引,超级慢。group by底层包含了order by,所以还有Using filesort

group by字段添加了索引,速度0秒。

  • USING index

    利用索引进行了排序或分组

    表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!
    如果同时出现using where,表明索引被用来执行索引键值的查找;
    如果没有同时出现using where,表明索引只是用来读取数据而非利用索引执行查找。

  • Using where,使用排序的索引字段,效率最佳,表明使用了where过滤

  • using join buffer,效率极低,关联字段未使用索引

  • impossible where,where子句的值总是false,不能用来获取任何元组,条件逻辑有误

  • select tables optimized away,使用优化器,效率很好。

    在没有GROUP BY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT()操作(执行前就确定COUNT()的值了),不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。

查询优化例子

面试题:怎么快速往表中插入一百万数据?

  • 一次Insert插入多条数据的方法:insert into report_batch (report_id, batch_id) values (1, 2),(3, 4)…

  • 事务关闭,插入一百万数据才提交,只提交一次

  • 索引缺点是插入时,需要更改索引。所以插入100w数据可以先删除索引,插入完再创建

  • 多线程

  • mybatis批量处理:

        //批处理@Transactionalpublic void add(List<Item> itemList) {SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH,false);ItemMapper mapper = session.getMapper(ItemMapper.class);for (int i = 0; i < itemList.size(); i++) {mapper.insertSelective(itemList.get(i));if(i%1000==999){//每1000条提交一次防止内存溢出session.commit();session.clearCache();}}session.commit();session.clearCache();}
    
  • sql编程,存储过程

批量数据脚本

建完表,利用sql编程去插入数据,

Redis主从复制,RDB持久化文件,给从节点覆盖执行一次

Mysql主从复制,主机sql写到bin-log,从机读日志文件执行

Mysql主从复制使用函数很容易造成数据不一致问题,比如日期,前后执行时间不一致。所以Mysql一般不允许用户创建函数。但我们又需要sql编程创建函数,就需要修改全局变量设置允许创建函数

show variables like ‘log_bin_trust_function_creators’;

默认关闭,开启如下:

set global log_bin_trust_function_creators=1;

这样添加了参数以后,如果mysqld重启,上述参数又会消失,永久方法:

windows下my.ini[mysqld]加上log_bin_trust_function_creators=1

linux下 /etc/my.cnf下my.cnf[mysqld]加上log_bin_trust_function_creators=1

创建函数

保证每条数据都不同

随机产生字符串

 DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';DECLARE return_str VARCHAR(255) DEFAULT '';DECLARE i INT DEFAULT 0;WHILE i < n DO  SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));  SET i = i + 1;END WHILE;RETURN return_str;
END $$#假如要删除
#drop function rand_string;

随机产生部门编号

#用于随机产生多少到多少的编号
DELIMITER $$
CREATE FUNCTION  rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN   DECLARE i INT DEFAULT 0;  SET i = FLOOR(from_num +RAND()*(to_num -from_num+1))   ;
RETURN i;  END$$ #假如要删除
#drop function rand_num;
创建存储过程

procedure pe si

创建往emp表中插入数据的存储过程

DELIMITER $$
CREATE PROCEDURE  insert_emp(  START INT ,  max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
#set autocommit =0 把autocommit设置成0  SET autocommit = 0;    REPEAT  SET i = i + 1;  INSERT INTO emp (empno, NAME ,age ,deptid ) VALUES ((START+i) ,rand_string(6)   , rand_num(30,50),rand_num(1,10000));  UNTIL i = max_num  END REPEAT;  COMMIT;  END$$ #删除
# DELIMITER ;
# drop PROCEDURE insert_emp;

创建往dept表中插入数据的存储过程

#执行存储过程,往dept表添加随机数据
DELIMITER $$
CREATE PROCEDURE `insert_dept`(  max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;   SET autocommit = 0;    REPEAT  SET i = i + 1;  INSERT INTO dept ( deptname,address,ceo ) VALUES (rand_string(8),rand_string(10),rand_num(1,500000));  UNTIL i = max_num  END REPEAT;  COMMIT;  END$$#删除
# DELIMITER ;
# drop PROCEDURE insert_dept;

调用存储过程

#执行存储过程,往dept表添加1万条数据
DELIMITER ;
CALL insert_dept(10000); #执行存储过程,往emp表添加50万条数据
DELIMITER ;
CALL insert_emp(100000,500000); 
批量删除某个表上的所有索引

mysql索引表位置:

#查询对应库对应表的所有索引名
SELECT index_name FROM information_schema.STATISTICS WHERE table_name='t_emp' AND table_schema='mydb' AND index_name <>'PRIMARY' AND seq_in_index = 1

*<>是标准的, !=是兼容的,一般没啥问题,但有时会故障 所以在数据库中建议用<>*表示不等于

#存储引擎获取所有索引删除
DELIMITER $$
CREATE  PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
BEGINDECLARE done INT DEFAULT 0;DECLARE ct INT DEFAULT 0;DECLARE _index VARCHAR(200) DEFAULT '';DECLARE _cur CURSOR FOR  SELECT   index_name   FROM information_schema.STATISTICS   WHERE table_schema=dbname AND table_name=tablename AND seq_in_index=1 AND    index_name <>'PRIMARY'  ;DECLARE  CONTINUE HANDLER FOR NOT FOUND set done=2 ;      OPEN _cur;FETCH   _cur INTO _index;WHILE  _index<>'' DO SET @str = CONCAT("drop index ",_index," on ",tablename ); PREPARE sql_str FROM @str ;EXECUTE  sql_str;DEALLOCATE PREPARE sql_str;SET _index=''; FETCH   _cur INTO _index; END WHILE;CLOSE _cur;END$$CALL proc_drop_index("dbname","tablename");CREATE X
DROP INDEX idx_xxx ON emp1 查出该表有哪些索引,索引名-->集合
SHOW INDEX FROM t_emp
元数据:meta DATA  描述数据的数据
SELECT index_name  FROM information_schema.STATISTICS WHERE table_name='t_emp' AND table_schema='mydb'AND index_name <>'PRIMARY' AND seq_in_index = 12 如何循环集合CURSOR 游标FETCH xxx INTO xxx3 如何让mysql执行一个字符串
PREPARE 预编译 XXXEXECUTECALL proc_drop_index ('mydb','t_emp');

索引失效问题

全值匹配我最爱,where后多个字段过滤,使用组合索引,效率更好,匹配更多。

  • 最佳左前缀法则,如果是组合索引,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列(第一个必须有,中间不能断),不然索引失效。底层是从左到右一次次匹配B+树,如果中间和第一个断了,无法连接到下一个B+树。每个节点都连接着下个索引字段的B+树。

    比如:

    CREATE INDEX idx_age_deptid_name ON emp(age,deptid,NAME)#只有age匹配到索引,ken_len只有age的
    EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30   AND emp.name = 'abcd' #ALL,全局匹配,索引失效
    EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.deptid=1   AND emp.name = 'abcd'
    
  • 不要在过滤索引列上做任何操作(计算±*/、函数、(自动或者手动)类型转换),会导致索引失效而转向全表扫描

    比如:

    #索引失效,where过滤字段索引列使用了函数
    EXPLAIN  SELECT SQL_NO_CACHE * FROM emp WHERE  LEFT(emp.name,3)  = 'abc'
  • 存储引擎不能使用索引中范围条件右边的列

    比如:

    CREATE INDEX idx_age_deptid_name ON emp(age,deptid,NAME)#只用到age,deptid,name失效
    #deptid范围range查询,组合索引后面发name就失效了
    EXPLAIN SELECT  SQL_NO_CACHE * FROM emp WHERE emp.age=30 AND emp.deptId>20 AND emp.name = 'abc'; #改进方法,deptid范围字段放最后
    CREATE INDEX idx_age_deptid_name ON emp(age,NAME,deptid)
  • mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描

  • is not null 也无法使用索引,但是is null是可以使用索引的

  • like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作,只要左边有%就索引失效,abc%则不会,B+数安排首字符排序,第一个字符都不清楚,无法定位。

  • 字符串不加单引号索引失效

  • or两边的字段中,如果有一个不是索引字段,而其他条件也不是索引字段,会造成该查询不走索引的情况。

索引创建建议:

  • 对于单键索引,尽量选择针对当前query过滤性更好的索引(主键、唯一、分支多的,值有很多各种)
  • 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
  • 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引
  • 在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面
  • 书写sql语句时,尽量避免造成索引失效的情况

关联查询优化

#都没索引
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;

using join buffer效率极低,添加被驱动表索引:

驱动表字段添加索引是覆盖索引,还是ALL全盘扫描。

#inner会自动去找有索引的表作为被驱动表
EXPLAIN SELECT * FROM class inner JOIN book ON class.card = book.card;

建议把数据小的表放到驱动表中

关联优化对比:

#速度第2
EXPLAIN SELECT SQL_NO_CACHE a.name,
(SELECT c.name FROM emp c WHERE c.id = b.CEO) ceoname
FROM emp a
LEFT JOIN dept b ON a.deptId = b.id;

2个eq_ref,主键唯一索引

#速度第1
EXPLAIN SELECT SQL_NO_CACHE a.name,c.name ceoname FROM emp a
LEFT JOIN dept b ON a.deptId = b.id
LEFT JOIN emp c ON b.CEO = c.id;

id都为1,一趟,2个eq_ref主键唯一索引

#速度第4
EXPLAIN SELECT SQL_NO_CACHE c.name,ab.name ceoname FROM emp c LEFT JOIN
(SELECT a.name,b.id FROM emp a
INNER JOIN dept b On b.ceo = a.id) ab
ON c.deptId = ab.id;

MySQL5.7以下,2个关联,2个ALL,1个eq_ref,1个ref,内连接自动选择数据少的作为驱动表, LEFT JOIN后不要有衍生表,因为衍生表没有索引:

MySQL5.7以上,新版本优化,速度跟第一那个的一样:

#速度第3
EXPLAIN SELECT SQL_NO_CACHE ab.name,c.name ceoname FROM
(SELECT a.name,b.CEO FROM emp a
LEFT JOIN dept b ON a.deptId = b.id) ab
LEFT JOIN emp c ON ab.ceo = c.id;

2个关联,2个ALL,2个eq_ref

总结:

  • 保证被驱动表的join字段已经被索引;
  • left join 时,选择小表作为驱动表,大表作为被驱动表;
  • inner join 时,mysql会自己帮你把小结果集的表选为驱动表;
  • 子查询尽量不要放在被驱动表,有可能使用不到索引;
  • 能够直接多表关联的尽量直接关联,不用子查询(多一趟ALL)。

子查询优化

尽量不要使用not in 、not null或者 not exists

用left join on xxx is null 替代

SELECT * FROM emp a WHERE a.id NOT IN
(SELECT b.CEO FROM dept b WHERE b.CEO is NOT NULL)
#子查询,多一趟,NOT NULL 索引失效
#优化如下,都用关联查询替换
SELECT * FROM emp a
LEFT JOIN dept on a.id = b.CEO
WHERE b.id IS NULL;

排序分组优化

create index idx_age_deptid_name on emp (age,deptid,name) 以下  是否能使用到索引,能否去掉using filesort
#不能,还是using filesort
1、explain  select SQL_NO_CACHE * from emp order by age,deptid; #能,using filesort去掉了
2、explain  select SQL_NO_CACHE * from emp order by age,deptid limit 10; #1无过滤 不索引,必须有Where,limit等过滤,order by才能使用索引,key_len是where后的索引数#能,using filesort变成了using index
3、explain  select * from emp where age=45 order by deptid;#能,using filesort变成了using index
4、explain  select * from emp where age=45 order by deptid,name; #能,where、deptid是using index,empno是using filesort
5、explain  select * from emp where age=45 order by deptid,empno;#不能,where是using index,排序是using filesort
6、explain  select * from emp where age=45 order by name,deptid;#都不能
7、explain select * from emp where deptid=45 order by age;#2顺序错,必filesort排序,order by优化器不调整顺序,调整了结果就变了#能,using where,过滤和排序都使用了索引
8、explain select * from emp where age=45 order by  deptid desc, name desc;#不能,using where+using filesort
9、explain select * from emp where age=45 order by  deptid asc, name desc ;#3方向反 必filesort排序

ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序

例子:

EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE age = 30 and empno<101000 ORDER BY `name`;
#优化,范围后的索引失效
CREATE INDEX idx_age_name ON emp (age,empno,name)
#去掉范围,去掉using filesort
CREATE INDEX idx_age_name ON emp (age,name)#2个都创建,mysql选哪个?
CREATE INDEX idx_age_name ON emp (age,empno)#选这个,rows行数才几十,虽然using filesort和range
CREATE INDEX idx_age_name ON emp (age,name)#不选这个,虽然去掉了using filesort,但是rows物理行上万
#分析
原因是所有的排序都是在条件过滤之后才执行的,所以如果条件过滤了大部分数据的话,几百几千条数据进行排序其实并不是很消耗性能,即使索引优化了排序但实际提升性能很有限。  相对的 empno<101000 这个条件如果没有用到索引的话,要对几万条的数据进行扫描,这是非常消耗性能的,所以索引放在这个字段上性价比最高,是最优选择。

结论: 当范围条件和group by 或者 order by 的字段出现二选一时 ,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。

filesort两种算法

如果不在索引列上,filesort有两种算法:
mysql就要启动双路排序和单路排序

双路排序:MySQL 4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。

取一批数据,要对磁盘进行了两次扫描,众所周知,I/O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序。

单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。内存排序。

由于单路是后出的,总体而言好过双路,但是用单路有问题:

在sort_buffer中,方法B比方法A要多占用很多空间,因为方法B是把所有字段都取出, 所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取取sort_buffer容量大小,再排……从而多次I/O。

本来想省一次I/O操作,反而导致了大量的I/O操作,反而得不偿失。

优化策略:

  • 增大sort_buffer_size参数的设置
  • 增大max_length_for_sort_data参数的设置
  • 减少select 后面的查询的字段。

提高Order By的速度:

  1. Order by时select * 是一个大忌,只Query需要的字段, 这点非常重要。在这里的影响是:
    1.1 当Query的字段大小总和小于max_length_for_sort_data 而且排序字段不是 TEXT|BLOB 类型时,会用改进后的算法——单路排序, 否则用老算法——多路排序。
    1.2 两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次I/O,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size。
  2. 尝试提高 sort_buffer_size
    不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的 1M-8M之间调整
  3. 尝试提高 max_length_for_sort_data
    提高这个参数, 会增加用改进算法的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率. 1024-8192之间调整

group by 使用索引的原则几乎跟order by一致 ,唯一区别是group by 即使没有过滤条件用到索引,也可以直接使用索引

覆盖索引

最后使用索引的手段:覆盖索引

简单说就是,select 到 from 之间查询的列 <=使用的索引列+主键

select * 不要使用,具体用到哪些列哪些,select 具体字段也会使用覆盖索引

explain select * from emp where name like ‘%abc’;

使用覆盖索引后:

优化实战

#1、列出自己的掌门比自己年龄小的人员
SELECTa.`name`,a.`age`,c.`name` ceoname,c.`age` ceoage
FROMt_emp a
LEFT JOIN t_dept b ON a.`deptId` = b.`id`
LEFT JOIN t_emp c ON b.`CEO` = c.`id`
WHEREc.`age` < a.`age`;
#优化
EXPLAIN SELECT SQL_NO_CACHEa.`name`,a.`age`,c.`name` ceoname,c.`age` ceoage
FROMemp a
LEFT JOIN dept b ON a.`deptId` = b.`id`
LEFT JOIN emp c ON b.`CEO` = c.`id`
WHEREc.`age` < a.`age`CREATE INDEX idx_age ON emp (age) #2、列出所有年龄低于自己门派平均年龄的人员
SELECTc.`name`,c.`age`,aa.age
FROMt_emp c
INNER JOIN (SELECTa.`deptId`,AVG(a.`age`) ageFROMt_emp aWHEREa.`deptId` IS NOT NULLGROUP BYa.`deptId`
) aa ON c.`deptId` = aa.deptId
WHEREc.`age` < aa.age;#优化
EXPLAIN SELECT SQL_NO_CACHEc.`name`,c.`age`,aa.age
FROMemp c
INNER JOIN (SELECTa.`deptId`,AVG(a.`age`) ageFROMemp aWHEREa.`deptId` IS NOT NULLGROUP BYa.`deptId`
) aa ON c.`deptId` = aa.deptid
WHEREc.`age` < aa.age CREATE INDEX idx_deptid ON emp (deptid)
CREATE INDEX idx_deptid_age ON emp (deptid, age) #3、列出至少有2个年龄大于40岁的成员的门派
# select后的字段只能是group后有的或者函数
# 能连接就连接,不要子查询,被驱动表要有索引
# inner join 自动选有索引为被驱动表
# 每一趟只选一个索引
SELECTb.`deptName`,COUNT(*)
FROMt_emp a
INNER JOIN t_dept b ON b.`id` = a.`deptId`
WHEREa.age > 40
GROUP BYb.`deptName`,b.`id`
HAVINGCOUNT(*) >= 2;#优化
EXPLAIN SELECT SQL_NO_CACHEb.`deptName`,COUNT(*)
FROMdept b STRAIGHT_JOIN emp a ON b.`id` = a.`deptId`
WHEREa.age > 40
GROUP BYb.`deptName`,b.`id`
HAVINGCOUNT(*) >= 2;CREATE INDEX idx_deptid_age ON emp (deptid, age)
CREATE INDEX idx_deptname ON dept (deptname)#STRAIGHT_JOIN 强制确定驱动表和被驱动表 1、概念非常明确 2、对数据量的比例非常明确
#Mysql的inner join 自动选被驱动表,被驱动表虽然有索引,但驱动表可能数据量很大,所以效率反而降低,需要STRAIGHT_JOIN强制确定驱动表,数据量少的在前#4、至少有2位非掌门人成员的门派
#优化
EXPLAIN SELECT SQL_NO_CACHEc.deptname,c.id,COUNT(*)
FROMdept c
STRAIGHT_JOIN emp a ON a.`deptId` = c.`id`
LEFT JOIN dept b ON a.`id` = b.`ceo`
WHEREb.`id` IS NULL
GROUP BYc.deptname,c.`id`
HAVINGCOUNT(*) >= 2;CREATE INDEX idx_ceo_deptnam ON dept (ceo, deptname);
CREATE INDEX idx_deptnam ON dept (deptname);
CREATE INDEX idx_deptid ON emp (deptid);##5、列出全部人员,并增加一列备注“是否为掌门”,如果是掌门人显示是,不是掌门人显示否
SELECTa.`name`,a.age,(CASEWHEN b.`id` IS NULL THEN'否'ELSE'是'END) '是否为掌门'
FROMt_emp a
LEFT JOIN t_dept b ON a.`id` = b.`ceo`#6、列出全部门派,并增加一列备注“老鸟or菜鸟”,若门派的平均值年龄>50显示“老鸟”,否则显示“菜鸟”
SELECTb.`deptName`,
IF (AVG(a.age) > 50,'老鸟','菜鸟'
) '老鸟or菜鸟'
FROMt_emp a
INNER JOIN t_dept b ON a.`deptId` = b.`id`
GROUP BYb.`id`,b.`deptName`;#7、显示每个门派年龄最大的人
SELECTNAME,age
FROMt_emp a
INNER JOIN (SELECTdeptid,MAX(age) maxageFROMt_empWHEREdeptid IS NOT NULLGROUP BYdeptid
) aa ON a.`age` = aa.maxage
AND a.`deptId` = aa.deptid;#优化
EXPLAIN SELECT SQL_NO_CACHENAME,age
FROMemp a
INNER JOIN (SELECTdeptid,MAX(age) maxageFROMempWHEREdeptid IS NOT NULLGROUP BYdeptid
) aa ON a.`age` = aa.maxage
AND a.`deptId` = aa.deptid;CREATE INDEX idx_deptid_age ON emp (deptid, age);#一个@是自定义变量,@@是系统变量
#8、显示每个门派年龄第二大的人,笔试经常出现,排名
SET @rank = 0;
SET @last_deptid = 0;SELECTa.deptid,a. NAME,a.age
FROM(SELECTt.*,IF (#组内排序,排名@last_deptid = deptid ,@rank:=@rank + 1,@rank:= 1) AS rk,@last_deptid := deptid AS last_deptidFROMt_emp tORDER BYdeptid,age DESC) a
WHEREa.rk = 2;#排名,并列名,相同则排名一样,其他向后推
SET @rank = 0;
SET @last_deptid = 0;
SET @last_age = 0;SELECTt.*,
IF (@last_deptid = deptid,
IF (@last_age = age ,@rank ,@rank :=@rank + 1
) ,@rank := 1
) AS rk,@last_deptid := deptid AS last_deptid,@last_age := age AS last_age
FROMt_emp t
ORDER BYdeptid,age DESC
#oracle实习组内排序排名有函数,rank(),over()#IF(条件,true,false)
CASE
WHEN b.`id` IS NULL THEN'否'
ELSE'是'
END

MySQL详解(四)——高级 2.0相关推荐

  1. mysql数据库设计四大范性_数据库篇-mysql详解( 四 )之范式与数据高级骚操作

    标题.png 一 : 主键 primary key,主要的键. 一张表只能有一个字段可以使用对应的键, 用来唯一的约束该字段里面的数据, 不能重复: 这种称之为主键.一张表只能有最多一个主键 操作 增 ...

  2. MySql详解(四)

    MySql详解(四) MySql的DML操作 插入: 一.方式一 语法: insert into 表名(字段名,...) values(值,...); 特点: 1.要求值的类型和字段的类型要一致或兼容 ...

  3. 在虚拟机安装MySQL详解

    在虚拟机安装MySQL详解 MySQL是一种关系型数据库,原本是瑞典的公司叫MySqlAB公司,后被Oracle收购 DB:database,数据库,里边保存了有组织的规范的数据 DBMS:datab ...

  4. MySql详解(六)

    MySql详解(六) MySql事务 一.含义 事务:一条或多条sql语句组成一个执行单位,一组sql语句要么都执行要么都不执行 二.特点(ACID) A 原子性:一个事务是不可再分割的整体,要么都执 ...

  5. 详解ZStack高级功能--裸金属服务

    详解Zstack高级功能--裸金属服务 一.前言 今天我们来了解一下ZStack的裸金属,提到裸金属服务,很多人从字面上可能对其不是很了解,其实早在之前的私有云OpenStack平台,就已经推行了Ir ...

  6. Mysql详解——索引优化

    本篇文章是对Mysql索引的创建以及优化进行一个介绍,关于索引的底层原理可以看我另一篇文章:Mysql详解--索引详解 文章目录 一.索引的创建和设计原则 1. 索引的声明和使用 1.1 索引的分类: ...

  7. linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  8. Android Studio 插件开发详解四:填坑

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78265540 本文出自[赵彦军的博客] 系列目录 Android Gradle使用 ...

  9. springboot 详解 (四)redis filter

    ---------------------------------------------------------------------------------------------------- ...

  10. 大数据WEB阶段(六)MySql详解(二)

    MySql详解(二) 一.分组查询 语法: select col_name1,col_name2... from tb_name group by having ...; 练习: 执行下面的SQL,创 ...

最新文章

  1. 计算机考试批处理试题,2015计算机三级考试pc技术模拟试题及答案(八)
  2. 怎么在html中加判断,css样式里面如何做判断
  3. C++ Primer 5th笔记(chap 19 特殊工具与技术)控制内存分配
  4. 大学计算机测试试题,大学计算机基础 excel测试题 求答案~~喵~~
  5. 使用iOS手势UIGestureRecognizer
  6. Python 3.6学习笔记(一)
  7. LeetCode 23. 合并K个排序链表(优先队列)
  8. Spark集群试运行
  9. select标签multiple属性的使用方法
  10. 【Tensor】(张量)的创建
  11. 【Silverlight】Bing Maps学习系列(一):开发前的准备工作
  12. 科研绘图必备软件简介
  13. mysql关联分组查询,Mysql 分组查询/子查询/关联查询【总结】
  14. # java 核心技术卷1 (原书第11版)通读 第一章:java的基本程序设计结构
  15. SOP封装的后缀字母L M N都代表什么意思?
  16. extern关键字的作用
  17. B树与B+树简明扼要的分析
  18. 效率源linux,FLOOPY效率源硬盘坏道修复工具 修复坏硬盘的时候 为何只能手动修复 而自动修复又说找不到硬盘?...
  19. java 参数不知道类型_java – 类型参数不在其范围内
  20. 渗透测试之SQL注入

热门文章

  1. 《人生的意义与价值 》季羡林
  2. 宝玉论“后生可畏”,存于此以自勉
  3. 圣杯布局、双飞翼布局的理解及对比
  4. 数据仓库,什么是数据仓库?
  5. QQ2012 Beta1登录协议之重定向
  6. 百度前端面试题及答案
  7. html 字号和像素的关系,像素与分辨率的关系
  8. 江苏科技大学c语言程序设计考试,【江苏科技大学】vc语言全国计算机二级试题及答案课程设计实践报告(全)...
  9. 安装BurpSuite
  10. iOS 通过定位启动App