中国建设招标网网站首页,互联网行业发展前景分析报告,淘宝美工培训,建设局象山网站MySQL专题0.准备sql设计一张员工信息表#xff0c;要求如下#xff1a;编号#xff08;纯数字#xff09;员工工号 (字符串类型#xff0c;长度不超过10位)员工姓名#xff08;字符串类型#xff0c;长度不超过10位#xff09;性别#xff08;男/女#xff0c;存储一…MySQL专题0.准备sql设计一张员工信息表要求如下编号纯数字员工工号 (字符串类型长度不超过10位)员工姓名字符串类型长度不超过10位性别男/女存储一个汉字年龄正常人年龄不可能存储负数身份证号二代身份证号均为18位身份证中有X这样的字符入职时间取值年月日即可对应的建表语句如下:drop table if exists emp;create table emp(id int comment 编号,workno varchar(10) comment 工号,name varchar(10) comment 姓名,gender char(1) comment 性别,age tinyint unsigned comment 年龄,idcard char(18) comment 身份证号,workaddress varchar(50) comment 工作地址,entrydate date comment 入职时间
)comment 员工表;INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (1, 1, 柳岩666, 女, 20, 123456789012345678, 北京, 2000-01-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (2, 2, 张无忌, 男, 18, 123456789012345670, 北京, 2005-09-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (3, 3, 韦一笑, 男, 38, 123456789712345670, 上海, 2005-08-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (4, 4, 赵敏, 女, 18, 123456757123845670, 北京, 2009-12-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (5, 5, 小昭, 女, 16, 123456769012345678, 上海, 2007-07-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (6, 6, 杨逍, 男, 28, 12345678931234567X, 北京, 2006-01-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (7, 7, 范瑶, 男, 40, 123456789212345670, 北京, 2005-05-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (8, 8, 黛绮丝, 女, 38, 123456157123645670, 天津, 2015-05-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (9, 9, 范凉凉, 女, 45, 123156789012345678, 北京, 2010-04-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (10, 10, 陈友谅, 男, 53, 123456789012345670, 上海, 2011-01-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (11, 11, 张士诚, 男, 55, 123567897123465670, 江苏, 2015-05-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (12, 12, 常遇春, 男, 32, 123446757152345670, 北京, 2004-02-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (13, 13, 张三丰, 男, 88, 123656789012345678, 江苏, 2020-11-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (14, 14, 灭绝, 女, 65, 123456719012345670, 西安, 2019-05-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (15, 15, 胡青牛, 男, 70, 12345674971234567X, 西安, 2018-04-01);
INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate) VALUES (16, 16, 周芷若, 女, 18, null, 北京, 2012-06-01);1.关于sql关键字的 执行顺序我们要来说明的是DQL语句在执行时的执行顺序也就是先执行那一部分后执行那一部分。验证查询年龄大于15的员工姓名、年龄并根据年龄进行升序排序。select name , age from emp where age 15 order by age asc;在查询时我们给emp表起一个别名 e然后在select 及 where中使用该别名。select e.name , e.age from emp e where e.age 15 order by age asc;执行上述SQL语句后我们看到依然可以正常的查询到结果此时就说明 from 先执行, 然后 where 和 select 执行。那 where 和 select 到底哪个先执行呢?此时此时我们可以给select后面的字段起别名然后在 where 中使用这个别名然后看看是否可以执行成功。select e.name ename , e.age eage from emp e where eage 15 order by age asc;执行上述SQL报错了:由此我们可以得出结论: from 先执行然后执行 where 再执行select 。接下来我们再执行如下SQL语句查看执行效果select e.name ename , e.age eage from emp e where e.age 15 order by eage asc;结果执行成功。 那么也就验证了: order by 是在select 语句之后执行的。综上所述我们可以看到DQL语句的执行顺序为 from ... where ... group by ... having ... select ... order by ... limit ...2. 函数函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着这一段程序或代码在MySQL中已经给我们提供了我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即可。 那么函数到底在哪儿使用呢我们先来看两个场景1). 在企业的OA或其他的人力系统中经常会提供的有这样一个功能每一个员工登录上来之后都能够看到当前员工入职的天数。 而在数据库中存储的都是入职日期如 2000-11-12那如果快速计算出天数呢2). 在做报表这类的业务需求中,我们要展示出学员的分数等级分布。而在数据库中存储的是学生的分数值如98/75如何快速判定分数的等级呢其实上述的这一类的需求呢我们通过MySQL中的函数都可以很方便的实现 。MySQL中的函数主要分为以下四类 字符串函数、数值函数、日期函数、流程函数。2.1 字符串函数MySQL中内置了很多字符串函数常用的几个如下函数功能CONCAT(S1,S2,...Sn)字符串拼接将S1S2... Sn拼接成一个字符串LOWER(str)将字符串str全部转为小写UPPER(str)将字符串str全部转为大写LPAD(str,n,pad)左填充用字符串pad对str的左边进行填充达到n个字符串长度RPAD(str,n,pad)右填充用字符串pad对str的右边进行填充达到n个字符串长度TRIM(str)去掉字符串头部和尾部的空格(中间的不可以去除)SUBSTRING(str,start,len)返回从字符串str从start位置起的len个长度的字符串,从1开始演示如下A. concat : 字符串拼接select concat(Hello , MySQL);B. lower : 全部转小写select lower(HELLO);C. upper : 全部转大写select upper(Hello);D. lpad : 左填充select lpad(hello, 8, -);E. rpad : 右填充select rpad(hello, 8, -);F. trim : 去除空格select trim( Hello MySQL );G. substring : 截取子字符串select substring(Hello MySQL,1,5);案例:由于业务需求变更企业员工的工号统一为5位数目前不足5位数的全部在前面补0。比如 1号员工的工号应该为00001。 update emp set workno lpad(workno, 5, 0);处理完毕后, 具体的数据为:2.2 数值函数常见的数值函数如下函数功能CEIL(x)向上取整FLOOR(x)向下取整MOD(x,y)返回x/y的模RAND()返回0~1内的随机数ROUND(x,y)求参数x的四舍五入的值保留y位小数演示如下A. ceil向上取整select ceil(1.1);B. floor向下取整select floor(1.9);C. mod取模select mod(7,4);D. rand获取随机数select rand();E. round四舍五入select round(2.344,2);案例通过数据库的函数生成一个六位数的随机验证码。思路 获取随机数可以通过rand()函数但是获取出来的随机数是在0-1之间的所以可以在其基础上乘以1000000然后舍弃小数部分如果长度不足6位补0select lpad(round(rand()*1000000 , 0), 6, 0);2.3 日期函数常见的日期函数如下函数功能CURDATE()返回当前日期CURTIME()返回当前时间NOW()返回当前日期和时间YEAR(date)获取指定date的年份MONTH(date)获取指定date的月份DAY(date)获取指定date的日期DATE_ADD(date, INTERVAL expr type)返回一个日期/时间值加上一个时间间隔expr后的时间值DATEDIFF(date1,date2)返回起始时间date1 和 结束时间date2之间的天数演示如下A. curdate当前日期select curdate();B. curtime当前时间select curtime();C. now当前日期和时间select now();D. YEAR , MONTH , DAY当前年、月、日select YEAR(now());
select MONTH(now());
select DAY(now());
-- 都查
SELECT YEAR(NOW()) year ,MONTH(NOW()) month, DAY(NOW()) dayE. date_add增加指定的时间间隔70年后的今天的日期select date_add(now(), INTERVAL 70 YEAR );F. datediff获取两个日期相差的天数select datediff(2021-12-01, 2021-10-01);案例查询所有员工的入职天数并根据入职天数倒序排序。思路 入职天数就是通过当前日期 - 入职日期所以需要使用datediff函数来完成。select name, datediff(curdate(), entrydate) as entrydays from emp order by entrydays desc;2.4 流程函数流程函数也是很常用的一类函数可以在SQL语句中实现条件筛选从而提高语句的效率。函数功能IF(value , t , f)如果value为true则返回t否则返回fIFNULL(value1 , value2)如果value1不为空返回value1否则返回value2CASE WHEN [ val1 ] THEN [res1] ... ELSE [ default ] END如果val1为true返回res1... 否则返回default默认值CASE [ expr ] WHEN [ val1 ] THEN [res1] ... ELSE [ default ] END如果expr的值等于val1返回res1... 否则返回default默认值演示如下A. if ,如果参数1true的话那么则返回ok字符,否则返回Error字符select if(false, Ok, Error);B: ifnull如果参数1不等于空null则返回参数1否则返回参数2select ifnull(Ok,Default);select ifnull(,Default);select ifnull(null,Default);
-- 一条sql
select IFNULL(ok,Default) as param1, IFNULL(,Default) as param2, IFNULL(null,Default) as param3C. case when then else end需求: 查询emp表的员工姓名和工作地址 (北京/上海 ---- 一线城市 , 其他 ---- 二线城市)-- 第一种做法
SELECT name,
(CASE workaddressWHEN 北京 THEN一线城市WHEN 上海 THEN一线城市ELSE二线城市
END
) as 工作地址
FROM emp;--第二种做法
SELECT name,
(CASE WHEN workaddress北京 THEN一线城市WHEN workaddress上海 THEN一线城市ELSE二线城市
END
) as 工作地址
FROM emp;案例:查询出每个学生对应的id,name及每科的学习情况(比如 85分以上优秀60分以上及格否则不及格)create table score(id int comment ID,name varchar(20) comment 姓名,math int comment 数学,english int comment 英语,chinese int comment 语文
) comment 学员成绩表;
insert into score(id, name, math, english, chinese) VALUES (1, Tom, 67, 88, 95 ), (2, Rose , 23, 66, 90),(3, Jack, 56, 98, 76);具体的SQL语句如下:SELECT id ,name,
(CASE WHEN math85 THEN优秀WHEN math60 THEN及格ELSE不及格
END
) as 数学,
(CASE WHEN english85 THEN优秀WHEN english60 THEN及格ELSE不及格
END
) as 英语,
(CASE WHEN chinese85 THEN优秀WHEN chinese60 THEN及格ELSE不及格
END
) as 语文FROM scoreMySQL的常见函数我们学习完了那接下来我们就来分析一下在前面讲到的两个函数的案例场景思考一下需要用到什么样的函数来实现?1). 数据库中存储的是入职日期如 2000-01-01如何快速计算出入职天数呢 -------- 答案: datediff2). 数据库中存储的是学生的分数值如98、75如何快速判定分数的等级呢 ---------- 答案: case ... when ...3. 多表查询我们之前在讲解SQL语句的时候讲解了DQL语句也就是数据查询语句但是之前讲解的查询都是单表查询而本章节我们要学习的则是多表查询操作主要从以下几个方面进行讲解。3.1 多表关系项目开发中在进行数据库表结构设计时会根据业务需求及业务模块之间的关系分析并设计表结构由于业务之间相互关联所以各个表结构之间也存在着各种联系基本上分为三种一对多(多对一)多对多一对一3.1.1 一对多案例: 部门 与 员工的关系关系: 一个部门对应多个员工一个员工对应一个部门实现: 在多的一方建立外键指向一的一方的主键3.1.2 多对多案例: 学生 与 课程的关系关系: 一个学生可以选修多门课程一门课程也可以供多个学生选择实现: 建立第三张中间表中间表至少包含两个外键分别关联两方主键对应的SQL脚本:create table student(id int auto_increment primary key comment 主键ID,name varchar(10) comment 姓名,no varchar(10) comment 学号
) comment 学生表;
insert into student values (null, 黛绮丝, 2000100101),(null, 谢逊, 2000100102),(null, 殷天正, 2000100103),(null, 韦一笑, 2000100104);create table course(id int auto_increment primary key comment 主键ID,name varchar(10) comment 课程名称
) comment 课程表;
insert into course values (null, Java), (null, PHP), (null , MySQL) , (null, Hadoop);create table student_course(id int auto_increment comment 主键 primary key,studentid int not null comment 学生ID,courseid int not null comment 课程ID,constraint fk_courseid foreign key (courseid) references course (id),constraint fk_studentid foreign key (studentid) references student (id)
)comment 学生课程中间表;insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3),(null,3,4);3.1.3 一对一案例: 用户 与 用户详情的关系关系: 一对一关系多用于单表拆分将一张表的基础字段放在一张表中其他详情字段放在另一张表中以提升操作效率实现: 在任意一方加入外键关联另外一方的主键并且设置外键为唯一的(UNIQUE)对应的SQL脚本:create table tb_user(id int auto_increment primary key comment 主键ID,name varchar(10) comment 姓名,age int comment 年龄,gender char(1) comment 1: 男 , 2: 女,phone char(11) comment 手机号
) comment 用户基本信息表;create table tb_user_edu(id int auto_increment primary key comment 主键ID,degree varchar(20) comment 学历,major varchar(50) comment 专业,primaryschool varchar(50) comment 小学,middleschool varchar(50) comment 中学,university varchar(50) comment 大学,userid int unique comment 用户ID,constraint fk_userid foreign key (userid) references tb_user(id)
) comment 用户教育信息表;insert into tb_user(id, name, age, gender, phone) values(null,黄渤,45,1,18800001111),(null,冰冰,35,2,18800002222),(null,码云,55,1,18800008888),(null,李彦宏,50,1,18800009999);insert into tb_user_edu(id, degree, major, primaryschool, middleschool, university, userid) values(null,本科,舞蹈,静安区第一小学,静安区第一中学,北京舞蹈学院,1),(null,硕士,表演,朝阳区第一小学,朝阳区第一中学,北京电影学院,2),(null,本科,英语,杭州市第一小学,杭州市第一中学,杭州师范大学,3),(null,本科,应用数学,阳泉第一小学,阳泉区第一中学,清华大学,4);3.2 多表查询概述3.2.1 数据准备1). 删除之前 emp, dept表的测试数据2). 执行如下脚本创建emp表与dept表并插入测试数据-- 创建dept表并插入数据
create table dept(id int auto_increment comment ID primary key,name varchar(50) not null comment 部门名称
)comment 部门表;
INSERT INTO dept (id, name) VALUES (1, 研发部), (2, 市场部),(3, 财务部), (4, 销售部), (5, 总经办), (6, 人事部);-- 创建emp表并插入数据
create table emp(id int auto_increment comment ID primary key,name varchar(50) not null comment 姓名,age int comment 年龄,job varchar(20) comment 职位,salary int comment 薪资,entrydate date comment 入职时间,managerid int comment 直属领导ID,dept_id int comment 部门ID
)comment 员工表;
-- 添加外键
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id) VALUES(1, 金庸, 66, 总裁,20000, 2000-01-01, null,5),(2, 张无忌, 20, 项目经理,12500, 2005-12-05, 1,1),(3, 杨逍, 33, 开发, 8400,2000-11-03, 2,1),(4, 韦一笑, 48, 开发,11000, 2002-02-05, 2,1),(5, 常遇春, 43, 开发,10500, 2004-09-07, 3,1),(6, 小昭, 19, 程序员鼓励师,6600, 2004-10-12, 2,1),(7, 灭绝, 60, 财务总监,8500, 2002-09-12, 1,3),(8, 周芷若, 19, 会计,48000, 2006-06-02, 7,3),(9, 丁敏君, 23, 出纳,5250, 2009-05-13, 7,3),(10, 赵敏, 20, 市场部总监,12500, 2004-10-12, 1,2),(11, 鹿杖客, 56, 职员,3750, 2006-10-03, 10,2),(12, 鹤笔翁, 19, 职员,3750, 2007-05-09, 10,2),(13, 方东白, 19, 职员,5500, 2009-02-12, 10,2),(14, 张三丰, 88, 销售总监,14000, 2004-10-12, 1,4),(15, 俞莲舟, 38, 销售,4600, 2004-10-12, 14,4),(16, 宋远桥, 40, 销售,4600, 2004-10-12, 14,4),(17, 陈友谅, 42, null,2000, 2011-10-12, 1,null);dept表共6条记录emp表共17条记录。3.2.2 概述多表查询就是指从多张表中查询数据。原来查询单表数据执行的SQL形式为select * from emp;那么我们要执行多表查询就只需要使用逗号分隔多张表即可如 select * from emp , dept ; 具体的执行结果如下:此时,我们看到查询结果中包含了大量的结果集总共102条记录而这其实就是员工表emp所有的记录(17) 与 部门表dept所有记录(6) 的所有组合情况这种现象称之为笛卡尔积。接下来就来简单介绍下笛卡尔积。笛卡尔积: 笛卡尔乘积是指在数学中两个集合A集合 和 B集合的所有组合情况。而在多表查询中我们是需要消除无效的笛卡尔积的只保留两张表关联部分的数据。在SQL语句中如何来去除无效的笛卡尔积呢 我们可以给多表查询加上连接查询的条件即可。select * from emp , dept where emp.dept_id dept.id;而由于id为17的员工没有dept_id字段值所以在多表查询时根据连接查询的条件并没有查询到。3.2.3 分类连接查询内连接相当于查询A、B交集部分数据外连接左外连接查询左表所有数据以及两张表交集部分数据右外连接查询右表所有数据以及两张表交集部分数据自连接当前表与自身的连接查询自连接必须使用表别名子查询3.3 内连接内连接查询的是两张表交集部分的数据。(也就是绿色部分的数据)内连接的语法分为两种: 隐式内连接、显式内连接。先来学习一下具体的语法结构。1). 隐式内连接SELECT 字段列表 FROM 表1 , 表2 WHERE 条件 ... ;2). 显式内连接SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ... ;案例:A. 查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)表结构: emp , dept连接条件: emp.dept_id dept.idselect emp.name , dept.name from emp , dept where emp.dept_id dept.id ;-- 为每一张表起别名,简化SQL编写
select e.name,d.name from emp e , dept d where e.dept_id d.id;B. 查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现) --- INNER JOIN ... ON ...表结构: emp , dept连接条件: emp.dept_id dept.idselect e.name, d.name from emp e inner join dept d on e.dept_id d.id;-- 为每一张表起别名,简化SQL编写
select e.name, d.name from emp e join dept d on e.dept_id d.id;表的别名: ①. tablea as 别名1 , tableb as 别名2 ; ②. tablea 别名1 , tableb 别名2 ;注意事项: 一旦为表起了别名就不能再使用表名来指定对应的字段了此时只能够使用别名来指定字段。3.4 外连接外连接分为两种分别是左外连接 和 右外连接。具体的语法结构为1). 左外连接SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ;左外连接相当于查询表1(左表)的所有数据当然也包含表1和表2交集部分的数据。2). 右外连接SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;右外连接相当于查询表2(右表)的所有数据当然也包含表1和表2交集部分的数据。案例:A. 查询emp表的所有数据, 和对应的部门信息由于需求中提到要查询emp的所有数据所以是不能内连接查询的需要考虑使用外连接查询。表结构: emp, dept连接条件: emp.dept_id dept.idselect e.*, d.name from emp e left outer join dept d on e.dept_id d.id;select e.*, d.name from emp e left join dept d on e.dept_id d.id;B. 查询dept表的所有数据, 和对应的员工信息(右外连接)由于需求中提到要查询dept表的所有数据所以是不能内连接查询的需要考虑使用外连接查询。表结构: emp, dept连接条件: emp.dept_id dept.idselect d.*, e.* from emp e right outer join dept d on e.dept_id d.id;select d.*, e.* from dept d left outer join emp e on e.dept_id d.id;注意事项 左外连接和右外连接是可以相互替换的只需要调整在连接查询时SQL中表结构的先后顺序就可以了。而我们在日常开发使用时更偏向于左外连接。3.5 自连接3.5.1 自连接查询自连接查询顾名思义就是自己连接自己也就是把一张表连接查询多次。我们先来学习一下自连接的查询语法SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;而对于自连接查询可以是内连接查询也可以是外连接查询。案例A. 查询员工 及其 所属领导的名字表结构: empselect a.name , b.name from emp a , emp b where a.managerid b.id;B. 查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来表结构: emp a , emp bselect a.name 员工, b.name 领导 from emp a left join emp b on a.managerid b.id;注意事项: 在自连接查询中必须要为表起别名要不然我们不清楚所指定的条件、返回的字段到底是哪一张表的字段。3.5.2 联合查询对于union查询就是把多次查询的结果合并起来形成一个新的查询结果集。SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;对于联合查询的多张表的列数必须保持一致字段类型也需要保持一致。union all 会将全部的数据直接合并在一起union 会对合并之后的数据去重。案例:A. 将薪资低于 5000 的员工 , 和 年龄大于 50 岁的员工全部查询出来.当前对于这个需求我们可以直接使用多条件查询使用逻辑运算符 or 连接即可。 那这里呢我们也可以通过union/union all来联合查询.select * from emp where salary 5000
union all
select * from emp where age 50;union all查询出来的结果仅仅进行简单的合并并未去重。select * from emp where salary 5000
union
select * from emp where age 50;union 联合查询会对查询出来的结果进行去重处理。注意如果多条查询语句查询出来的结果字段数量不一致在进行union/union all联合查询时将会报错。如3.6 子查询3.6.1 概述1). 概念SQL语句中嵌套SELECT语句称为嵌套查询又称子查询。SELECT * FROM t1 WHERE column1 ( SELECT column1 FROM t2 );子查询外部的语句可以是INSERT / UPDATE / DELETE / SELECT 的任何一个。2). 分类根据子查询结果不同分为A. 标量子查询子查询结果为单个值B. 列子查询(子查询结果为一列)C. 行子查询(子查询结果为一行)D. 表子查询(子查询结果为多行多列)根据子查询位置分为A. WHERE之后B. FROM之后C. SELECT之后3.6.2 标量子查询子查询返回的结果是单个值数字、字符串、日期等最简单的形式这种子查询称为标量子查询。常用的操作符 案例:A. 查询 销售部 的所有员工信息完成这个需求时我们可以将需求分解为两步①. 查询 销售部 部门IDselect id from dept where name 销售部;②. 根据 销售部 部门ID, 查询员工信息select * from emp where dept_id (select id from dept where name 销售部);B. 查询在 方东白 入职之后的员工信息完成这个需求时我们可以将需求分解为两步①. 查询 方东白 的入职日期select entrydate from emp where name 方东白;②. 查询指定入职日期之后入职的员工信息select * from emp where entrydate (select entrydate from emp where name 方东白);3.6.3 列子查询子查询返回的结果是一列可以是多行这种子查询称为列子查询。常用的操作符IN 、NOT IN 、 ANY 、SOME 、 ALL操作符描述IN在指定的集合范围之内多选一NOT IN不在指定的集合范围之内ANY子查询返回列表中有任意一个满足即可SOME与ANY等同使用SOME的地方都可以使用ANYALL子查询返回列表的所有值都必须满足案例:A. 查询 销售部 和 市场部 的所有员工信息分解为以下两步:①. 查询 销售部 和 市场部 的部门IDselect id from dept where name 销售部 or name 市场部;②. 根据部门ID, 查询员工信息select * from emp where dept_id in (select id from dept where name 销售部 or name 市场部);B. 查询比 财务部 所有人工资都高的员工信息分解为以下两步:①. 查询所有 财务部 人员工资select id from dept where name 财务部;select salary from emp where dept_id (select id from dept where name 财务部);②. 比 财务部 所有人工资都高的员工信息select * from emp where salary all ( select salary from emp where dept_id (select id from dept where name 财务部) );C. 查询比研发部其中任意一人工资高的员工信息分解为以下两步:①. 查询研发部所有人工资select salary from emp where dept_id (select id from dept where name 研发部);②. 比研发部其中任意一人工资高的员工信息select * from emp where salary any ( select salary from emp where dept_id (select id from dept where name 研发部) );3.6.4 行子查询子查询返回的结果是一行可以是多列这种子查询称为行子查询。常用的操作符 、 、IN 、NOT IN案例:A. 查询与 张无忌 的薪资及直属领导相同的员工信息 ;这个需求同样可以拆解为两步进行:①. 查询 张无忌 的薪资及直属领导select salary, managerid from emp where name 张无忌;②. 查询与 张无忌 的薪资及直属领导相同的员工信息 ;select * from emp where (salary,managerid) (select salary, managerid from emp where name 张无忌);3.6.5 表子查询子查询返回的结果是多行多列这种子查询称为表子查询。常用的操作符IN案例:A. 查询与 鹿杖客 , 宋远桥 的职位和薪资相同的员工信息分解为两步执行:①. 查询 鹿杖客 , 宋远桥 的职位和薪资select job, salary from emp where name 鹿杖客 or name 宋远桥;②. 查询与 鹿杖客 , 宋远桥 的职位和薪资相同的员工信息select * from emp where (job,salary) in ( select job, salary from emp where name 鹿杖客 or name 宋远桥 );B. 查询入职日期是 2006-01-01 之后的员工信息 , 及其部门信息分解为两步执行:①. 入职日期是 2006-01-01 之后的员工信息select * from emp where entrydate 2006-01-01;②. 查询这部分员工, 对应的部门信息;select e.*, d.* from (select * from emp where entrydate 2006-01-01) e left join dept d on e.dept_id d.id ;3.7 多表查询案例数据环境准备:create table salgrade(grade int,losal int,hisal int
) comment 薪资等级表;insert into salgrade values (1,0,3000);
insert into salgrade values (2,3001,5000);
insert into salgrade values (3,5001,8000);
insert into salgrade values (4,8001,10000);
insert into salgrade values (5,10001,15000);
insert into salgrade values (6,15001,20000);
insert into salgrade values (7,20001,25000);
insert into salgrade values (8,25001,30000);在这个案例中我们主要运用上面所讲解的多表查询的语法完成以下的12个需求即可而这里主要涉及到的表就三张emp员工表、dept部门表、salgrade薪资等级表 。1). 查询员工的姓名、年龄、职位、部门信息 隐式内连接表: emp , dept连接条件: emp.dept_id dept.idselect e.name , e.age , e.job , d.name from emp e , dept d where e.dept_id d.id;2). 查询年龄小于30岁的员工的姓名、年龄、职位、部门信息显式内连接表: emp , dept连接条件: emp.dept_id dept.idselect e.name , e.age , e.job , d.name from emp e inner join dept d on e.dept_id d.id where e.age 30;3). 查询拥有员工的部门ID、部门名称表: emp , dept连接条件: emp.dept_id dept.idselect distinct d.id , d.name from emp e , dept d where e.dept_id d.id;4). 查询所有年龄大于40岁的员工, 及其归属的部门名称; 如果员工没有分配部门, 也需要展示出来(外连接)表: emp , dept连接条件: emp.dept_id dept.idselect e.*, d.name from emp e left join dept d on e.dept_id d.id where e.age 40 ;5). 查询所有员工的工资等级表: emp , salgrade连接条件 : emp.salary salgrade.losal and emp.salary salgrade.hisal-- 方式一
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary s.losal and e.salary s.hisal;
-- 方式二
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary between s.losal and s.hisal;6). 查询 研发部 所有员工的信息及 工资等级表: emp , salgrade , dept连接条件 : emp.salary between salgrade.losal and salgrade.hisal , emp.dept_id dept.id查询条件 : dept.name 研发部select e.* , s.grade from emp e , dept d , salgrade s where e.dept_id d.id and ( e.salary between s.losal and s.hisal ) and d.name 研发部;7). 查询 研发部 员工的平均工资表: emp , dept连接条件 : emp.dept_id dept.idselect avg(e.salary) from emp e, dept d where e.dept_id d.id and d.name 研发部;8). 查询工资比 灭绝 高的员工信息。①. 查询 灭绝 的薪资select salary from emp where name 灭绝;②. 查询比她工资高的员工数据select * from emp where salary ( select salary from emp where name 灭绝 );9). 查询比平均薪资高的员工信息①. 查询员工的平均薪资select avg(salary) from emp;②. 查询比平均薪资高的员工信息select * from emp where salary ( select avg(salary) from emp );10). 查询低于本部门平均工资的员工信息①. 查询指定部门平均薪资select avg(e1.salary) from emp e1 where e1.dept_id 1;
select avg(e1.salary) from emp e1 where e1.dept_id 2;②. 查询低于本部门平均工资的员工信息select * from emp e2 where e2.salary ( select avg(e1.salary) from emp e1 where e1.dept_id e2.dept_id );11). 查询所有的部门信息, 并统计部门的员工人数select d.id, d.name , ( select count(*) from emp e where e.dept_id d.id ) 人数 from dept d;12). 查询所有学生的选课情况, 展示出学生名称, 学号, 课程名称表: student , course , student_course连接条件: student.id student_course.studentid , course.id student_course.courseidselect s.name , s.no , c.name from student s , student_course sc , course c where s.id sc.studentid and sc.courseid c.id ;备注: 以上需求的实现方式可能会很多, SQL写法也有很多只要能满足我们的需求查询出符合条件的记录即可。3.8 行转列和列转行查询行列转换在做报表分析时还是经常会遇到的sql问题转换的样式行转列的互相转换具体的实现假如我们有下表1、创建表sqlCREATE TABLE student-sss (id int(11) NOT NULL AUTO_INCREMENT COMMENT 主键ID,name varchar(10) DEFAULT NULL COMMENT 姓名,subject varchar(10) DEFAULT NULL COMMENT 科目,score varchar(10) DEFAULT NULL COMMENT 分数,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT5 DEFAULT CHARSETutf8 COMMENT学生表;INSERT INTO student-sss VALUES(1,小明,语文,96),(2,小明,数学,98),(3,小明,英语,95),(4,大花,语文,92),(5,大花,数学,96),(6,大花,英语,98)2、方式一使用case...when...then进行行转列SELECT name,
SUM(CASE subject WHEN 语文 THEN score ELSE 0 END ) as 语文,
SUM(CASE subject WHEN 数学 THEN score ELSE 0 END ) as 数学,
SUM(CASE subject WHEN 英语 THEN score ELSE 0 END ) as 英语
FROM student-sss
GROUP BY name3、方式二使用IF() 进行行转列SELECT name,
SUM(IF(subject语文,score,0)) as 语文,
SUM(IF(subject数学,score,0)) as 数学,
SUM(IF(subject英语,score,0)) as 英语
FROM student-sss
GROUP BY name注意点1SUM() 是为了能够使用GROUP BY根据name进行分组因为每一个name对应的subject语文的记录只有一条所以SUM() 的值就等于对应那一条记录的score的值。假如name‘小明’ and subject‘语文’ 的记录有两条则此时SUM() 的值将会是这两条记录的和同理使用Max()的值将会是这两条记录里面值最大的一个。但是正常情况下一个user对应一个subject只有一个分数因此可以使用SUM()、MAX()、MIN()、AVG()等聚合函数都可以达到行转列的效果。2IF(subject‘语文’,score,0) 作为条件即对所有subject语文’的记录的score字段进行SUM()、MAX()、MIN()、AVG()操作如果score没有值则默认为0。列转行的互相转换具体的实现假如我们有下表1、创建表sqlCREATE TABLE student-ccc (id int(11) NOT NULL AUTO_INCREMENT COMMENT 主键ID,name varchar(10) DEFAULT NULL COMMENT 姓名,score-yu VARCHAR(4) DEFAULT NULL COMMENT 语文分数,score-shu VARCHAR(4) DEFAULT NULL COMMENT 数学分数,score-ying VARCHAR(4) DEFAULT NULL COMMENT 英语分数,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT5 DEFAULT CHARSETutf8 COMMENT学生表;INSERT INTO student-ccc VALUES(1,小明,96,98,95),(2,大花,92,96,98)2、列转行sql实现SELECT id,name,语文 as SUBJECT,score-yu as score FROM student-ccc
UNION ALL
SELECT id,name,数学 as SUBJECT,score-shu as score FROM student-ccc
UNION ALL
SELECT id,name,英语 as SUBJECT,score-ying as score FROM student-ccc
ORDER BY id4. 事务4.1 事务简介事务 是一组操作的集合它是一个不可分割的工作单位事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求即这些操作要么同时成功要么同时失败。就比如: 张三给李四转账1000块钱张三银行账户的钱减少1000而李四银行账户的钱要增加1000。 这一组操作就必须在一个事务的范围内要么都成功要么都失败。正常情况: 转账这个操作, 需要分为以下这么三步来完成 , 三步完成之后, 张三减少1000, 而李四增加1000, 转账成功 :异常情况: 转账这个操作, 也是分为以下这么三步来完成 , 在执行第三步是报错了, 这样就导致张三减少1000块钱, 而李四的金额没变, 这样就造成了数据的不一致, 就出现问题了。为了解决上述的问题就需要通过数据的事务来完成我们只需要在业务逻辑执行之前开启事务执行完毕后提交事务。如果执行过程中报错则回滚事务把数据恢复到事务开始之前的状态。注意 默认MySQL的事务是自动提交的也就是说当执行完一条DML语句时MySQL会立即隐式的提交事务。4.2 事务操作数据准备drop table if exists account;create table account(id int primary key AUTO_INCREMENT comment ID,name varchar(10) comment 姓名,money double(10,2) comment 余额
) comment 账户表;insert into account(name, money) VALUES (张三,2000), (李四,2000);4.2.1 未控制事务1). 测试正常情况-- 1. 查询张三余额
select * from account where name 张三;
-- 2. 张三的余额减少1000
update account set money money - 1000 where name 张三;
-- 3. 李四的余额增加1000
update account set money money 1000 where name 李四;测试完毕之后检查数据的状态, 可以看到数据操作前后是一致的。2). 测试异常情况-- 1. 查询张三余额
select * from account where name 张三;
-- 2. 张三的余额减少1000
update account set money money - 1000 where name 张三;
出错了....
-- 3. 李四的余额增加1000
update account set money money 1000 where name 李四;我们把数据都恢复到2000 然后再次一次性执行上述的SQL语句(出错了.... 这句话不符合SQL语法,执行就会报错)检查最终的数据情况, 发现数据在操作前后不一致了。l4.2.2 控制事务一1). 查看/设置事务提交方式SELECT autocommit ;
SET autocommit 0 ;2). 提交事务COMMIT;3). 回滚事务ROLLBACK;注意上述的这种方式我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提交, 此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交。autocommit1表示自动提交事务把值设置成0的话表示手动提交事务4.2.3 控制事务二1). 开启事务START TRANSACTION 或 BEGIN ;2). 提交事务COMMIT;3). 回滚事务ROLLBACK;转账案例-- 开启事务
start transaction -- 1. 查询张三余额
select * from account where name 张三;-- 2. 张三的余额减少1000
update account set money money - 1000 where name 张三;-- 3. 李四的余额增加1000
update account set money money 1000 where name 李四;-- 如果正常执行完毕, 则提交事务
commit;
-- 如果执行过程中报错, 则回滚事务
-- rollback;4.3 事务四大特性原子性Atomicity事务是不可分割的最小操作单元要么全部成功要么全部失败。一致性Consistency事务完成时必须使所有的数据都保持一致状态。隔离性Isolation数据库系统提供的隔离机制保证事务在不受外部并发操作影响的独立环境下运行。持久性Durability事务一旦提交或回滚它对数据库中的数据的改变就是永久的。上述就是事务的四大特性简称ACID。4.4 并发事务问题1). 脏读一个事务读到另外一个事务还没有提交的数据。比如B读取到了A未提交的数据。2). 不可重复读一个事务先后读取同一条记录但两次读取的数据不同称之为不可重复读。事务A两次读取同一条记录但是读取到的数据却是不一样的。3). 幻读一个事务按照条件查询数据时没有对应的数据行但是在插入数据时又发现这行数据已经存在好像出现了 幻影。演示第一步事务A去数据库查询id1的数据发现没有数据但是此时事务B新增了id1的数据并提交了此时数据库中有了id1的数据第二步事务A再次去新增id1的数据但是发现已经添加不进去了主键冲突异常第三步事务A再次去查询id1的数据但是还是没有查询到id1的数据此时就像出现了幻觉一样称之为幻读4.5 事务隔离级别为了解决并发事务所引发的问题在数据库中引入了事务隔离级别。主要有以下几种隔离级别脏读不可重复读幻读Read uncommitted【读未提交】√√√Read committed【读已提交】×√√Repeatable Read(默认)【可重复读】××√Serializable【串行化】×××注意事务隔离级别越高数据越安全但是性能越低。PSmysql的默认隔离级别是Repeatable Read但是如果数据库是oracle 的是Read committed4.5.1 查看和设置隔离级别1). 查看事务隔离级别mysql8的方式SELECT TRANSACTION_ISOLATION;mysql5.7的方式SELECT tx_isolation2). 设置事务隔离级别SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }SESSION表示窗口级别而GLOBAL是表示全局级别演示设置读未提交级别SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED查看隔离级别改回默认隔离级别[可重复读]SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ查看隔离级别4.5.2 利用隔离级别解决并发事务1 准备工作本次采用黑窗口的方式来分别模拟两个事务操作准备两个事务窗口两个窗口都设置手动提交事务2 演示Read uncommitted看是否会出现脏读问题注意从图上看到发现事务1中查看账户的时候张三账户少了1000元但是这个是由事务2中的修改操作来的但是这个事务并没有提交。所以导致出现了脏读问题。注意最后每个窗口都要执行以下commit提交事务。3 演示Read committed 看是否能出现脏读问题注意两个事务操作从1-7步骤发现当事务2没有提交之前事务1中查询的张三账户余额并没有发生改变这说明读已提交隔离级别控制了脏读问题当事务2提交了之后事务1中张三的余额才会发生变化。注意最后每个窗口都要执行以下commit提交事务。4 演示Read commited看是否出现 不可重复读问题从步骤中5和7发现两次查询出现了不一样的结果发现会出现不可重复的问题注意最后每个窗口都要执行以下commit提交事务。5 演示Repeatable Read 看是否出现不可重复读问题注意从图上发现当事务2执行6后在事务1上第7步的时候发现查询的结果还是一样的事务2上的步骤8执行之后事务1上的步骤9后还是结果一样的只有当步骤10执行之后查询的数据才会更新账户余额发现事务隔离级别可重复读确实解决了不可重复读的问题。注意最后每个窗口都要执行以下commit提交事务。6 演示Repeatable Read 看是否出现幻读问题从图上看发现事务1中步骤5中没有发现该数据但是在事务2中执行了步骤6和7之后在事务1中执行步骤8的时候发现主键冲突但是在事务1中再次查询id3的数据发现还是没有数据此时就出现了幻读问题。注意最后每个窗口都要执行以下commit提交事务。7 演示Serializable 看是否出现幻读问题第一步第二步当事务1中新增数据并提交该事务之后事务2报主键冲突异常注意该隔离级别发现不会出现幻读了因为当一个事务1中正在执行的时候另外一个事务2是处于等待阻塞状态的只有当事务1提交之后事务2才会继续执行。5. 存储引擎5.1 MySQL体系结构1). 连接层最上层是一些客户端和链接服务包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。2). 服务层第二层架构主要完成大多数的核心服务功能如SQL接口并完成缓存的查询SQL的分析和优化部分内置函数的执行。所有跨存储引擎的功能也在这一层实现如 过程、函数等。在该层服务器会解析查询并创建相应的内部解析树并对其完成相应的优化如确定表的查询的顺序是否利用索引等 最后生成相应的执行操作。如果是select语句服务器还会查询内部的缓存如果缓存空间足够大这样在解决大量读操作的环境中能够很好的提升系统的性能。3). 引擎层存储引擎层 存储引擎真正的负责了MySQL中数据的存储和提取服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能这样我们可以根据自己的需要来选取合适的存储引擎。数据库中的索引是在存储引擎层实现的。4). 存储层数据存储层 主要是将数据(如: redolog、undolog、数据、索引、二进制日志、错误日志、查询日志、慢查询日志等)存储在文件系统之上并完成与存储引擎的交互。和其他数据库相比MySQL有点与众不同它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上插件式的存储引擎架构将查询处理和其他的系统任务以及数据的存储提取分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。1.2 存储引擎介绍大家可能没有听说过存储引擎但是一定听过引擎这个词引擎就是发动机是一个机器的核心组件。比如对于舰载机、直升机、火箭来说他们都有各自的引擎是他们最为核心的组件。而我们在选择引擎的时候需要在合适的场景选择合适的存储引擎就像在直升机上我们不能选择舰载机的引擎一样。而对于存储引擎也是一样他是mysql数据库的核心我们也需要在合适的场景选择合适的存储引擎。接下来就来介绍一下存储引擎。存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的而不是基于库的所以存储引擎也可被称为表类型。我们可以在创建表的时候来指定选择的存储引擎如果没有指定将自动选择默认的存储引擎。mysql5.5版本后默认是innoDB存储引擎。1). 建表时指定存储引擎CREATE TABLE 表名(字段1 字段1类型 [ COMMENT 字段1注释 ] ,......字段n 字段n类型 [COMMENT 字段n注释 ]
) ENGINE INNODB [ COMMENT 表注释 ] ;2). 查询当前数据库支持的存储引擎show engines;示例演示:A. 查询建表语句 --- 默认存储引擎: InnoDBshow create table account;我们可以看到创建表时即使我们没有指定存储疫情数据库也会自动选择默认的存储引擎。B. 查询当前数据库支持的存储引擎show engines ;C. 创建表 my_myisam , 并指定MyISAM存储引擎create table my_myisam(id int,name varchar(10)
) engine MyISAM ;D. 创建表 my_memory , 指定Memory存储引擎create table my_memory(id int,name varchar(10)
) engine Memory ;1.3 存储引擎特点上面我们介绍了什么是存储引擎以及如何在建表时如何指定存储引擎接下来我们就来介绍下来上面重点提到的三种存储引擎 InnoDB、MyISAM、Memory的特点。1.3.1 InnoDB1). 介绍InnoDB是一种兼顾高可靠性和高性能的通用存储引擎在 MySQL 5.5 之后InnoDB是默认的 MySQL 存储引擎。2). 特点DML操作遵循ACID模型支持事务行级锁提高并发访问性能支持外键FOREIGN KEY约束保证数据的完整性和正确性3). 文件xxx.ibdxxx代表的是表名innoDB引擎的每张表都会对应这样一个表空间文件存储该表的表结构frm-早期的 、sdi-新版的、数据和索引。参数innodb_file_per_tableshow variables like innodb_file_per_table;如果该参数开启代表对于InnoDB引擎的表每一张表都对应一个ibd文件。 我们直接打开MySQL的数据存放目录 C:\ProgramData\MySQL\MySQL Server 8.0\Data 这个目录下有很多文件夹不同的文件夹代表不同的数据库我们直接打开itcast文件夹。可以看到里面有很多的ibd文件每一个ibd文件就对应一张表比如我们有一张表 account就有这样的一个account.ibd文件而在这个ibd文件中不仅存放表结构、数据还会存放该表对应的索引信息。 而该文件是基于二进制存储的不能直接基于记事本打开我们可以使用mysql提供的一个指令 ibd2sdi 通过该指令就可以从ibd文件中提取sdi信息而sdi数据字典信息中就包含该表的表结构。4). 逻辑存储结构表空间 : InnoDB存储引擎逻辑结构的最高层ibd文件其实就是表空间文件在表空间中可以包含多个Segment段。段 : 表空间是由各个段组成的 常见的段有数据段、索引段、回滚段等。InnoDB中对于段的管理都是引擎自身完成不需要人为对其控制一个段中包含多个区。区 : 区是表空间的单元结构每个区的大小为1M。 默认情况下 InnoDB存储引擎页大小为16K 即一个区中一共有64个连续的页。页 : 页是组成区的最小单元页也是InnoDB 存储引擎磁盘管理的最小单元每个页的大小默认为 16KB。为了保证页的连续性InnoDB 存储引擎每次从磁盘申请 4-5 个区。行 : InnoDB 存储引擎是面向行的也就是说数据是按行进行存放的在每一行中除了定义表时所指定的字段以外还包含两个隐藏字段(后面会详细介绍)。1.3.2 MyISAM1). 介绍MyISAM是MySQL早期的默认存储引擎。2). 特点不支持事务不支持外键支持表锁不支持行锁访问速度快3). 文件xxx.sdi存储表结构信息xxx.MYD: 存储数据xxx.MYI: 存储索引1.3.3 Memory1). 介绍Memory引擎的表数据时存储在内存中的由于受到硬件问题、或断电问题的影响只能将这些表作为临时表或缓存使用。2). 特点内存存放hash索引默认3).文件xxx.sdi存储表结构信息1.3.4 区别及特点特点InnoDBMyISAMMemory存储限制64TB有有事务安全支持--锁机制行锁表锁表锁Btree索引支持支持支持Hash索引--支持全文索引支持(5.6版本之后)支持-空间使用高低N/A内存使用高低中等批量插入速度低高高支持外键支持--面试题: InnoDB引擎与MyISAM引擎的区别 ? ①. InnoDB引擎, 支持事务, 而MyISAM不支持。 ②. InnoDB引擎, 支持行锁和表锁, 而MyISAM仅支持表锁, 不支持行锁。 ③. InnoDB引擎, 支持外键, 而MyISAM是不支持的。 主要是上述三点区别当然也可以从索引结构、存储限制等方面更加深入的回答具体参考如下官方文档 https://dev.mysql.com/doc/refman/8.0/en/innodb-introduction.html https://dev.mysql.com/doc/refman/8.0/en/myisam-storage-engine.html1.4 存储引擎选择在选择存储引擎时应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统还可以根据实际情况选择多种存储引擎进行组合。InnoDB: 是Mysql的默认存储引擎支持事务、外键。如果应用对事务的完整性有比较高的要求在并发条件下要求数据的一致性数据操作除了插入和查询之外还包含很多的更新、删除操作那么InnoDB存储引擎是比较合适的选择。MyISAM 如果应用是以读操作和插入操作为主只有很少的更新和删除操作并且对事务的完整性、并发性要求不是很高那么选择这个存储引擎是非常合适的。MEMORY将所有数据保存在内存中访问速度快通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制太大的表无法缓存在内存中而且无法保障数据的安全性。