MySQL高级学习笔记day01

MySQL 的架构介绍

mysql的安装(提前上传文件)

1.安装前的准备工作

  1. 执行安装命令前,检查CentOS7会看到 默认安装MariaDB而 不是MySQL

    CentOS6

    rpm -qa|grep mysql
    如果存在mysql-libs的旧版本包如下:
    请先执行卸载命令
    rpm -e --nodeps  mysql-libs
    

    CentOS7

    rpm -qa|grep mariadb
    如果存在如下:
    请先执行卸载命令:
    rpm -e --nodeps mariadb-libs-5.5.52-1.el7.x86_64
    
  2. 检查以下两项依赖

    rpm -qa|grep libaio
    yum install libaio -y
    rpm -qa|grep net-tools
    
  3. List item检查tmp目录权限

    由于mysql安装过程中,会通过mysql用户在/tmp目录下
    新建tmp_db文件,所以请给/tmp较大的权限
    执行 :chmod -R 777 /tmp
    

2.正式安装

上传以下文件分别执行安装
在mysql的安装文件目录下执行:(必须按照顺序执行)

rpm -ivh mysql-community-common-5.7.16-1.el7.x86_64.rpm 
rpm -ivh mysql-community-libs-5.7.16-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-5.7.16-1.el7.x86_64.rpm 
rpm -ivh mysql-community-server-5.7.16-1.el7.x86_64.rpm

3.查看MySQL版本号

执行mysqladmin --version命令,类似java -version打出消息,即为成功。

4.mysql服务的初始化

为了保证数据库目录为与文件的所有者为 mysql 登陆用户,如果你是以 root 身份运行 mysql 服务,需要执行下面的命令初始化

	mysqld --initialize --user=mysql

另外–initialize选项默认以“安全”模式来初始化,则会为 root 用 户生成一个密码并将该密码标记为过期,登陆后你需要设置一个新密码
查看密码:cat /var/log/mysqld.log

5.MySQL服务启动

	启动:systemctl start mysqld 
	状态: systemctl status mysqld
	关闭:systemctl stop mysqld

6. 首次登录

首次登陆通过需要录入初始化密码
因为初始化密码默认是过期的,所以查看数据库会报错
首先执行 mysql -uroot -p 进行登录,在Enter password:
修改密码:

ALTER USER 'root'@'localhost' IDENTIFIED BY '123'; 

设置完密码就可以用新密码登陆,正常使用数据库了

7. 退出登录验证

在mysql窗口输入quit,然后重新执行

mysql -uroot -p

输入新密码

show databases;
create database mydb;
use mydb
create table mytbl(id int,name varchar(20))

MySQL高级学习笔记day01_第1张图片
8. mysql是否自启动

查看mysql是否自启动(默认自启动)

systemctl list-unit-files|grep mysqld.service 

如不是enabled可以运行如下命令设置自启动

systemctl enable mysqld

9. 修改字符集问题

1.修改配置文件(步骤一)

vim /etc/my.cnf

在最后加上中文字符集配置

character_set_server=utf8

2.重新启动mysql

systemctl restart mysqld
systemctl status mysqld

3.再次执行insert语句的时候依然报错
4.

 show create database mydb;

5.已生成的库表字符集如何变更(步骤二)
修改数据库的字符集

 alter database mydb character set 'utf8';

修改数据表的字符集

alter table mytbl convert to character set 'utf8';

6.如果数据库已经存储乱码数据(步骤三)
更新数据或者删除数据

Mysql的用户与权限管理

1.了解USER表

查看用户

select host,user,authentication_string,select_priv,
insert_priv,drop_priv from mysql.user;

Host: 表示连接类型

% 表示所有远程通过 TCP方式的连接

IP 地址 如(192.168.1.2,127.0.0.1)通过制定ip地址进行的TCP方式的连接

机器名通过制定i网络中的机器名进行的TCP方式的连接

::1 IPv6的本地ip地址 等同于IPv4的 127.0.0.1

localhost本地方式通过命令行方式的连接,比如mysql -u xxx -p 123xx方式

User:表示用户名

同一用户通过不同方式链接的权限是不一样的。

password:密码

所有密码串通过password(明文字符串)生成的密文字符串加密算法为

MYSQLSHA1,不可逆。

mysql 5.7的密码保存到 authentication_string字段中不再使用password字 段. select_priv , insert_priv等 为该用户所拥有的权限。

2 权限问题

只看其中几项数据

select Host,user,authentication_string from user;

创建用户

create user zhang3 identified by '123';

表示创建名称为zhang3的用户,密码设为123;

select Host,user,authentication_string from user;

使用Zhang3用户连接 看到只能看到一个数据库—>权限

3.授予权限

授权命令:

grant 权限1,权限2,…权限n on 数据库名称.表名称 to 用户名@用户地址 identified by ‘连接口令’;

该权限如果发现没有该用户,则会直接新建一个用户。

比如

grant select,insert,delete,drop on atguigudb.* to li4@localhost  ;

#给li4用户用本地命令行方式下,授予atguigudb这个库下的所有表的插删改查的权限。

grant all privileges on *.* to root@'%' identified by '123';

#授予通过网络方式登录的的root用户 ,对所有库所有表的全部权限,密码设为

123最后需要刷新权限才能生效

flush privileges; 

MySQL的sql_mode合理设置

1.sql_mode的概念

sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入。在生产环境必须将这个值设置为严格模式,所以开发、测试环境的数据库也必须要设置,这样在开发测试阶段就可以发现问题。

show variables like 'sql_mode'; 

(mysql5.5没有这些校验数据 为空)

建议:大家以后在公司的时候 把公司的sql_mode从测试环境原封不动到拷贝到开发数据库

好处就在于以后开发的所有语法检查就和公司一样的
开发时候没有测出bug,到测试环境也不会测出BUG

2. sql_mode常用值如下

set sql_mode='ONLY_FULL_GROUP_BY';

ONLY_FULL_GROUP_BY:

对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,因为列不在GROUP BY从句中

NO_AUTO_VALUE_ON_ZERO:

该值影响自增长列的插入。默认设置下,插入0或NULL代表生成下一个自增长值。如果用户 希望插入的值为0,而该列不是自增长的,那么这个选项就有用了。

STRICT_TRANS_TABLES:

在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做限制

NO_ZERO_IN_DATE:

在严格模式下,不允许日期和月份为零

NO_ZERO_DATE:

设置该值,mysql数据库不允许插入零日期,插入零日期会抛出错误而不是警告。

ERROR_FOR_DIVISION_BY_ZERO:

在INSERT或UPDATE过程中,如果数据被零除,则产生错误而非警告。如 果未给出该模式,那么数据被零除时MySQL返回NULL

NO_AUTO_CREATE_USER:

禁止GRANT创建密码为空的用户

NO_ENGINE_SUBSTITUTION:

如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常

PIPES_AS_CONCAT:

将"||"视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似

ANSI_QUOTES:

启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符

ORACLE:

设置等同:PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER.

ORACLE遵从的语法MySQL也同样遵循 这样我们MySQL的语法就很标准
无论你是什么数据库,我们写的SQL语法都很通用

Mysql逻辑架构

MySQL高级学习笔记day01_第2张图片

1. 相关概念

和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

2. 连接层
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具 实现的类似于tcp/ip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全 方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在 该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

3. 服务层
Management Serveices & Utilities: 系统管理和控制工具

SQL Interface:SQL接口

接受用户的SQL命令,并且返回用户需要查询的结果。比如select from
就是调用SQL Interface

Parser:解析器

SQL命令传递到解析器的时候会被解析器验证和解析。

Optimizer:查询优化器。

SQL语句在查询之前会使用查询优化器对查询进行优化。

用一个例子就可以理解:

 	 select uid,name from user where  gender= 1;

优化器来决定先投影还是先过滤。

Cache和Buffer:查询缓存。

如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等

4. 引擎层

存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。后面介绍MyISAM和InnoDB

5. 存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。

利用show profile 看SQL执行过程(性能分析工具)
修改配置文件

	vim /etc/my.cnf

新增一行:query_cache_type=1(开启查询缓存0时表示关闭,1时表示打开)

重启mysql

systemctl restart mysqld

使用指定数据库

use mydb

看状态

show variables like '%profiling%';

开启执行周期 (开启profiling) 在xshell执行

set profiling=1;
show variables like '%profiling%';

编写一条SQL查询语句

select * from mytbl2 where id=2; 

显示最近的几次查询

show profiles;    

更加详细的信息

show profile cpu,block io for query 2; 编号

再一次执行上面已经查询的语句会看到

 select * from mytbl2 where id=2; 

重点
1.应用程序通过客户端发起访问请求连接
2.从连接池里面拿到连接
3.访问缓存/缓冲 如果有缓存 走缓存
4.如果缓存没有命中 走SQL查询 走解析器 走优化器
5.选择存储引擎 从磁盘中扫描数据 分别返回给请求端和cache

SQL执行顺序

手写SQL内容

SELECT DISTINCT 
	
FROM
	
JOIN	 ON 
WHERE
	
GROUP BY 
	
HAVING 
	
ORDER BY 
	
LIMIT 	

机读内容

FROM  	
ON 	
 JOIN 
WHERE

GROUP BY 

HAVING 

SELECT 
DISTINCT 
ORDER BY 

LIMIT 	

存储引擎

1. MyISAM和InnoDB

MySQL高级学习笔记day01_第3张图片

1.MyISAM和InnoDB的区别
a.事务
InnoDB具备事务 有多个SQL存储需要保证事务 性能就会有所影响
MyISAM不具有事务 它能提供高速检索和快速的查询
b.锁
InnoDB采用的行锁 适用于高并发修改数据 吞吐量大
MyISAM采用的表锁 不适用于高并发修改数据 降低写的并发量
c.索引
InnoDB存储着索引与数据 (聚簇索引)
MyISAM只存储索引 (非聚簇索引)
d.如何选择
InnoDB 频繁修改 有事务 高并发
MyISAM 经常查询 没有事务

2.事务的概念
多条SQL语句要么同时执行完 要么都不执行

各个引擎介绍

1、InnoDB存储引擎

InnoDB是MySQL的默认事务型引擎,它被设计用来处理大量的短期(short-lived)事务。除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。

2、MyISAM存储引擎

MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持事务和行级锁,有一个毫无疑问的缺陷就是崩溃后无法安全恢复。

3、Archive引擎

Archive档案存储引擎只支持INSERT和SELECT操作,在MySQL5.1之前不支持索引。

Archive表适合日志和数据采集类应用。

根据英文的测试结论来看,Archive表比MyISAM表要小大约75%,比支持事务处理的InnoDB表小大约83%。

(不支撑update delete 类比个人档案)

4、Blackhole引擎

Blackhole引擎没有实现任何存储机制,它会丢弃所有插入的数据,不做任何保存。但服务器会记录Blackhole表的日志,所以可以用于复制数据到备库,或者简单地记录到日志。但这种应用方式会碰到很多问题,因此并不推荐。

(黑洞 可以往这里写操作 只会记录不会真正的保持)

5、CSV引擎

CSV引擎可以将普通的CSV文件作为MySQL的表来处理,但不支持索引。

CSV引擎可以作为一种数据交换的机制,非常有用。

CSV存储的数据直接可以在操作系统里,用文本编辑器,或者excel读取。

(名字是CSV后缀的文件,可以直接复制然后用Excel等工具打开,方便不懂MySQL的人

进行操作 操作之后直接放到数据库相当于就更新数据了,不用写SQL)

6、Memory引擎

如果需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没有关系,那么使用Memory表是非常有用。Memory表至少比MyISAM表要快一个数量级。

(有了redis等内存框架 这个就显得鸡肋引擎)

7、Federated引擎

Federated引擎是访问其他MySQL服务器的一个代理,尽管该引擎看起来提供了一种很好的跨服务器的灵活性,但也经常带来问题,因此默认是禁用的。

常用命令
mysql现在已提供什么存储引擎:

show engines; 

mysql当前默认的存储引擎:

show variables like '%storage_engine%';

索引优化分析

1. SQL出现以下问题

性能下降SQL慢 执行时间长 等待时间长有可能由以下问题导致
数据过多 分库分表
关联了太多的表,太多join SQL优化
没有充分利用到索引 索引建立
服务器调优及各个参数设置 调整my.cnf

2. 连接类型

必须知道:
内连接:两张表的交集

	SELECT * FROM A INNER JOIN B ON A.key=B.key;

MySQL高级学习笔记day01_第4张图片
左外连接:包含左边表的全部行,和右边表中全部匹配的行

	SELECT * FROM A LEFT JOIN B ON A.key=B.key;

MySQL高级学习笔记day01_第5张图片
右外连接:包含右边表的全部行,和左边表的中全部匹配的行

 	SELECT * FROM A RIGHT JOIN B ON A.key=B.key;

MySQL高级学习笔记day01_第6张图片
左连接:包含左边表除去和右边表交集的全部行

	SELECT * FROM A LEFT JOIN B ON A.key=B.key WHERE B.key=NULL;

MySQL高级学习笔记day01_第7张图片
右连接:包含右边表出去和左边表焦急的全部行

SELECT * FROM A RIGHT JOIN B ON A.key=B.key WHERE A.key=NULL;

MySQL高级学习笔记day01_第8张图片
全连接:包含两个表全部数据

SELECT * FROM A FULL OUTER JOIN	B ON A.key=B.key;

MySQL高级学习笔记day01_第9张图片(Mysql中不支持下图写法)

A、B的所有也就是A的独有、B的独有 和A、B的共同拥有的数据

SELECT * FROM A LEFT JOIN B ON A.key=B.key(找出A的所有)

UNION(去重)

SELECT * FROM A RIGHT JOIN B ON A.key=B.key(找出B的所有)

全外连接:其实全外连接也就是A的独有+B的独有

SELECT * FROM A FULL OUTER JOIN B ON B.key=B.key WHERE B.key IS NULL OR B.key IS null

MySQL高级学习笔记day01_第10张图片

SELECT * FROM TABLE A LEFT JOIN TABLE B ON A.Key = B.Key WHERE B.Key IS NULL      (找出A的独有)

UNION      (去重)

SELECT * FROM TABLE A RIGHT JOIN TABLE B ON A.Key =B.Key WHERE A.Key IS NULL  (找出B的独有)

3.SQL练习

CREATE TABLE `t_dept` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `deptName` varchar(30) DEFAULT NULL,

  `address` varchar(40) DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `t_emp` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `name` varchar(255) DEFAULT NULL,

  `age` int(3) DEFAULT NULL,

  `deptId` int(11) DEFAULT NULL,

  `empno` int(11) NOT NULL,

  PRIMARY KEY (`id`),

  KEY `idx_dept_id` (`deptId`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

INSERT INTO t_dept(deptName,address) VALUES('华山','华山');

INSERT INTO t_dept(deptName,address) VALUES('丐帮','洛阳');

INSERT INTO t_dept(deptName,address) VALUES('峨眉','峨眉山');

INSERT INTO t_dept(deptName,address) VALUES('武当','武当山');

INSERT INTO t_dept(deptName,address) VALUES('明教','光明顶');

INSERT INTO t_dept(deptName,address) VALUES('少林','少林寺');

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('风清扬',90,1,100001);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('岳不群',50,1,100002);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('令狐冲',24,1,100003);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('洪七公',70,2,100004);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('乔峰',35,2,100005);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('灭绝师太',70,3,100006);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('周芷若',20,3,100007);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('张三丰',100,4,100008);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('张无忌',25,5,100009);

INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('韦小宝',18,null,100010);

4.SQL-7种join

1.所有有门派的人员信息 

(A、B两表共有)

select * from t_emp a inner join t_dept b on a.deptId=b.id 

2.列出所有用户,并显示其门派信息 

(A的全集)

select * from t_emp a left join t_dept b on a.deptId = b.id

3.列出所有门派 

(B的全集)

select * from t_dept b 

4.所有不入门派的人员 

(A的独有)

select * from t_emp a left join t_dept b on a.deptId = b.id where b.id is null; 

5.所有没人入的门派 

(B的独有)

select * from t_dept b left join t_emp a on a.deptId= b.id where a.deptId is null; 

6.列出所有人员和门派的对照关系

(AB全有)

#MySQL Full Join的实现 因为MySQL不支持FULL JOIN,下面是替代方法

#left join + union(可去除重复数据)+ right join

SELECT * FROM t_emp A LEFT JOIN t_dept B ON A.deptId = B.id

UNION

SELECT * FROM t_emp A RIGHT JOIN t_dept B ON A.deptId = B.id

7.列出所有没入派的人员和没人入的门派

(A的独有+B的独有)

SELECT * FROM t_emp A LEFT JOIN t_dept B ON A.deptId = B.id WHERE B.`id` IS NULL

UNION

SELECT * FROM t_emp A RIGHT JOIN t_dept B ON A.deptId = B.id WHERE A.`deptId` IS NULL;

 

5. 额外需求

MySQL高级学习笔记day01_第11张图片
修改表结构

ALTER TABLE `t_dept`

add  CEO  INT(11)  ;

update t_dept set CEO=2 where id=1;

update t_dept set CEO=4 where id=2;

update t_dept set CEO=6 where id=3;

update t_dept set CEO=8 where id=4;

update t_dept set CEO=9 where id=5;

求各个门派对应的掌门人名称:

select * from t_dept b left join t_emp a on b.CEO=a.id;

求所有当上掌门人的平均年龄:

select avg(a.age) from t_emp a inner join t_dept b on a.id=b.CEO;

作业: 求所有人物对应的掌门名称

6. 额外需求答案

MySQL高级学习笔记day01_第12张图片
求所有人物对应的掌门名称:

答案一:

a.根据dept字段里面的ceo把该部门ceo名称查出来

SELECT a.`name`,b.`id`

FROM

t_emp a INNER JOIN t_dept b ON b.ceo = a.id

b.然后再查所有人物对应的门派是什么

SELECT c.`name`,ab.name ceoname

FROM

t_emp c INNER JOIN (中间表) ab ON c.`deptId`= ab.id;

答案二:

a.利用t_emp和t_dept查所有人物对应CEO的Id是什么

SELECT a.`name`,b.`CEO`

FROM

t_emp a INNER JOIN t_dept b ON a.`deptId`= b.`id`

b.在根据第一步的CEO查CEO名称

SELECT ab.name,c.`name` ceoname

FROM

(中间表)ab INNER JOIN t_emp c ON ab.ceo=c.`id`;

答案三:

a.前两种方式我们发现其实用的就是t_dept和t_emp两张表不过t_emp

用了2次,直接一次查询

SELECT a.`name`,c.`name` ceoname

FROM t_emp a

INNER JOIN t_dept b ON a.`deptId`= b.`id`

INNER JOIN t_emp c ON b.`CEO`= c.`id`;

答案四:

a.利用t_emp和t_dept查所有人物对应CEO的Id是什么

SELECT a.`name`,b.`CEO`

FROM

t_emp a INNER JOIN t_dept b ON a.`deptId`= b.`id`

b.在这一步的时候我们直接利用子查询

SELECT a.`name`,

(SELECT c.name FROM t_emp c WHERE c.id=b.`CEO`) ceoname

FROM t_emp a

INNER JOIN t_dept b ON a.`deptId`= b.`id`

完整SQL

1.先查出所有人物对于的ceo

select ab.name,c.name ceoName from

(select a.name,b.CEO from t_emp a inner join t_dept b on a.deptId=b.id) ab

inner join t_emp c on ab.ceo=c.id

2.所有部门ceo的名称查出来

select c.name,ab.name ceoName from t_emp c inner join

(select a.name,b.id from t_emp a inner join t_dept b on a.id=b.ceo) ab

on c.deptId=ab.id

3.一次性查两次emp

select a.name,c.name ceoName from t_emp a

inner join t_dept b on a.deptId=b.id

inner join t_emp c on b.ceo=c.id



select a.name,c.name ceoName from

t_emp a,t_dept b,t_emp c where a.deptId=b.id and b.ceo=c.id

4.对第一种进行改写成子查询

select a.name,(select c.name from t_emp c where c.id=b.CEO) ceoName from t_emp a inner join t_dept b on a.deptId=b.id

7. 作业八个SQL

1、列出自己的掌门比自己年龄小的人员
	select a.name,a.age,c.name,c.age from t_emp a inner join t_dept b on 
	a.deptId=b.id 
	left join t_emp c on b.ceo=c.id 
	where a.age>c.age
	优化 
	//create index index_age_deptid on emp (age,deptid);按照字段出场顺序选择下一个
	create index index_deptid_age on emp (deptid,age);
	create index index_ceo on dept (ceo);
2、列出所有年龄低于自己门派平均年龄的人员
	select * from t_emp b 
	inner join (select a.deptId,AVG(age) avgAge from t_emp a GROUP BY a.deptId) c
	on b.deptId=c.deptId and b.age<c.avgAge
	
	优化
	create index index_deptid_age on emp (deptid,age);
	单独设计一张表存储平均年龄 然后再进行联合查询
3、列出至少有2个年龄大于40岁的成员的门派
	select b.deptName,count(0) num from t_emp a inner join t_dept b on a.deptId=b.id where a.age>40  
	GROUP BY b.deptName HAVING num>=2
	
	优化
	explain select b.deptName,count(0) num from emp a inner join dept b 
	on a.deptId=b.id where a.age>40 GROUP BY b.deptName HAVING num>=2
	create index index_deptid_age on emp (deptid,age);
	create index index_deptName on dept (deptName);
4、至少有2位非掌门人成员的门派
	select * from t_dept c inner join 
	(select a.deptId,count(0) num from t_emp a left join t_dept b on a.id=b.ceo
	where b.id is null GROUP BY a.deptId HAVING num>=2) ab 
	on c.id=ab.deptId
	
	优化 
	create index index_ceo on dept (ceo);
	create index index_deptId on emp (deptId);
	
	explain select a.deptId,c.deptName,count(0) num from t_emp a 
	left join t_dept c on c.id=a.deptId
	left join t_dept b on a.id=b.ceo
	where b.id is null
	GROUP BY a.deptId,c.deptName
	HAVING num>=2
	
	create index index_ceo_deptName on dept (ceo,deptName);
	create index index_deptId on emp (deptId);
	
	最终优化语句
	explain select a.deptId,c.deptName,count(0) num from dept c
	left join emp a on c.id=a.deptId
	left join dept b on a.id=b.ceo
	where b.id is null
	GROUP BY a.deptId,c.deptName
	HAVING num>=2
5、列出全部人员,并增加一列备注“是否为掌门” 如果是掌门人显示是 不是掌门人显示否
	select a.name,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 a.deptName,avg(b.age) avgAge,if(avg(b.age)>50,'老鸟','菜鸟') '老鸟or菜鸟' 
	from t_dept a left join t_emp b on a.id=b.deptId
	GROUP BY a.deptName
7、显示每个门派年龄最大的人
	select * from t_emp c
	inner join (select a.deptId,max(age) maxAge from t_emp a GROUP BY a.deptId) b
	on c.deptId=b.deptId and c.age=b.maxAge
	
	优化
	create index index_deptid_age on emp (deptId,age);
8、显示每个门派年龄第二大的人
	SET @rank=0;
	SET @last_deptid=0;

		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
		已经在组内实现分组按照年龄降序排序

	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;
    解决bug
	update t_emp set age=90 where id =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

你可能感兴趣的:(MySQL高级,sql,笔记,数据仓库)