MySQL详解(四)——高级 2.0

news2024/11/17 20:45:59

性能分析

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批量处理:

    	//批处理
        @Transactional
        public 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))
BEGIN
       DECLARE 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 emp

1 查出该表有哪些索引,索引名-->集合
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 = 1

2 如何循环集合
 CURSOR 游标
 FETCH xxx INTO xxx

3 如何让mysql执行一个字符串
PREPARE 预编译 XXX

EXECUTE

CALL 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
1explain  select SQL_NO_CACHE * from emp order by age,deptid; 

#能,using filesort去掉了
2explain  select SQL_NO_CACHE * from emp order by age,deptid limit 10; 
 
 
#1无过滤 不索引,必须有Where,limit等过滤,order by才能使用索引,key_len是where后的索引数
 
 
#能,using filesort变成了using index
3explain  select * from emp where age=45 order by deptid;
 
#能,using filesort变成了using index
4explain  select * from emp where age=45 order by deptid,name; 
 
#能,where、deptid是using index,empno是using filesort
5explain  select * from emp where age=45 order by deptid,empno;

#不能,where是using index,排序是using filesort
6explain  select * from emp where age=45 order by name,deptid;

#都不能
7explain select * from emp where deptid=45 order by age;
 
 
#2顺序错,必filesort排序,order by优化器不调整顺序,调整了结果就变了
 
 
#能,using where,过滤和排序都使用了索引
8explain select * from emp where age=45 order by  deptid desc, name desc;

#不能,using where+using filesort
9explain 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、列出自己的掌门比自己年龄小的人员
SELECT
	a.`name`,
	a.`age`,
	c.`name` ceoname,
	c.`age` ceoage
FROM
	t_emp a
LEFT JOIN t_dept b ON a.`deptId` = b.`id`
LEFT JOIN t_emp c ON b.`CEO` = c.`id`
WHERE
	c.`age` < a.`age`;
#优化 
EXPLAIN SELECT SQL_NO_CACHE
	a.`name`,
	a.`age`,
	c.`name` ceoname,
	c.`age` ceoage
FROM
	emp a
LEFT JOIN dept b ON a.`deptId` = b.`id`
LEFT JOIN emp c ON b.`CEO` = c.`id`
WHERE
	c.`age` < a.`age`

CREATE INDEX idx_age ON emp (age) 



#2、列出所有年龄低于自己门派平均年龄的人员
SELECT
	c.`name`,
	c.`age`,
	aa.age
FROM
	t_emp c
INNER JOIN (
	SELECT
		a.`deptId`,
		AVG(a.`age`) age
	FROM
		t_emp a
	WHERE
		a.`deptId` IS NOT NULL
	GROUP BY
		a.`deptId`
) aa ON c.`deptId` = aa.deptId
WHERE
	c.`age` < aa.age;

#优化 
EXPLAIN SELECT SQL_NO_CACHE
	c.`name`,
	c.`age`,
	aa.age
FROM
	emp c
INNER JOIN (
	SELECT
		a.`deptId`,
		AVG(a.`age`) age
	FROM
		emp a
	WHERE
		a.`deptId` IS NOT NULL
	GROUP BY
		a.`deptId`
) aa ON c.`deptId` = aa.deptid
WHERE
	c.`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 自动选有索引为被驱动表
# 每一趟只选一个索引
SELECT
	b.`deptName`,
	COUNT(*)
FROM
	t_emp a
INNER JOIN t_dept b ON b.`id` = a.`deptId`
WHERE
	a.age > 40
GROUP BY
	b.`deptName`,
	b.`id`
HAVING
	COUNT(*) >= 2;

#优化 
EXPLAIN SELECT SQL_NO_CACHE
	b.`deptName`,
	COUNT(*)
FROM
	dept b STRAIGHT_JOIN emp a ON b.`id` = a.`deptId`
WHERE
	a.age > 40
GROUP BY
	b.`deptName`,
	b.`id`
HAVING
	COUNT(*) >= 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_CACHE
	c.deptname,
	c.id,
	COUNT(*)
FROM
	dept c 
STRAIGHT_JOIN emp a ON a.`deptId` = c.`id`
LEFT JOIN dept b ON a.`id` = b.`ceo`
WHERE
	b.`id` IS NULL
GROUP BY
	c.deptname,
	c.`id`
HAVING
	COUNT(*) >= 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、列出全部人员,并增加一列备注“是否为掌门”,如果是掌门人显示是,不是掌门人显示否
SELECT
	a.`name`,
	a.age,
	(
		CASE
		WHEN b.`id` IS NULL THEN
			'否'
		ELSE
			'是'
		END
	) '是否为掌门'
FROM
	t_emp a
LEFT JOIN t_dept b ON a.`id` = b.`ceo`


#6、列出全部门派,并增加一列备注“老鸟or菜鸟”,若门派的平均值年龄>50显示“老鸟”,否则显示“菜鸟”
SELECT
	b.`deptName`,
IF (
	AVG(a.age) > 50,
	'老鸟',
	'菜鸟'
) '老鸟or菜鸟'
FROM
	t_emp a
INNER JOIN t_dept b ON a.`deptId` = b.`id`
GROUP BY
	b.`id`,
	b.`deptName`;
	
	

#7、显示每个门派年龄最大的人
SELECT
	NAME,
	age
FROM
	t_emp a
INNER JOIN (
	SELECT
		deptid,
		MAX(age) maxage
	FROM
		t_emp
	WHERE
		deptid IS NOT NULL
	GROUP BY
		deptid
) aa ON a.`age` = aa.maxage
AND a.`deptId` = aa.deptid;

#优化 
EXPLAIN SELECT SQL_NO_CACHE
	NAME,
	age
FROM
	emp a
INNER JOIN (
	SELECT
		deptid,
		MAX(age) maxage
	FROM
		emp
	WHERE
		deptid IS NOT NULL
	GROUP BY
		deptid
) 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;

SELECT
	a.deptid,
	a. NAME,
	a.age
FROM
	(
		SELECT
			t.*,
		IF (#组内排序,排名
			@last_deptid = deptid ,@rank:=@rank + 1,@rank:= 1
		) AS rk,
		@last_deptid := deptid AS last_deptid
	FROM
		t_emp t
	ORDER BY
		deptid,
		age DESC
	) a
WHERE
	a.rk = 2;

#排名,并列名,相同则排名一样,其他向后推
SET @rank = 0;
SET @last_deptid = 0;
SET @last_age = 0;

SELECT
	t.*,
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
FROM
	t_emp t
ORDER BY
	deptid,
	age DESC
#oracle实习组内排序排名有函数,rank(),over()

	
#IF(条件,true,false)
CASE
WHEN b.`id` IS NULL THEN
	'否'
ELSE
	'是'
END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/192371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ECharts线性渐变色示例演示(2种渐变方式)

第003个点击查看专栏目录Echarts的渐变色采用了echarts.graphic.LinearGradient的方法&#xff0c;可以根据代码中的内容来看如何使用。线性渐变&#xff0c;多用于折线柱形图&#xff0c;前四个参数分别是 x0, y0, x2, y2, 范围从 0 - 1&#xff0c;相当于在图形包围盒中的百分…

PTA L1-025 正整数A+B(详解)

前言&#xff1a;本期是关于正整数AB的详解&#xff0c;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读&#xff0c;今天你c了吗&#xff1f; 题目&#xff1a; 题的目标很简单&#xff0c;就是求两个正整数A和B的和&#xf…

用户使用苹果AirTag来追踪宠物存在风险,苹果Find My功能用处广

苹果的 AirTag 不失为追踪宠物的一种便捷方式&#xff0c;这样宠物即便挣脱宠物圈或者其它方式丢失&#xff0c;都可以通过“Find My”方式追踪定位。正如《华尔街日报》所指出的&#xff0c;这种方式也存在 AirTag 被宠物吞食的风险。 AirTag 的直径为 1.26 英寸&#xff0c…

【Faster R-CNN】之 Resize_and_Padding 代码精读

【Faster R-CNN】之 Resize_and_Padding1、前言&#xff1a;2、resize_image_and_bbox1&#xff09;先对图像做resize处理2&#xff09;再对 bounding box 做resize处理3、padding_images代码1、前言&#xff1a; 在上一篇文章 【Faster R-CNN】之 Dataset and Dataloader 代码…

Linux网络:传输层之UDPTCP协议

文章目录一、端口号1.端口号范围划分2.常用命令二、UDP 协议1.格式2.特点3. UDP 的缓冲区4. UDP 使用注意事项5.基于 UDP 的应用层协议三、TCP 协议1.格式2.确认应答机制3.超时重传机制4.连接管理机制三次握手四次挥手5.滑动窗口6.流量控制7.拥塞控制8.延迟应答9.捎带应答10.面…

PyQt5利用Qt Designer制作一个可以拖动获取文件信息的页面

前言 本篇在讲什么 用pyqt5制作一个简单的程序&#xff0c;拖动文件或脚本可以读取文件信息 本篇适合什么 适合初学PyQt5的小白 本篇需要什么 对Python语法有简单认知 对Qt有简单认知 依赖Pycharm编辑器 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&…

[Golang实战]整理Golang忽略的问题

整理Golang忽略的问题参考资料1.WaitGroup与GoRoutine的竞速2.Mutex互斥锁和RWMutex互斥读写锁3.poll,select,epoll4.何时栈和堆?5.GoRoutine合理使用6.GoRoutine优雅退出6.1data channel关闭通知退出6.2exit channel关闭通知退出6.3context超时或取消通知退出6.4WaitGroup/Er…

IPWorks EDI 2022.0.8381 for NET Crack

IPWorks EDI基于用于安全 EDI 通信&#xff08;AS2、SFTP、OFTP、RosettaNet、MLLP 等&#xff09;的领先 EDI-INT 协议&#xff0c;IPWorks EDI 库包含促进安全 EDI 消息传递以及 EDI 映射、翻译和验证&#xff08;X12、 EDIFACT、HL7、TRADACOMS、VDA、XML 和 JSON&#xff0…

golang/安装

golang中文官网 https://golang.google.cn/ golang下载 安装 一路next 配置 配置值说明GOROOTD:\ProgramFiles\golanggolang安装目录PATHD:\ProgramFiles\golang\bingolang命令路径GO111MODULEon开启go.mod功能&#xff0c;go.mod是go官方依赖包管理工具GOPROXYhttps://go…

【FPGA笔记系列7】时序逻辑电路基础D触发器

时序逻辑电路 组合逻辑与时序逻辑电路的本质区别:时序逻辑电路的输出和前一时刻的状态有关,组合逻辑电路的输出只和当前的输入有关 与非门RS锁存器的缺陷:当SR从00变到11时,状态不稳定! 电路中小圈圈表示低电平有效! 透明锁存器 R=1当En=1时,Q=S当En=0时,后面为RS触发器…

使用git合并两个不同项目代码

使用git合并两个不同项目代码 前言, 这里解决的是两个不同的项目, 因为不同项目那必然是两个不同的git仓库 都是不同的git仓库了那就更不可能是相同的分支了(即使分支名相同) 至于为什么会有这种业务情况出现, 我也不知道, 反正先学干就完了 这里图形化界面演示用的是idea自带的…

人工智能时代八大类算法你了解吗?(包邮送书6本)

文章目录本文导读1. 关联规则分析2. 回归分析3. 分类分析4. 聚类分析5. 集成学习6. 自然语言处理7. 图像处理8. 深度学习9. 书籍推荐&#xff08;包邮送书6本&#xff09;本文导读 从零带你了解人工智能时代需要掌握的8大类算法&#xff0c;包括基础理论、关联规则分析、回归分…

Java-基础-4.IO流

一&#xff1a;为什么有IO流&#xff1f; 在显示生产中&#xff0c;我们的数据&#xff0c;都是不停的往过输入和输出&#xff0c;我们将这种模式称作为流。并且在输入和输出的过程中&#xff0c;我们包装了一些其他类。 二&#xff1a;什么是IO流&#xff1f; 1. 按照数据处理…

Linux学习之常用基本命令【1】

文章目录前言一 Linux系统简介二 补充知识Unix和Minix三 开关机命令四 系统目录结构五 树形显示文件目录结构六 目录管理6.0 目录操作常用命令6.1 ls(列出目录)【常用命令】6.2 cd(切换目录)6.3 pwd( 显示当前所在的目录 )6.4 mkdir&#xff08;make directory创建目录&#xf…

Power BI折线图

如果要展现数据的趋势变化&#xff0c;折线图应该是不二之选&#xff0c;并且它更擅长于展现时间序列下的数据&#xff0c;根据折线斜率的不同展现变化的速率。 本文使用PowerBI Desktop来轻松生成一个折线图。 案例数据&#xff1a;2006-2015年各省市的三个产业的产值&#…

【SAP Abap】X档案:SAP 快速提供基础数据给第三方系统访问的几种方法

SAP 快速提供基础数据给第三方系统访问的几种方法1、数据封装2、开放RFC访问3、开放接口服务4、开放DB访问1、数据封装 在企业信息系统建设过程中&#xff0c;少不了的就是系统集成数据对接。 尤其是SAP系统中大量的基础数据集成&#xff0c;如各种字段值域&#xff0c;需要提…

Vue2笔记02 表单数据,过滤器,常见指令,生命周期,组件

表单数据 过滤器 过滤器&#xff1a;将数据进行简单处理后再使用 好用的第三方库的网站&#xff1a;BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务 显示当前时间计算属性的写法 过滤器的写法 多个过滤器可以串联 &#x1f446;这里的过滤器是局部过滤器&#xff0c…

day03_java基本语法

今日内容 零、复习昨日 一、开发工具 二、Eclipse使用 三、程序解读 四、输出语句 五、常量 六、变量 七、数据类型 零、 复习昨日 见晨考.txt 一、开发工具 开发工具: Eclipse(免费),IDEA(收费) 1.1 Eclipse安装 Eclipse是绿色安全的,直接解压即可使用 1.2 启动Eclipse ps:…

原型和原型链

什么是原型&#xff1f; 因为每一个函数都有一个属性&#xff0c;这个属性名就是prototype,&#xff08;即为显式原型&#xff09; 这个属性的值是一个对象 每一个实例对象都有一个__proto__&#xff08;即为隐式原型&#xff09; 原型就是函数的prototype属性&#xff0c;…

excel数据比较:如何做一个完美的多条件排名方案

排名&#xff0c;简单&#xff1b;但如果有多个项目类别&#xff0c;并且可能存在业绩相同&#xff0c;怎么快速找出各个分享排名第一的人物呢&#xff1f;这就要通过多条件去匹配&#xff0c;才能找出需要的排名第一者。这里提供了两个方案&#xff0c;但都不够完美&#xff0…