mysql高级篇笔记
mysql高级篇(一)mysql的安装配置、架构介绍及SQL语句的复习.
mysql高级篇(二)mysql索引优化分析.
mysql高级篇(三)查询截取分析(慢查询日志)、主从复制以及mycat的安装和使用.
第 1 章 MySQL 简介
1.1什么是 Mysql
- MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。
- Mysql 是开源的,可以定制的,采用了 GPL 协议,你可以修改源码来开发自己的 Mysql 系统。
- . MySQL 使用标准的 SQL 数据语言形式。
- Mysql 可以允许于多个系统上,并且支持多种语言。这些编程语言包括 C、C++、Python、Java、Perl、PHP、 Eiffel、Ruby 和 Tcl 等。
- MySQL 支持大型数据库,支持 5000 万条记录的数据仓库,32 位系统表文件最大可支持 4GB,64 位系统支 持最大的表文件为 8TB。
1.2. Mysql 高手是怎样练成的
- 职位:
- DBA(数据库管理员)、
- 运维人员、
- 项目经理、
- 技术经理
- 开发人员
- 技能:
- 数据库内部结构和原理 (运维、开发)
- 数据库建模优化 (开发)
- 数据库索引建立 (开发)
- SQL 语句优化 (开发)
- SQL 编程(自定义函数、存储过程、触发器、定时任务) (DBA、运维)
- mysql 服务器的安装配置 (运维、开发)
- 数据库的性能监控分析与系统优化 (DBA、运维)
- 各种参数常量设定(运维,开发简单了解)
- 主从复制 (开发出方案、运维执行)
- 分布式架构搭建、垂直切割和水平切割(开发出方案、运维执行)
- 数据迁移 (开发出方案、运维执行)
- 容灾备份和恢复 (运维)
- shell 或 python 等脚本语言开发 (运维,开发了解)
- 对开源数据库进行二次开发(专家级人物)
1.3.在 Linux 上安装 Mysql
1.3.1准备工作
1.3.1.1检查当前系统是否安装过 Mysql
- (1)如果CentOS6 环境下
命令:rpm -qa|grep mysql
默认 Linux 在安装的时候,自带了 mysql 相关的组件。
先卸载系统自带的 mysql,执行卸载命令 rpm -e --nodeps mysql-libs
- (2)如果CentOS7 环境下 命令:rpm -qa|grep mariadb
默认 Linux(CentOS7)在安装的时候,自带了 mariadb(mysql 完全开源版本)相关的组件。 先卸载系统自带的 mariadb,执行卸载命令 rpm -e --nodeps mariadb-libs
1.3.1.2检查/tmp 文件夹权限
- 查看/tmp 文件夹权限:
- 赋予其最大权限:
此时上面查看的已是最大权限,若不是可以用下面指令操作
1.3.2 Mysql 的安装
- 安装的版本是 mysql 5.7
- 官网下载.
选择对应的Linux版本和x86/x64进行下载
可以选择 RPM Bundle,下载完记得解压 tar -xvf xxx.tar
也可以细化下载,下载须要的mysql组件,有4个:分别是 server、client、common、libs(我选择的这个)
下面的安装版本是资料里的5.7.16,没用新版安装,不过安装过程一样
- ①将 rpm 安装包拷贝到 opt 目录下
- ②在安装目录下执行 rpm 安装(必须按顺序)
- 1.首先安装rpm -ivh mysql-community-common-5.7.16-1.el7.x86_64.rpm
- 2.其次安装依赖类库rpm -ivh mysql-community-libs-5.7.16-1.el7.x86_64.rpm
这时如果出错了
出错原因:没有安装时所需的GLIBC_2.14版本
解决办法:
使用yum更新安装依赖
- 1.首先安装rpm -ivh mysql-community-common-5.7.16-1.el7.x86_64.rpm
sudo yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make -y
下载rpm包(下载时间挺长的,可能我的电脑不行吧)
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-utils-2.17-55.el6.x86_64.rpm &
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-static-2.17-55.el6.x86_64.rpm &
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-2.17-55.el6.x86_64.rpm &
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-common-2.17-55.el6.x86_64.rpm &
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-devel-2.17-55.el6.x86_64.rpm &
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-headers-2.17-55.el6.x86_64.rpm &
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/nscd-2.17-55.el6.x86_64.rpm &
下载后安装包如下:
安装rpm包
sudo rpm -Uvh *-2.17-55.el6.x86_64.rpm --force --nodeps
安装完毕后,再次查看版本
可以看到一次性增加了 13 到 17 的版本号。
- 2.再次安装依赖类库rpm -ivh mysql-community-libs-5.7.16-1.el7.x86_64.rpm
- 3.然后安装客户端 rpm -ivh mysql-community-client-5.7.16-1.el7.x86_64.rpm
- 4.最后安装server rpm -ivh mysql-community-server-5.7.16-1.el7.x86_64.rpm
③查看是否安装成功:mysqladmin --version
或者也可以通过 rpm 命令来查看:
1.3.3 Mysql 服务
1.3.3.1mysql服务初始化
- mysql5.5自动初始化,5.7需要手动,如果你是以root身份进行mysql服务,需执行以下命令初始化
mysqld --initialize --user=mysql
- 另外,–initialize选项默认以“安全”模式来初始化,则会为root用户生成一个密码并将该密码标记为过期,登录之后你需要设置一个新的密码
- 查看密码
root@localhost:后面就是初始化的密码
1.3.3.2Mysql 服务的启动和停止
-
启动:systemctl start mysqld
-
确认是否启动
- 方法一查看状态systemctl status mysqld
- 方法一查看状态systemctl status mysqld
-
登录mysql: mysql –u root -p
然后输入初始化密码 -
首次登陆之后因为初始化密码是过期的,所以查看数据库会报错,需要修改密码:
ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘新密码’;
-
查看数据库后,退出(quit)
-
之后的登录
-
关闭mysql服务
systemctl stop mysqld
1.3.3.3安装位置
- 在Linux下查看安装目录
ps -ef|grep mysqld
1.3.3.4自启动mysql服务
- 查看是否自启动
1.3.4修改字符集问题
1.3.4.1常用命令
测试:
1.3.4.2 字符集乱码原因
- 如果在建库建表的时候,没有明确指定字符集,则采用默认的字符集 latin1,其中是不包含中文字符的。
- 查看默认的编码字符集:
1.3.4.3永久修改
-
1.修改配置文件
vim /etc/mycnf
在最后加上中文字符集配置
character_set_server=utf8
-
修改完之后查看之前的库的编码
注意:已经创建的数据库的设定不会发生变化,参数修改只对新建的数据库有效 -
2.修改已创建库、表字符集
修改数据库的字符集
alter database mydb character set ‘utf8’;
修改数据表的字符集
alter table mytable convert to character set ‘utf8’;
现在插入中文
查看添加张三成功
-
3.修改已经乱码数据
无论是修改 mysql 配置文件或是修改库、表字符集,都无法改变已经变成乱码的数据。 只能删除数据重新插入或更新数据才可以完全解决
第 2 章 MySql 的用户和权限管理
2.1Mysql 的用户管理
- 创建名称为 zhang3 的用户, 密码设为 123456;
create user zhang3 identified by ‘123456’; - 查看用户和权限的相关信 息
select host,user,authentication_string from user;
- 接下来进行mysql远程连接
可能会出现错误及解决办法:
MySQL远程连接失败(错误码:2003)
查看防火墙是否开放3306端口,以及查看云服务器的配置规则是否加入3306端口,附:
firewall-cmd --permanent --zone=public --add-port=3306/tcp 防火墙开启3306端口
firewall-cmd --reload 重启防火墙
firewall-cmd --list-ports 查看防火墙开放端口
- 设置密码:
- 修改当前用户密码
set password =password(‘123456’) - 修改某个用户的密码
update mysql.user set password=password(‘123456’) where user=‘li4’;
注:所有通过 user 表的修改,必须 用 **flush privileges;**命令才能生 效
- 修改当前用户密码
- 修改用户名:
- update mysql.user set user=‘li4’ where user=‘wang5’;
- 注:所有通过 user 表的修改,必须 用 **flush privileges;**命令才能生 效
- 删除用户:
- drop user li4
- 注:不要通过 delete from user u where user=‘li4’ 进行删除,系 统会有残留信息保留。
2.2Mysql 的权限管理
2.2.1授予权限
- 授权命令(该权限如果发现没有该用户,则会直接新建一个用户):
- grant 权限 1,权限 2,…权限 n on 数据库名称. 表名称 to 用户名@用户地址 identified by ‘连接口令’
- 举例:
- grant select,insert,delete,drop on mydb.* to zhang3@localhost ;
- 授予所有权限:grant all privileges on
∗
.
∗
*.*
∗.∗ to root@’%’ identified by ‘123456’;
远程登录root用户
2.2.2收回权限(一般用不到,了解即可)
- 查看当前用户权限
- show grants
- show grants
- 收回权限命令:
- revoke [权限 1,权限 2,…权限 n] on 库名.表名 from 用户名@用户地址 ;
- 收回全库全表的所有权限:
- REVOKE ALL PRIVILEGES ON mysql.* FROM joe@localhost;
- 收回 mysql 库下的所有表的插删改查 权限:
- REVOKE select,insert,update,delete ON mysql.* FROM joe@localhost;
第 3 章 Mysql的逻辑架构
3.1 Mysql的一些杂项配置
- 首先举例引入:
- 创建mytbl2表,插入数据,并查询
CREATE TABLE mytbl2 (id INT,NAME VARCHAR(200),age INT,dept INT);
INSERT INTO mytbl2 VALUES(1,'zhang3',33,101);
INSERT INTO mytbl2 VALUES(2,'li4',34,101);
INSERT INTO mytbl2 VALUES(3,'wang5',34,102);
INSERT INTO mytbl2 VALUES(4,'zhao6',34,102);
INSERT INTO mytbl2 VALUES(5,'tian7',36,102);
#每个机构年龄最大的人
SELECT NAME,dept,MAX(age) FROM mytbl2 GROUP BY dept;
- 发现上面查询语句是错的
- group by使用原则:select 后面只能放 函数 和group by后的字段
正确查询语句
SELECT * FROM mytbl2 m INNER JOIN(
SELECT dept,MAX(age)maxage FROM mytbl2 GROUP BY dept
)ab ON ab.dept=m.dept AND m.age=ab.maxage
结果:
- MySQL的sql_mode合理设置
- sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入。在生产环境必须将这个值设置为严格模式,所以开发、测试环境的数据库也必须要设置,这样在开发测试阶段就可以发现问题。
- sql_mode常用值如下:
set sql_mode=‘ONLY_FULL_GROUP_BY’;
- 查看:show variables like ‘sql_mode’;
3.2 Mysql的逻辑架构
3.2.1总体概览
自己理解的:
- 和其它数据库相比,MySQL 有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在 存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可 以根据业务的需求和实际需要选择合适的存储引擎。
- 各层介绍:
- 1 连接层 最上层是一些客户端和连接服务,包含本地 sock 通信和大多数基于客户端/服务端工具实现的类似于 tcp/ip 的 通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于 SSL 的安全链接。服务器也会为安全接入的每个客户端验 证它所具有的操作权限。
- 2 服务层
- 3 引擎层存储引擎层,存储引擎真正的负责了 MySQL 中数据的存储和提取,服务器通过 API 与存储引擎进行通信。不同 的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取
- 4.存储层 数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
3.2.2 利用show profile 查看sql的执行周期
- 修改配置文件/etc/my.cnf
新增一行:query_cache_type=1
重启mysql - 先开启 show variables like ‘%profiling%’;
- 发现profiling是关的 ,直接用命令set profiling=1;
- 开启profiling之后,就可以实行执行计划了,使用select * from xxx ;查询
- show profiles; #显示最近的几次查询
- show profile cpu,block io for query 编号 #查看程序的执行步骤
3.2.3mysql的大致查询流程
-
mysql客户端通过协议与mysql服务器建连接,发送查询语句,先检查查询缓存,如果命中,直接返回结果,否则进行语句解析,也就是说,在解析查询之前,服务器会先访问查询缓存(query cache)——它存储SELECT语句以及相应的查询结果集。如果某个查询结果已经位于缓存中,服务器就不会再对查询进行解析、优化、以及执行。它仅仅将缓存中的结果返回给用户即可,这将大大提高系统的性能。
-
语法解析器和预处理:首先mysql通过关键字将SQL语句进行解析,并生成一颗对应的“解析树”。mysql解析器将使用mysql语法规则验证和解析查询;预处理器则根据一些mysql规则进一步检查解析数是否合法。
-
查询优化器当解析树被认为是合法的了,并且由优化器将其转化成执行计划。一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。。
-
然后,mysql默认使用的BTREE索引,并且一个大致方向是:无论怎么折腾sql,至少在目前来说,mysql最多只用到表中的一个索引。
第 4 章 Mysql存储引擎
4.1查询命令
- #看你的mysql现在已提供什么存储引擎:
show engines;
- #看你的mysql当前默认的存储引擎:
mysql> show variables like ‘%storage_engine%’;
4.2.各个引擎介绍
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%。
4、Blackhole引擎
Blackhole引擎没有实现任何存储机制,它会丢弃所有插入的数据,不做任何保存。但服务器会记录Blackhole表的日志,所以可以用于复制数据到备库,或者简单地记录到日志。但这种应用方式会碰到很多问题,因此并不推荐。
5、CSV引擎
CSV引擎可以将普通的CSV文件作为MySQL的表来处理,但不支持索引。
CSV引擎可以作为一种数据交换的机制,非常有用。
CSV存储的数据直接可以在操作系统里,用文本编辑器,或者excel读取。
6、Memory引擎
如果需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没有关系,那么使用Memory表是非常有用。Memory表至少比MyISAM表要快一个数量级。
7、Federated引擎
Federated引擎是访问其他MySQL服务器的一个代理,尽管该引擎看起来提供了一种很好的跨服务器的灵活性,但也经常带来问题,因此默认是禁用的。
4.3.MyISAM和InnoDB区别
第五章 预热
- 性能下降SQL慢 执行时间长 等待时间长的原因及解决方法:
- 数据过多,解决方法:分库分表
- 关联了太多的表,太多join解决方法:SQL优化
- 没有充分利用到索引,解决方法:索引建立(最好)
- 服务器调优及各个参数设置,解决方法:调整my.cnf
- 优化手段索引建立最优
5.1常见的join查询图
5.2 建表SQL
USE mydb;
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 AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `t_emp` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`deptId` INT(11) DEFAULT NULL,
empno INT NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_dept_id` (`deptId`)
#CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 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);
SELECT * FROM t_dept;
SELECT * FROM t_emp;
结果(两个表):
5.3 7种JOIN
- 1 所有 有门派的人员信息
( A、B两表共有)
- 2 列出所有用户,并显示其机构信息
(A的全集)
- 3 列出所有门派 ,并显示其门派成员
(B的全集)
- 4 所有不入门派的人员
(A的独有)
- 5 所有没人入的门派
(B的独有)
- 6 列出所有人员和机构的对照关系
(AB全有)
#MySQL Full Join的实现 因为MySQL不支持FULL JOIN,下面是替代方法
#left join + union(可去除重复数据)+ right join
- 注:union和union all的区别
- 对比上面两个图,union可以去重,union all不能去重
- 同时两者都要保证前后合并的查询字段数是一样的
- 但是union all效率高
- 所以要想用union all列出所有人员和机构的对照关系如下
- 7 列出所有没入派的人员和没人入的门派
(A的独有+B的独有)
- 读到这里是不是发现很简单啊,这不都是mysql基础学的吗?为增加难度,我们增加一个字段
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;
生成此表:
- 求各个门派对应的掌门人名称:
- 求所有当上掌门人的平均年龄:.
- 求所有人物对应的掌门名称:
方法一:
方法二:
方式三:
方式四:
5.4 八道SQL语句题目
5.4.1、列出自己的掌门比自己年龄小的人员
#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);
5.4.2、列出所有年龄低于自己门派平均年龄的人员
#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)
5.4.3、列出至少有2个年龄大于40岁的成员的门派
#3、列出至少有2个年龄大于40岁的成员的门派
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
优化(记得删除之前的索引),换成大的数据表操作:
优化前:
STRAIGHT_JOIN 强制确定驱动表和被驱动表 1、概念非常明确 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)
5.4.4、至少有2位非掌门人成员的门派
SELECT c.deptname, c.id,COUNT(*) FROM t_emp a
INNER JOIN t_dept c ON a.`deptId` =c.`id`
LEFT JOIN t_dept b ON a.`id`=b.`ceo`
WHERE b.`id` IS NULL
GROUP BY c.`id` ,c.deptname
HAVING COUNT(*)>=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_deptname ON dept(ceo,deptname);
CREATE INDEX idx_deptname ON dept(deptname);
CREATE INDEX idx_deptid ON emp(deptid);
5.4.5列出全部人员,并增加一列备注“是否为掌门”,如果是掌门人显示是,不是掌门人显示否
#5、列出全部人员,并增加一列备注“是否为掌门”,如果是掌门人显示是,不是掌门人显示否
CASE WHEN
IF
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`
5.4.6、列出全部门派,并增加一列备注“老鸟or菜鸟”,若门派的平均值年龄>50显示“老鸟”,否则显示“菜鸟”
#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`
5.4.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
5.4.8、显示每个门派年龄第二大的人
#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;
如果年龄一样会出现bug
这样改:
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