资料:
需要的一些文件:
链接:https://pan.baidu.com/s/1zO6yBWyV2jcUW8GynlX9vg
提取码:sfih
安装教程:菜鸟
入门和一些细节部分(挺重要也)
名词解释
(1)数据库:通常是一个或一组文件,保存了一些符合特定规格的数据,数据库对应的英语单词(DB),数据库软件称为数据库管理软件(DBMS);
(2)DBMS—执行—> SQL ----操作-----> DB;
(3)表:数据库中最基本的单元;数据库中是以表格的形式表示数据的(都有行row和列column)行:被称为数据/记录;列:被称为字段;
(4)每个字段都有:字段名、数据类型(字符串、数字、日期等)、约束等属性;
(5)SQL语句有很多,最好分门别类更容易记录:
DQL:数据查询语言 (凡是带有select关键字的都是查询语句);
DML:数据操作语言(凡是对表中的数据进行增删改的都是DML);insert、delete、update
DDL:数据定义语言(create、drop、alter),主要是操作表的结构,而不是表的内容;
TCL:事务控制语言,事物提交:commit;事务回滚:rollback
DCL:数据据控制语言,授权、撤销等;
软件学习
(1)软件安装
安装选择经典版,第二步需要进行MySQL数据库配置(开发机器,一路下一步);
使用bin目录下的mysql.exe命令来连接mysql数据库服务器;
本地登录:mysql -uroot -pk0u%y0qi)zmC(显示编写密码的形式登录)
退出mysql:exit
查看数据库:
show databases;
选择使用数据库:
use 数据库名字;
创建数据库:
create database 数据库名字;
查看某个数据库下有哪些表:
show table
导入已经准备好的数据:source 路径(路径中不要有中文!!)
desc dept; #看数据表dept的结构;
查看mysql的版本号:
select verson();
终止输入:\c
需要注意的就是:
端口号:端口号(port)是任何一个软件/应用都会有的,是应用的唯一代表。端口号通常和IP地址在一块,IP地址是用来定位计算机的,端口号port在同一台计算机上,端口号不能重复,具有唯一性。
mysql数据库启动的时候,这个服务占有的默认端口号是3306(这个记住就好了)
设置mysql数据库的字符编码方式为 UTF-8;
服务名字(默认是MySQL)
mysql超级管理员用户名不能改,一定是root;但是需要设置my数据库超级管理员的密码;
设置密码的同时,可以激活root账户远程访问。激活表示root账户在外地可以登录,而不激活表示root只能在本机登录。
MySQL的服务,默认是运行状态,只有启动了mysql才能用。默认是自动,即下次重启系统的时候会默认启动。
不见分号不执行,可以接着上一行输入;
正式开始
简单查询
1.查询一个字段:
select 字段名 from 表名;
其中要注意:select和from是关键字;字段名和表名都是标识符;
强调:对于SQL语句来说,是通用的:所有SQL语句以“;”结尾,另外SQL不区分大小写;
2.查询两个或多个字段
使用逗号隔开;
3.查询所有字段
(1)可以把所有字段都写上;
(2)select * 就可以了(这种是一个通配符),但是这个效率和可读性比较低;(实际开发中不建议用);在dos命令窗口中想快速看一下所有字段,可以采用这种方式;
4.给查询的列起别名
用as关键字来起别名;
select deptno,dname as deptname from dept;
#把 dname 改名为 deptname;
#还有一点,其中的as可以省略,可以写成下面的样子
select deptno, dname deptname from dept;
#如果别名里边有空格或者别名是一个中文,那么需要 双引号 或者 单引号 把别名圈起来(单引号是标准,在orcale中不能用双引号)
select deptno, dname 'dept name' from dept;
#注意,其中的逗号是分隔作用,是为了查询多个字段用的;
只是将显示的查询结果列名显示为deptname,原列名还是叫:dname;
记住:selcet是DQL,是永远不会进行修改操作的;
5.列参数与数学运算
表 sqlpractice 中有一个 salgrade,表示的是薪水等级,我想看一下年薪,那么可以对这一列乘一个 12;
SELECT GRADE,LOSAL * 12,HISAL * 12 FROM SALGRADE;
条件查询
1.条件查询
不是将表中所有数据都查出来,是查询出来符合条件的。
语法格式:select ... from ... where 条件;
例子:查询薪资等于800的员工的姓名和编号;
SELECT EMPNO,ENAME FROM EMP WHERE SAL = 800.0;
#不等于号 != 或者 <>
#另外一些条件符号
between ... and ...(是说在。。。和。。。之间,如果是数字范围,那么一定是左小右大),这个包含端点的两个值
is null(判断是不是null)
#判断补助(COMM)是null,在数据库中null不能使用等号进行衡量,他不是一个值
select EMPNO,NNAME FROM EMP WHERE COMM IS NULL;
and or in(包含,相当于多个or,用法如下(注意小括号):) ,其中 and 的优先级会更高一点;
SELECT EMPNO,ENAME,JOB FROM EMP WHERE JOB IN ('MANAGER' ,'SALESMAN');
not
like 支持 % 和 _
% 匹配任意多个字符
_ 下划线匹配任意一个字符
2. 排序
# 基本语法
SELECT ... FROM ... ORDER BY ...; #默认是升序(ASC)
SELECT ... FROM ... ORDER BY ... DESC; #指定降序
3.多个字段排序
按照多个字段,先按照 sal(薪资)的升序排列,sal 相同的情况下再按照名字的升序排;
SELECT ENAME,JOB FROM EMP ORDER BY SAL ASC,ENAME ASC;
还可以和 where 结合,要知道这个执行顺序,排序总是在最后执行的;
SELECT ENAME,JOB,SAL FROM EMP WHERE SAL BETWEEN 1250 AND 3000 ORDER BY SAL DESC,ENAME DESC;
单行处理函数(数据处理函数)
因为是一行一行处理的,所以又叫单行处理函数;特点是一个输入对应一个输出,和单行处理函数相对的是多行处理函数(特点是多个输入对应一个输出);
lower #转换小写 SELECT LOWER(ENAME) FROM EMP;
upper #转换大写
length #取长度
substr #取子串(substr(被截取的字符串,起始下标,截取的长度)) 要注意:第一个字母下表是 1
trim #去空格 SELECT * FROM EMP WHERE ENAME = TRIM(' KING ');
str_to_date #字符串转成日期
date_format #格式化日期
format #设置千分位
round() #四舍五入
rand #生成随机数 和编程语言中一样应该是
ifnull #可以将null转换成一个具体值 只要有 null 参与的数学运算,结果都是null,为了解决这个才要使用ifnull
concat #进行字符串的拼接
CASE ... WHEN ... THEN ... WHEN ... THEN ... #相当于switch语句
#下面这个是以名字首字母大写的方式来选择出数据;
SELECT CONCAT(UPPER(SUBSTR(ENAME,1,1)),LOWER(SUBSTR(ENAME,2,LENGTH(ENAME)-1))) AS RESULT FROM EMP;
#执行结果如下所示:
+--------+
| RESULT |
+--------+
| Smith |
| Allen |
| Ward |
| Jones |
| Martin |
| Blake |
| Clark |
| Scott |
| King |
| Turner |
| Adams |
| James |
| Ford |
| Miller |
+--------+
select 后买你可以跟某个表的字段名(可以等同看做变量名),也可以跟字面量/字面值(数据);
SELECT ROUND(1236.567,1) AS RESULT FROM EMP; #表示保留 1 位小数;
SELECT ROUND(RAND()*100,2) FROM EMP;
SELECT ENAME, (SAL + IFNULL(COMM,0))*12 AS YAERSAL FROM EMP;
分组函数
特点:输入多行,最终输出一行;
count #计数 SELECT COUNT(ENAME) FROM EMP;
sum #求和 SELECT SUM(SAL) FROM EMP;
avg #平均值 SELECT AVG(SAL) FROM EMP;
max #最大值 SELECT MAX(SAL) FROM EMP;
min #最小值 SELECT MIN(SAL) FROM EMP;
注意:分组函数在使用的时候必须先进行分组,然后才能用;如果没有对数据进行分组,那么默认整张表是一组;
分组函数会自动忽略 null ;
count(*) 和 count(具体字段) 区别就是,count(具体字段)是统计该字段下所有不为null的元素的总数;count(*)统计表中的总行数,因为每一行记录不可能都为null,一行数据中总有一列不为null;
分组函数不能直接使用在where句子中;(说完分组查询(group by)就明白了)
所有分组函数可以组合起来一起用
分组查询(非常重要)
在实际应用中,可能会有这样的需求,需要先进行分组,然后对每一组的数据进行操作;
SELECT ... FROM ... GROUP BY ...
将以前的关键字全部组合在一起,来看一下他们的执行顺序:
select
...
from
...
where
...
group by
...
order by
...
以上关键字的顺序不能颠倒,需要记忆。
这也就是为什么,where后直接跟分组函数会报错的原因,因为分组函数使用之前要先分组,可以用在where之前,是因为select是在group by 之后才执行的,没写group by是默认的整张表是一组;
重点:在一条select语句中,如果有group语句的话,select后面只能跟:参加分组的字段,以及分组函数。其他一律不能跟;
技巧:GROUP BY
可以跟同时跟多个分组,每个分组之间用 “逗号” 隔开就好了;
使用 having 可以对分完组的数据进行进一步过滤,但是 having 不能单独使用,必须和group by联合使用;
SELECT DEPTNO,MAX(SAL) FROM EMP WHERE SAL > 3000 GROUP BY DEPTNO; # where和having 优先选择where,线用where过滤掉在分组,这样效率更高一些;
where完成不了的:
#找出每个部门的平均薪资,要求显示平均薪资高于2500的;
#这个因为where后面不能跟分组函数,所以这里不能用
SELECT DEPTNO,AVG(SAL) FROM EMP GROUP BY DEPTNO HAVING AVG(SAL) > 2500;
单表查询总结
SELECT
...
FROM
...
WHERE
...
GROUP BY
...
HAVING
...
ORDER BY
...
执行顺序就是:1.from 2.where 3. group by 4.having 5.order by 6. select
;
distinct关键字
去除重复记录,原表数据不会被修改,只是查询结果去重。
DISTINCT 只能出现在所有的字段最前面;
#distinct 出现在 job,deptno 两个字段之前,表示两个字段联合起来去重
SELECT DISTINCT JOB,DEPTNO FROM EMP;
+-----------+--------+
| JOB | DEPTNO |
+-----------+--------+
| CLERK | 20 |
| SALESMAN | 30 |
| MANAGER | 20 |
| MANAGER | 30 |
| MANAGER | 10 |
| ANALYST | 20 |
| PRESIDENT | 10 |
| CLERK | 30 |
| CLERK | 10 |
+-----------+--------+
#且可以:
SELECT COUNT(DISTINCT(JOB)) FORM EMP;
连接查询
两张或者多张表联合查询的方法;比如:从emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中去部门名字。
1.连接查询分类
根据语法的年代分:
#SQL92
1992年的时候出现的语法
#SQL99
1999年的时候出现的语法(重点学习)
根据表连接的方式分:
#内连接
等值连接、非等值连接、自连接
#外连接
左外连接、右外连接
#全连接(不讲)
2.当两张表进行连接查询时,没有任何条件的限制会发生生么现象?
#案例:查询每个员工所在部门名称
SELECT * FEOM EMP;
SELECT ENAME,DEPTNO FROM EMP;
SELECT ENAME,DNAME FROM EMP,DEPT;
#笛卡尔积现象
当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数,是两张表条数的成绩,这种现象被称为:笛卡尔积现象。
3.避免笛卡尔积现象
连接时加条件,满足这个条件的记录就被筛选出来;
SELECT ENAME,DNAME FROME EMP,DEPT WHERE EMP.DEPTNO = DEPT.DEPTNO;
#但是虽然筛选出来的结果少了,但是匹配次数并没有减少;(即匹配过程中,匹配次数并没有减少);
#为了提高效率,字段可以加一个所属范围的限制;
SELECT EMP.ENAME AS EMPname,DEPT.DNAME AS DEPTname FROM EMP,DEPT WHERE EMP.DEPTNO = DEPT.DEPTNO;
#表起别名非常重要;(也是提高效率的方式) 下面是92语法
SELECT E.ENAME,D.DNAME
FROM EMP E,DEPT D
WHERE E.DEPTNO = D.DEPTNO;
4.内连接之等值连接
内连接:没有表的主次关系,两张表的地位相同(查询优先级);
案例:查询每个员工所在部门名称,显示员工名和部门名
SELECT E.ENAME,D.DNAME
FROM EMP E,DEPT D
WHERE E.DEPTNO = D.DEPTNO AND ...;
#上面是92的语法 AND之前是将两个表连接起来的条件,AND后边是将两个表连接以后进行过滤的条件;
#99的语法
SELECT
E.ENAME,D.DNAME
FROM
EMP E
(inner)JOIN #这里的inner表示是内连接,平时是可以省略的;
DEPT D
ON
E.DEPTNO = D.DEPTNO; #内连接的特点:完全能够匹配上这个条件的数据查询出来
WHERE
...
#99的语法结构更加清晰,它把表连接的条件和过滤所用到的条件分开来考虑,ON 后边就是连接条件;
5.内连接之非等值连接
#案例:找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级;
SELECT E.ENAME,E.SAL,S.GRADE FROM EMP E JOIN SALGRADE S ON E.SAL > S.LOSAL AND E.SAL < S.HISAL;
6.内连接之自连接
案例:查询员工的上级领导,要求显示员工名和对应的领导名;
SELECT A.ENAME,B.ENAME FROM EMP A JOIN EMP B ON A.MGR = B.EMPNO;
#这种把一张看成两张表的方式,就是自连接;
7.外连接
#右外连接的一个例子
SELECT E.ENAME,D.DNAME FROM EMP E RIGHT (OUTER) JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
这里的 OUTER 可以不写,跟 INNER 是一样的;
+--------+------------+
| ENAME | DNAME |
+--------+------------+
| MILLER | ACCOUNTING |
| KING | ACCOUNTING |
| CLARK | ACCOUNTING |
| FORD | RESEARCH |
| ADAMS | RESEARCH |
| SCOTT | RESEARCH |
| JONES | RESEARCH |
| SMITH | RESEARCH |
| JAMES | SALES |
| TURNER | SALES |
| BLAKE | SALES |
| MARTIN | SALES |
| WARD | SALES |
| ALLEN | SALES |
| NULL | OPERATIONS | #这里还匹配出一个NULL,这是因为那个 right 意思就是把右边那个表的没有匹配的内容也显示出来;
+--------+------------+
RIGHT代表:表示将 JOIN 右边的表看作是主表,主要是为了查出这张表中的内容,捎带查出左边的表中关联的内容;
任何一个右连接都有左连接的方法;
任何一个左连接都有右连接的方法;
外连接的查询结果条数,一定是大于等于内连接的查询结果条数;
全连接:两张表都是主表;
8.三、四张表怎么连接
一条SQL中,内连和外连可以混合,都可以出现;
#案例1:找出每个员工的部门名和工资等级,要求显示员工名、部门名、薪资、薪资等级;
SELECT
E.ENAME,E.SAL,D.DNAME,S.GRADE
FROM
EMP E
JOIN
DEPT D
ON
E.DEPTNO = D.DEPTNO
JOIN
SALGRADE S
ON
E.SAL BETWEEN S.LOSAL AND S.HISAL;
#案例2:找出每个员工的部门名和工资等级,领导名,要求显示员工名、领导名、部门名、薪资、薪资等级;
SELECT
E.ENAME AS '员工名',EA.ENAME AS '领导名',E.SAL,D.DNAME,S.GRADE
FROM
EMP E
LEFT JOIN
EMP EA
ON
E.MGR = EA.EMPNO
JOIN
DEPT D
ON
E.DEPTNO = D.DEPTNO
JOIN
SALGRADE S
ON
E.SAL BETWEEN S.LOSAL AND S.HISAL;
子查询
1.什么是子查询
SELECT 语句中嵌套 SELECT 语句,被嵌套的 SELECT 语句被称为子查询;
2.子查询可以出现的位置
SELECT
..(SELECT).
FROM
..(SELECT).
WHERE
..(SELECT).
3.WHERE
中的子查询
#找出比最低工资高的员工姓名和工资
SELECT ENAME,SAL FROM EMP WHERE SAL > (SELECT MIN(SAL) FROM EMP);
4.FROM
字句中的子查询
注意:FROM
后面的子查询,可以将子查询的结果当作一张临时表。(技巧)
#案例:找出每个岗位的平均薪资等级
SELECT E.JOB,E.AV,S.GRADE FROM (SELECT JOB,AVG(SAL) AS AV FROM EMP GROUP BY JOB) E JOIN SALGRADE S ON E.AV BETWEEN S.LOSAL AND S.HISAL;
+-----------+-------------+-------+
| JOB | AV | GRADE |
+-----------+-------------+-------+
| CLERK | 1037.500000 | 1 |
| SALESMAN | 1400.000000 | 2 |
| MANAGER | 2758.333333 | 4 |
| ANALYST | 3000.000000 | 4 |
| PRESIDENT | 5000.000000 | 5 |
+-----------+-------------+-------+
5.SELECT
后面出现的子查询
union
的用法
可以用来合并两次查询的结果:
SELECT ENAME,JOB FROM EMP WHERE JOB IN('MANAGER','SALESMAN');
#上面的效率不如下面使用UNION
SELECT ENAME,JOB FROM EMP WHERE JOB = 'MANAGER' UNION SELECT ENAME,JOB FROM EMP WHERE JOB = 'SALESMAN';
#从下面的输出结果可以看到,两句话效果相同;
mysql> SELECT ENAME,JOB FROM EMP WHERE JOB IN('MANAGER','SALESMAN');
+--------+----------+
| ENAME | JOB |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| TURNER | SALESMAN |
+--------+----------+
7 rows in set (0.00 sec)
mysql> SELECT ENAME,JOB FROM EMP WHERE JOB = 'MANAGER' UNION SELECT ENAME,JOB FROM EMP WHERE JOB = 'SALESMAN';
+--------+----------+
| ENAME | JOB |
+--------+----------+
| JONES | MANAGER |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| MARTIN | SALESMAN |
| TURNER | SALESMAN |
+--------+----------+
7 rows in set (0.00 sec)
union的效率要高一些,对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻;
但union可以在减少匹配次数的情况下,完成两个结果集的拼接;
# UNION合并时,要求列数和列的数据类型都要一致,否则会报错;
LIMIT
概述
是将查询结果集的一部分取出来,通常是使用在分页查询中;
1.使用
LIMIT startIndex,length;
如果只跟了一个数字,那么前几;
起始下表从零开始;
#按薪资降序,取出排名在前5名的员工
SELECT ENAME,SAL FROM EMP ORDER BY SAL DESC LIMIT 5;
注意:mysql
中,limit
在order by
之后执行!!!
2.通用分页
每页显示3条记录
第一页:LIMIT
0, 3; #可以总结规律
第二页:LIMIT
3, 3;
第三页:LIMIT
6, 3;
DQL
大总结
1.from
2.where
3.group by
4.having
5.select
6.order by
7.limit
表的创建(建表)
1.建表的语法格式
建表属于DDL
语句,DDL
包括:CREATE DROP ALTER
CREATE TABLE 表名(字段名1 数据类型,字段名2 数据类型,字段名3 数据类型);
表名:建议以 t_ 或者 tbl_ 开始,
2.关于mysql
中的数据类型
很多数据类型,我们只需要掌握一些常见的数据类型即可:
varchar: 可变长度的字符串
char: 定长字符串,不管实际数据长度是多少,分配固定长度空间存储数据,使用不恰当可能会导致空间的浪费;
int(最长11位): 等同于java中的int
bigint: 等同于java中的long
float: 单精度浮点型
double: 双精度浮点型
date: 短日期类型
datatime: 长日期类型
clob: 字符大对象(最多可以存储4G的字符串),比如:存储一篇简介; 超过255个字符的都要用它来存储;
blob: 二进制大对象,专门用来存储图片、声音、视频等流媒体数据; 需要使用IO流才行;
#创建一个电影表
t_movie
#字段:
编号 名字 故事情节 上映时间 时长 海报 类型
no(bigint) name(varchar) storydetail(clob) playtime(date) time(float) image(blob) type(char)
___________________________________________________________________________________________________
10000 哪吒 ......... 2019-10-11 2.5 ..... '1'
10000 林正英之娘娘 ......... 2019-10-11 2.5 ..... '2'
3.创建一个学生表
#创建一个学生表
学号、姓名、年龄、邮箱地址
create table t_student(sno char(11),
sname char(15),
sex char(1),
age int(3));
#删除学生表
DROP TABLE IF EXISTS T_STUDENT; 这里用了 if exists 保证代码的健壮性;
#创建一个学生表,并指定性别默认是 'm'
create table t_student(sno char(11),
sname char(15),
sex char(1) default 'm',
age int(3));
4.insert
语句
语法格式:
INSERT INTO 表名(字段1,字段2,字段3,...) VALUES(值1,值2,值3,...); #这是一次一个记录
insert into 表名(字段1,字段2,字段3,...) values
(值1,值2,值3,...),
(值1,值2,值3,...),
(值1,值2,值3,...); #这种就是一次多条记录,每条记录用逗号隔开
插入时,如果没有给所有字段名一个值得话,那么没有给值的字段默认是NULL
;
这个字段名也可以不写,但是后面值的顺序要和创建表的时候的顺序一致;
STR_TO_DATE:将字符串varchar类型转成date类型
DATE_FORMAT:将date类型转换成具有一定格式的varchar类型
**数据库中有条命名规范:**所有的标识符都是全部小写,单词和单词之间使用下划线进行衔接;
如果设置的是date
类型,要想用字符串给出结果,那么需要使用str_to_date('字符串日期','日期格式')
来转换;
%Y:年
%m:月
%d:日
%h:时
%i:分
%s:秒
#使用例子:
drop table if exists t_user;
create table t_user(id int,name varchar(32),birth date);
insert into t_user(id,name,birth) values(1,'liyanlin',str_to_date('06-03-2000','%d-%m-%Y'));
如果字符串是:‘2000-03-06’,那么就不用使用str_to_date
再来转换了,会自动转换;
SELECT ID,NAME,DATE_FORMAT(BIRTH,'%Y-%m-%d') FROM T_USER;
6.date
和datetime
两个类型之间的区别
date
是短日期:只包括年月日 %Y-%m-%d
datetime
是长日期:包括年月日时分秒; %Y-%m-%d %h:%i:%s
在mysql
中获取当前时间:now()
; 并且获取的时间带有时分秒信息(即长类型);
7.修改 update (DML)
语法格式:
update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3... where 条件;
注意:没有条件限制会导致所有数据全部更新;
8.delete (DML)
删除数据
语法格式:
delete from 表名 where 条件;
#注意:如果没有条件,那么整张表的数据会被全部删除;
快速创建表
create table emp2 as select * from emp;
#将一个查询结果作为一张表新建
#这个可以完成表的快速复制
#表创建出来,同时表中的数据也存在了
将查询结果插入一张表
create table mytable as select ename,edeptno from emp where job = 'manager';
删除大表
drop table if exists dept_bak;
create table dept_bak as select * from dept;
delete from dept_bak; (属于DML)
#这种删除方式比较慢
#delete 删除,它删除并不会释放掉那个内存;优点就是可以在误删的情况下可以回滚回去;
truncate语句删除数据:(属于DDL)
#删除效率比较高,表被一次截断,物理删除;缺点就是无法回滚;
对表结构的增删改
概念:添加、删除、修改一个字段(整体)
对表结构结构的修改需要使用:alter;(属于DDL
语句)
约束(非常重要)
约束对用的英文单词:constraint
在创建表得时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性!!!
1.常见的约束
非空约束:not null
唯一性约束:unique
主键约束:primary key(简称PK)
外键约束:foreign key(简称FK)
检查约束:check(mysql不支持,oracle支持)
**重点学习四个约束:**not null、unique、primary key、foreign key;
3.非空约束(not null)
非空约束(not null)约束的字段不能为null;
#小插曲:sql脚本文件的使用
xxx.sql这种文件被称为sql脚本文件
可以用 source FilePath 这种方式来执行
这种文件中编写了大量的sql语句,我们执行sql脚本文件得时候,该文件中所有的sql语句会被全部执行!!
批量地执行sql语句,可以使用sql脚本文件。
#这里的not null其实就是没有给这个字段设置默认值
drop table if exists t_vip;
create table t_vip (id int,name varchar(32) not null); #注意这里注明 not null 的方式;
#上面创建了一个t_vip表
insert into t_vip(id) values(3);
#如果这样执行插入命令,那么会出现以下错误:
ERROR 1364 (HY000): Field 'name' doesn't have a default value
4.唯一性约束
#既满足非空约束,又满足唯一性约束,代码如下所示:
create table t_vip (id int,name varchar(32) not null unique);
#如果输入两个相同的名称会有如下报错:
ERROR 1062 (23000): Duplicate entry 'zhangsan' for key 't_vip.name'
5.两个字段联合唯一
drop table if exists t_vip;
create table t_vip (id int,name varchar(32),email varchar(255),unique(name,email)); #这里的方式就表示两个字段联合唯一
#!!!!!非常要注意的就是,这里的 unique 是和字段之间用逗号隔开的
这种约束没有添加在列的后面,这种约束被称为表级约束;
**什么时候需要使用表级约束:**需要给多个字段联合起来添加某一个约束的时候,需要使用表级约束;
6.not
和unique
联合
not null 只有列级约束 没有 表级约束
如果一个字段被这样约束,那么这个字段就自动成为主键了;
drop table if exists t_vip;
create table t_vip (id int,name varchar(32) not null unique);
desc t_vip;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(32) | NO | PRI | NULL | |
+-------+-------------+------+-----+---------+-------+
#这个特性,在 mysql 中有,但是在 oracle 中没有这个;
7.主键约束
主键约束的相关术语:主键约束(一种约束)、主键字段(改字段上添加了字段约束)、主键值;
任何一张表都应该有主键,如果没有主键,那么这张表无效;
特征:not null + unique
(主键值不能是NULL,同时也不能重复)
drop table if exists t_vip;
create table t_vip (id int primary key,name varchar(32)); #主键约束 列级约束 的使用方法
create table t_vip (id int,name varchar(32),primary key(id));
表级约束主要是:给多个字段联合起来添加约束的;
单一主键:单一字段做主键
复合主键:联合字段做主键
在实际开发中,不建议使用复合主键,建议使用单一主键;(因为主键值存在的意义就是这行的身份证号,达到意义就可以)
一张表主键约束只能添加一个;
主键值建议使用:int bigint char
等类型,不建议使用:varchar
;因为主键值一般都是数字,都是定长的;
自然主键:是一个自然数,和业务没有关系;
业务主键:主键值和业务紧密关联,例如银行卡号作为主键值;
自然主键使用比较多,因为业务主键变动的时候会影响;
auto_incerment;
#这个可以让主键自增;
8.外键约束(非常重要(FK))
#业务背景:请设计数据库表,来描述“班级和学生”的信息;
因为一个班会有好多学生,所以如果用班级和学生在一张表,会有很大的空间浪费来存储班级名字;
#解决方法:班级一张表,学生一张表
当cno字段没有任何约束的时候,可能会导致数据失效。可能出现一个102,但是102班级不存在,所以为了保证cno字段中的值都是100和101,就需要给cno字段添加外键约束;这个字段就成了一个外键字段;
用了外键约束,那么被引用的那张表就变成了父表,引用的那张表就变成了子表;
删除表的顺序:先删除子表
创建表的顺序:先创建父表
删除数据的顺序:先删除子
插入数据的顺序:先插入父
#外键的使用方法;
drop table if exists t_stu;
drop table if exists t_class;
create table t_class(
classno int primary key,
classname varchar(255)
);
create table t_stu(
stuno int primary key auto_increment,
stuname varchar(255),
cno int,
foreign key(cno) references t_class(classno)
);
insert into t_class(classno,classname) values
(101,'北京市大兴区亦庄镇第二中学高三1班'),
(102,'北京市大兴区亦庄镇第二中学高三2班');
insert into t_stu(stuname,cno) values
('jack',101),
('lucy',101),
('lilei',101),
('hanmeimei',101),
('lisi',101),
('wangwu',102),
('zhaoliu',102);
select * from t_stu;
select * from t_class;
#外键不一定是主键,但至少要具有 “”“唯一性”“”
9.存储引擎
存储引擎是mysql中特有的术语,其他数据库中没有(oracle中有,但是不叫这个名字);
实际上是表存储/组织数据的方式
1.存储引擎的使用
show create table t_student;
#展示表 t_student 建立的时候怎么创建的(使用的什么存储引擎等)
可以在建表的时候给表指定存储引擎。 方式如下:在语句的最后 “)” 的后边用:
engine #指定存储引擎
charset #指定字符编码方式
结论:mysql默认的存储引擎是 innodb ;字符编码的默认方式是:utf8;
2.怎么查看mysql支持哪些存储引擎
select version();
+-----------+
| version() |
+-----------+
| 8.0.33 |
+-----------+
show engines;
...
3.关于mysql
常用的存储引擎介绍
MyISAM
:用三个文件去表示一张表:格式文件(.frm)、数据文件(.myd)、索引文件(.myi)(一本书的目录,提高查询效率)
对一张表来说,只要是主键或者是加有unique
约束的字段上会自动创建索引;
InnoDB
:是一个默认的存储引擎,同时也是一个重量级的存储引擎
最主要的特点:非常安全;它支持事务,支持数据库崩溃后自动恢复机制;
可以被转换为压缩、只读表来节省空间
不支持事务机制,安全性不是很高
(1)每个innodb表在数据库目录中以.frm格式文件表示
(2)innodb表空间tablespace被用以存储表的内容(索引也存在这里边)
MEMORY
:使用这个引擎的表,其数据存储在内存中,且行的长度固定;这两个特点使得MEMORY存储引擎非常快;
每个表以 .frm 格式文件来表示;表数据被存储在内存中(目的就是为了快,查询快);表级锁机制;
不安全,关机之后数据消失;
事务(核心、必须掌握、必须理解)
1.什么是事务
一个事务其实就是一个完整的业务逻辑;是一个最小的工作单元,不可再分;
什么是一个完整的业务逻辑:假设转账,从A账户向B账户转账10000,将账户A的钱减去10000,账户B的钱加上10000,这就是一个完整的业务逻辑;
以上的操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分;
2.只有DML语句和事务有关,其他语句和事务无关
insert、delete、update
只有以上三个语句和事务有关系,其他都没有关系。因为,只有以上的三个语句是数据库表中数据进行增、删、改的;
3.假设所有业务,只要一条DML语句就能完成,还有必要存在事务机制?
正是因为做某件事的时候,需要多条DML语句联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事都能一条DML语句搞定,那么事务则没有存在的必要;
事务:就是多条DML语句同时成功,或者同时失败;
4.innodb是怎么做到多条DML语句同时成功和同时失败的呢?
innodb存储引擎:提供一组用来记录事务性活动的日志文件;
事务执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中;
事务执行过程中,我们可以提交事务,也可以回滚事务;
提交事务?
清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中;
提交事务标志着,事务的结束。并且是一种全部成功的结束。
回滚事务?
将之前所有的DML操作全部撤销,并且清空“事务性活动的日志文件”;
回滚事务标志着,事务的结束。并且是一种全部失败的结束。
5.怎么提交事务,怎么回滚事务?
提交事务:commit;语句
回滚事务:rollback;语句
事务对应的单词:transaction;
#mysql默认情况下是支持自动提交事务的;
#回滚永远只能回滚到上一次提交点;
如果想关闭自动提交机制,可以先执行:
start transaction; #这样就可以关闭自动提交机制
这种自动提交的机制实际上不符合我们的开发习惯,因为一个业务通常需要多条DML语句共同执行才能完成的,为了保证数据的安全,必须要求同时成功之后才提交,所以不能执行一条就提交一条;
6.事务的特性
#原子性:说明事务是最小的逻辑单元
#一致性:所有事务要求,在同一事务中,所有操作必须同时成功, 或者同时失败
#隔离性:事务A和事务B具有一定的隔离,像是高并发这种都需要;
#持久性:事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据,保存到硬盘上。
7.重点研究一下事务的隔离性
A教室和B教室之间有一堵墙,这道墙可以很厚,也可以很薄。这就是事务之间的隔离级别,这道墙越厚,表示隔离级别就越高。
#事务和事务之间的隔离级别有四种:
(1)读未提交 readuncommitted(最低的隔离级别): 事务A可以读取到事务B未提交的数据。这种隔离级别存在的问题就是脏读现象·· (dirty read),我们称读到了脏数据。 这种个隔离级别一般都是理论上,大多数的隔离级别都是二档起步;
(2)读已提交 readcommitted: 事务A只能读取到事务B提交之后的数据,这种隔离级别解决了脏读的现象;但是不可以重复读取数据,即事务开启之后,第一次读到的数据是3条,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读;意思应该是每次都是读取的事务B提交之后的数据;
#oracle中默认的隔离级别是这个
(3)可重复读 repeatable read:事务A开启以后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变; 解决了不可重复读的问题; 但是可能会出现幻读的现象;
#mysql中默认的隔离级别是这个
(4)序列化/串行化(最高的隔离级别)serializable:这种是最高的隔离级别,效率最低,解决了所有问题。这种隔离级别表示事务排队,不能并发;
有点类似于:synchronized,线程同步(事务同步);每一次读取到的数据都是最真实的,并且效率是最低的;
8.隔离级别的一些演示使用
#查看当前的事务隔离级别
select @@transaction.isolation; #这个是mysql8.0更改后的语法格式
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
#设置全局隔离级别
set global transaction isolation level read uncommitted;
索引(重要)
1. 什么是索引
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。
一张表的一个字段可以添加一个索引,也可以多个字段联合添加一个索引。
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。
mysql在查询方面主要就是两种方式:(1)全表扫描(2)根据索引检索
#在mysql中数据库当中,索引也是需要排序的,并且这个索引的排序和TreeSet数据结构相同。TreeSe(TreeMap)底层是一个自平衡二叉树!在mysql当中索引是一个B-Tree数据结构
#遵循左小右大原则存放,采用中序遍历方式遍历取数据。
2.索引的实现原理
(1)任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引,因为id是PK(primary key);一个字段上如果有unique约束,那么也会自动创建索引;
(2)在任何数据库中,在任何一张表中任何一个记录在硬盘存储上都有一个硬盘的物理存储编号。
(3)在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名叫做tablespace当中。在MEMORY存储引擎中搜索被存储在内存中。不管索引存储在哪里,索引在mysql中都是一个树的形式存在(自平衡二叉树:B-Tree)。
3.在mysql中,主键上,以及unique字段上都会自动添加索引!!
什么条件下,我们会考虑给字段加索引:
#条件1:数据量庞大(到底多么庞大算庞大,这个需要测试,因为每一个硬件环境不同)
#条件2:该字段经常出现在where后面,以条件的形式存在,也就是说这个字段总是被扫描;
#条件3:该字段很少的DML操作。(因为DML后,索引需要重新排序)
#
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能;
建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的;
4.索引的创建和删除
#索引的创建
create index emp_ename_index on emp(ename);
给emp表的ename字段添加索引,起名:emp_ename_index;
#索引的删除
drop index emp_ename_index on emp;
将emp表上的emp_ename_index索引对象删除;
5.怎么查看一个SQL语句是否使用了索引进行检索?
explain select * from emp where ename = 'king';
+----+--------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra
+----+--------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 10.00 | Using where |
+----+--------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
从上面看,扫描了14条记录;
6.索引有失效的时候,什么时候索引失效呢?
select * from emp where ename like '%T';
#向上面这种模糊查询,即使添加了索引也不会走索引,因为模糊匹配中以 ‘%’开头了; 所以应该在模糊查询的时候尽量避免模糊查询的时候以‘%’开始;这是一种优化的手段/策略;
#失效的第二种情况:使用or的时候可能会失效,如果使用or那么要求两边的条件字段都要有索引,那么才会走索引。如果其中一边有而另外一个字段没有索引,那么另一个字段上的索引也会失效。所以这就是为什么不建议使用or的原因。
复合索引:两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引;比如下边:
create index emp_ename_jon_index on emp(ename,job);
#失效的第三种情况:使用复合索引的时候,没有使用左侧的列查找,索引失效;
#失效的第四种情况: 在where当中索引列参加了运算,索引失效;
#失效的第五种情况: 在where中,索引列使用了函数
7.索引是各种数据库进行优化的重要手段
索引在数据库中分了很多类:
单一索引:一个字段上添加索引
复合索引:两个或者多个字段上添加索引
主键索引:主键上添加索引
唯一性索引:具有unique约束的字段上添加索引
#注意:唯一性比较弱的字段上添加索引用处不大
视图
1.什么是视图?
view:站在不同的角度去看待同一份数据
2.怎么创建视图对象?怎么删除视图对象?
create view emp_view as select * from emp;
drop view emp_view;
#注意:只有DQL语句才能以view的形式创建
3.用视图做什么?
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!!
4.视图对象在实际开发中到底有什么用
假设有一条非常复杂的sql语句,而这条sql语句需要在不同的位置上反复使用,每一次使用这个sql语句的时候都需要重新编写,很长很麻烦; -------------> 可以把这条复杂的sql语句以视图对象的形式新建,在需要编写这条sql语句的位置直接使用视图对象,可以大大简化开发。并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的sql语句; 视图对象也是以一个文件的形式存在的,关机重启并不会消失,可以像使用table一样;
create view viewName as DQL语句;
增删改查:CRUD --------> 'create'/'retrive'/'update'/'delete'
是公司中程序员之间沟通的术语;
DBA常用命令
create user liyanlin identified by '123456'; #创建一个新用户 liyanlin
重点掌握数据的导入和导出(数据的备份)
#导出:
#导入需要注意:
(1)新建数据库:create
(2)使用数据库:use
(3)然后初始化数据库:source
数据库三范式
1.什么是数据库设计范式
数据库表的设计依据,教你怎么进行数据库表的设计;
2.数据库设计范式共有?
#三种
第一范式(1)要求任何一张表都必须有主键,每一个字段原子性不可再分
第二范式(2)建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖
第三范式(3)建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖
第一范式
最核心最重要的范式,所有表的设计都要满足;
(1)一定要有主键 (2)每个字段原子性不可再分
第二范式
主键可能是联合的两个字段,另外不是主键的字段,可能只依赖于联合构成主键的两个字段的一个;
可能造成数据冗余问题;
面对多对多的关系,可以拆分成三个表来表示,其中一个关系表(需要两外键);
第三范式
要求不要产生传递依赖;
比如:
学生编号(PK) 姓名 班级编号 班级名称
其中 **班级名称依赖于班级编号** ,**班级编号又依赖于学生编号**
一对多,两张表,多的表加外键;
总结表设计
#一对多:两张表,多的加外键
#多对多:三张表,关系表加外键
#一对一:一张表字段比较多的时候可以对表进行拆分,一张表叫登录表(外键+唯一,即 FK+unique),一张叫用户信息表;
一对一,外键唯一
但是有的时候需要拿冗余换速度,因为表连接越多,速度就越慢(笛卡尔积),并且对于开发人员来说,sql
语句的编写难度也会降低;主要是为了满足用户的目的;
课后作业
1.取得每个部门最高薪水的人员名称
#第一步:取得每个部门最高薪水(按照部门编号分组,找出每一组最大值)
mysql> select deptno,max(sal) as maxsal from emp group by deptno;
+--------+---------+
| deptno | maxsal |
+--------+---------+
| 10 | 5000.00 |
| 20 | 3000.00 |
| 30 | 2850.00 |
+--------+---------+
#第二步:将以上的查询结果当做一张临时表t,
t和emp表连接,条件:t.deptno = e.deptno and t.maxsal = e.sal
select
e.ename, t.*
from
emp e
join
(select deptno,max(sal) as maxsal from emp group by deptno) t
on
t.deptno = e.deptno and t.maxsal = e.sal;
+-------+--------+---------+
| ename | deptno | maxsal |
+-------+--------+---------+
| BLAKE | 30 | 2850.00 |
| SCOTT | 20 | 3000.00 |
| KING | 10 | 5000.00 |
| FORD | 20 | 3000.00 |
+-------+--------+---------+
2.哪些人的薪水在部门的平均薪水之上
#第一步,取得各部门的平均薪资
select deptno,avg(sal) avgsal from emp group by deptno;
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 20 | 2175.000000 |
| 30 | 1566.666667 |
| 10 | 2916.666667 |
+--------+-------------+
#第二步,将上边得到的表作为 t 表和原本的表做连接,然后筛选
select
e.ename,t.*
from
emp as e,(select deptno,avg(sal) avgsal from emp group by deptno) as t
where
e.deptno = t.deptno and e.sal > t.avgsal;
+-------+--------+-------------+
| ename | deptno | avgsal |
+-------+--------+-------------+
| ALLEN | 30 | 1566.666667 |
| JONES | 20 | 2175.000000 |
| BLAKE | 30 | 1566.666667 |
| SCOTT | 20 | 2175.000000 |
| KING | 10 | 2916.666667 |
| FORD | 20 | 2175.000000 |
+-------+--------+-------------+
3.取得各个部门中所有人薪水等级的平均值
#第一步,取得部门中(所有人)的平均薪水等级
select
e.ename,e.sal,e.deptno,s.grade
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal;
#第二步,连接,然后输出
select
e.deptno,avg(s.grade) as avggrade
from
emp e
left join
salgrade s
on
e.sal between s.losal and s.hisal
group by
e.deptno;
4.不准用组函数(Max ),取得最高薪水
#思路一:排序加limit
select
*
from
emp
order by
sal desc
limit 0,1;
+-------+-------+-----------+------+------------+---------+------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+-------+-----------+------+------------+---------+------+--------+
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
+-------+-------+-----------+------+------------+---------+------+--------+
#第二种方案:表的自连接
select sal from emp where sal not in(select distinct a.sal from emp a join emp b on a.sal < b.sal);
5.取得平均薪水最高的部门的部门编号
#第一种方案:先取出每个部门的平均工资的表
SELECT deptno
FROM (
SELECT deptno, AVG(sal) AS avgsal
FROM emp
GROUP BY deptno
) a
JOIN (
SELECT MAX(avgsal) AS sal
FROM (
SELECT deptno, AVG(sal) AS avgsal
FROM emp
GROUP BY deptno
) b
) c ON a.avgsal = c.sal;
#这里一定要注意在 SQL 语句中,不能在子查询中使用别名
+--------+
| deptno |
+--------+
| 10 |
+--------+
#第二种方案:排序和limit结合的方案
SELECT
*
FROM
(SELECT deptno,AVG(sal) as avgsal FROM emp GROUP BY deptno) as a
ORDER BY
avgsal DESC
LIMIT
0,1;
#要注意的点:每个子表都得有自己的别名
+--------+-------------+
| deptno | avgsal |
+--------+-------------+
| 10 | 2916.666667 |
+--------+-------------+
6.取得平均薪水最高的部门的部门名称
#先找到平均薪水最高的部门的编号,然后表连接取到名称
#先找出最高的部门平均薪水是多少
SELECT
DISTINCT MAX(avgsal) AS maxavgsal
FROM
(SELECT
AVG(sal) AS avgsal
FROM
emp
GROUP BY
deptno) AS avem;
#用上面的表作为一个子表,找出平均薪资等于这个最高薪水的部门的部门编号
SELECT
a.deptno
FROM
(SELECT
deptno,AVG(sal) AS avgsal
FROM
emp
GROUP BY
deptno
) AS a
JOIN
(SELECT
DISTINCT MAX(avgsal) AS maxavgsal
FROM
(SELECT
AVG(sal) AS avgsal
FROM
emp
GROUP BY
deptno) AS avem
) AS b
ON
a.avgsal = b.maxavgsal;
#和别的表连接(内连接)
SELECT
a.*,d.dname
FROM
(SELECT
a.deptno
FROM
(SELECT
deptno,AVG(sal) AS avgsal
FROM
emp
GROUP BY
deptno
) AS a
JOIN
(SELECT
DISTINCT MAX(avgsal) AS maxavgsal
FROM
(SELECT
AVG(sal) AS avgsal
FROM
emp
GROUP BY
deptno) AS avem
) AS b
ON
a.avgsal = b.maxavgsal
) AS a
JOIN
dept d
ON
a.deptno = d.deptno;
#上面这个真的又臭又长,建议使用别人的这个
select
d.dname,avg(e.sal) as avgsal
from
emp e
join
dept d
on
e.deptno = d.deptno
group by
d.dname
order by
avgsal desc
limit
1;
7.求平均薪水的等级最低的部门的部门名称
#平均薪水等级最低不就是平均薪水最低???
select
a.dname,s.grade
from
(select
d.dname,avg(sal) as avgsal
from
emp e
join
dept d
on
e.deptno = d.deptno
group by
d.dname) as a
left join #这里用左连接的意思是把在同一个等级的部门的名字都展示出来
salgrade s
on
a.avgsal between s.losal and s.hisal
order by
s.grade asc
limit 1;
+-------+-------+
| dname | grade |
+-------+-------+
| SALES | 3 |
+-------+-------+
8.取得比普通员工(员工代码没有在 mgr 字段上出现的) 的最高薪水还要高的领导人姓名
select ename,sal from emp where sal > (select max(sal) from emp where empno not in(select distinct mgr from emp where mgr is not null));
9.取得薪水最高的前五名员工
select ename,sal from emp order by sal desc limit 5;
10.取得薪水最高的前五名员工
select ename from emp order by sal desc limit 5,5;
+--------+---------+
| ename | sal |
+--------+---------+
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| WARD | 1250.00 |
+--------+---------+
11.取得最后入职的 5 名员工
desc emp;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| EMPNO | int | NO | PRI | NULL | |
| ENAME | varchar(10) | YES | | NULL | |
| JOB | varchar(9) | YES | | NULL | |
| MGR | int | YES | | NULL | |
| HIREDATE | date | YES | | NULL | | #这里日期的形式是 date 日期, 日期也可以进行升序或降序排列
| SAL | double(7,2) | YES | | NULL | |
| COMM | double(7,2) | YES | | NULL | |
| DEPTNO | int | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
select ename,hiredate from emp order by hiredate desc limit 5;
+--------+------------+
| ename | hiredate |
+--------+------------+
| ADAMS | 1987-05-23 |
| SCOTT | 1987-04-19 |
| MILLER | 1982-01-23 |
| JAMES | 1981-12-03 |
| FORD | 1981-12-03 |
+--------+------------+
12.取得每个薪水等级有多少员工
#先确定每个员工在什么薪水等级
select ename,grade from emp e join salgrade s on e.sal between s.losal and s.hisal;
#创建一个视图 这样方便后面用
create view e_salgrade as (select ename,grade from emp e join salgrade s on e.sal between s.losal and s.hisal);
#用这个视图分组分析计数
select grade,count(ename) from e_salgrade group by grade;
+-------+--------------+
| grade | count(ename) |
+-------+--------------+
| 1 | 3 |
| 3 | 2 |
| 2 | 3 |
| 4 | 5 |
| 5 | 1 |
+-------+--------------+
#扔掉这个视图
drop view e_salgrade;
13.面试题:
## 有 3 个表 S(学生表),C(课程表),SC(学生选课表)
S(SNO,SNAME)代表(学号,姓名)
C(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
SC(SNO,CNO,SCGRADE)代表(学号,课号,成绩)
问题:
1,找出没选过“黎明”老师的所有学生姓名。
#思路:先弄一个选过黎明老师的课的视图,用学号连接学生表就可以了
start transaction;
create view s_liming as (select sc.sno as sno from sc join c on sc.cno = s.cno && s.cteacher = '黎明');
select sname from s join s_liming on s.sno not in s_liming.sno);
2,列出 2 门以上(含2 门)不及格学生姓名及平均成绩。
#思路:对sc表用学号分组,然后用分组函数统计每个学号的成绩不及格的课数和平均成绩,用这个表做个视图;然后和s表连接展示姓名和成绩
3,即学过 1 号课程又学过 2 号课所有学生的姓名。
#选出1号课程选的人,再选出2号课程选的人,之后对这两个表做一个连接
14.列出所有员工及领导的姓名
#需要一个左连接,把所有员工名字都弄出来
select a.ename as '员工',b.ename as '领导' from emp a left join emp b on a.mgr = b.empno;
+--------+-------+
| 员工 | 领导 |
+--------+-------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| KING | NULL |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+-------+
15.列出受雇日期早于其直接上级的所有员工的编号,姓名,部门名称
#思路:这个不需要左连接了就,先新建一个包含员工姓名、入职时间和其领导入职时间的视图
CREATE VIEW emp1
AS
SELECT a.empno,a.ename,a.hiredate as h1,b.ename as e1,b.hiredate as h2,a.deptno from emp a join emp b on a.mgr = b.empno;
#用emp1和dept连接并筛选
SELECT
a.ename as '姓名',a.e1 as '领导',a.h1 as hirdate,b.dname as '部门名称'
FROM
emp1 a
join
dept b
ON
a.deptno = b.deptno
WHERE
a.h1 < a.h2;
#结果:
+--------+--------+------------+--------------+
| 姓名 | 领导 | hirdate | 部门名称 |
+--------+--------+------------+--------------+
| SMITH | FORD | 1980-12-17 | RESEARCH |
| ALLEN | BLAKE | 1981-02-20 | SALES |
| WARD | BLAKE | 1981-02-22 | SALES |
| JONES | KING | 1981-04-02 | RESEARCH |
| BLAKE | KING | 1981-05-01 | SALES |
| CLARK | KING | 1981-06-09 | ACCOUNTING |
+--------+--------+------------+--------------+
#这个只用了一个连接,这个更简单,简单很多;
select
a.ename '员工', a.hiredate, b.ename '领导', b.hiredate, d.dname
from
emp a
join
emp b
on
a.mgr = b.empno
join
dept d
on
a.deptno = d.deptno
where
a.hiredate < b.hiredate;
16.列出部门名称和这些部门的员工信息, 同时列出那些没有员工的部门
#考的是一个右连接
select
e.*,d.dname
from
emp e
right join
dept d
on
e.deptno = d.deptno;
SELECT
b.dname,a.ename
FROM
emp a
LEFT JOIN
dept b
ON
a.deptno = b.deptno;
+-------+--------+-----------+------+------------+---------+---------+--------+------------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO | dname |
+-------+--------+-----------+------+------------+---------+---------+--------+------------+
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 | ACCOUNTING |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 | ACCOUNTING |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 | ACCOUNTING |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 | RESEARCH |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 | RESEARCH |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 | RESEARCH |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 | RESEARCH |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 | RESEARCH |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 | SALES |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 | SALES |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 | SALES |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 | SALES |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 | SALES |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 | SALES |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | OPERATIONS |
+-------+--------+-----------+------+------------+---------+---------+--------+------------+
17.列出至少有 5 个员工的所有部门
#可以先做一个视图,然后用这个视图做分析
create view dept_num as (select d.dname,count(e.ename) as empnum from emp e join dept d on e.deptno = d.deptno group by deptno);
#对这个视图做一个筛选
select dname,empnum from dept_num where empnum >= 5;
+----------+--------+
| dname | empnum |
+----------+--------+
| RESEARCH | 5 |
| SALES | 6 |
+----------+--------+
18.列出薪金比"SMITH" 多的所有员工信息
create view sal_simith as (select sal from emp where ename = 'smith');
select e.ename,e.sal from emp e join sal_simith s on e.sal > s.sal;
+--------+---------+
| ename | sal |
+--------+---------+
| ALLEN | 1600.00 |
| WARD | 1250.00 |
| JONES | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| TURNER | 1500.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| FORD | 3000.00 |
| MILLER | 1300.00 |
19.列出所有"CLERK"( 办事员) 的姓名及其部门名称, 部门的人数
#创建一个包括部门编号、名称和人数的视图
create view dept_view as select e.deptno,d.dname,count(e.ename) from emp e join dept d on e.deptno = d.deptno group by e.deptno;
+--------+------------+----------------+
| deptno | dname | count(e.ename) |
+--------+------------+----------------+
| 20 | RESEARCH | 5 |
| 30 | SALES | 6 |
| 10 | ACCOUNTING | 3 |
+--------+------------+----------------+
#然后用emp连接这个表就可以了
select e.ename,d.* from emp e join dept_view d on e.job = 'clerk' and e.deptno = d.deptno;
+--------+--------+------------+----------------+
| ename | deptno | dname | count(e.ename) |
+--------+--------+------------+----------------+
| SMITH | 20 | RESEARCH | 5 |
| ADAMS | 20 | RESEARCH | 5 |
| JAMES | 30 | SALES | 6 |
| MILLER | 10 | ACCOUNTING | 3 |
+--------+--------+------------+----------------+
20.列出最低薪金大于 1500 的各种工作及从事此工作的全部雇员人数
#按工作岗位分组求最小值,然后比较选出工作
select jobfrom emp group by job having min(sal) > 1500;
+-----------+
| job |
+-----------+
| MANAGER |
| ANALYST |
| PRESIDENT |
+-----------+
#选出job是这个工作的人
select ename,job from emp where job in (select job from emp group by job having min(sal) > 1500);
+-------+-----------+
| ename | job |
+-------+-----------+
| JONES | MANAGER |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| SCOTT | ANALYST |
| KING | PRESIDENT |
| FORD | ANALYST |
+-------+-----------+
21.列出在部门"SALES"< 销售部> 工作的员工的姓名, 假定不知道销售部的部门编号.
select ename from emp where deptno = (select deptno from dept where dname = 'sales');
+--------+
| ename |
+--------+
| ALLEN |
| WARD |
| MARTIN |
| BLAKE |
| TURNER |
| JAMES |
+--------+
22.列出薪金高于公司平均薪金的所有员工, 所在部门, 上级领导, 雇员的工资等级.
#先找出薪资高于高于平均薪资的员工的所有信息
select empno,ename,sal,mgr,deptno from emp where sal > (select avg(sal) from emp);
+-------+-------+---------+------+--------+
| empno | ename | sal | mgr | deptno |
+-------+-------+---------+------+--------+
| 7566 | JONES | 2975.00 | 7839 | 20 |
| 7698 | BLAKE | 2850.00 | 7839 | 30 |
| 7782 | CLARK | 2450.00 | 7839 | 10 |
| 7788 | SCOTT | 3000.00 | 7566 | 20 |
| 7839 | KING | 5000.00 | NULL | 10 |
| 7902 | FORD | 3000.00 | 7566 | 20 |
+-------+-------+---------+------+--------+
#上边这个创建成一个视图,然后先和salgrade连接,再和dept连接,用union连接
create view emp_info as (select empno,ename,sal,mgr,deptno from emp where sal > (select avg(sal) from emp));
select e.*,s.grade from emp_info e join salgrade s on e.sal between s.losal and hisal;
+-------+-------+---------+------+--------+-------+
| empno | ename | sal | mgr | deptno | grade |
+-------+-------+---------+------+--------+-------+
| 7902 | FORD | 3000.00 | 7566 | 20 | 4 |
| 7788 | SCOTT | 3000.00 | 7566 | 20 | 4 |
| 7782 | CLARK | 2450.00 | 7839 | 10 | 4 |
| 7698 | BLAKE | 2850.00 | 7839 | 30 | 4 |
| 7566 | JONES | 2975.00 | 7839 | 20 | 4 |
| 7839 | KING | 5000.00 | NULL | 10 | 5 |
+-------+-------+---------+------+--------+-------+
select d.dname from emp_info e join dept d on e.deptno = d.deptno;
+------------+
| dname |
+------------+
| RESEARCH |
| SALES |
| ACCOUNTING |
| RESEARCH |
| ACCOUNTING |
| RESEARCH |
+------------+
#上边这两个表不能用union连接,因为他们列数不同; union连接需要列数相同而且数据类型相似;
#这里采用两次连接,避免一次二连接造成的笛卡尔积现象;
select a.*,d.dname from (select e.*,s.grade from emp_info e join salgrade s on e.sal between s.losal and hisal) a join dept d on a.deptno = d.deptno;
+-------+-------+---------+------+--------+-------+------------+
| empno | ename | sal | mgr | deptno | grade | dname |
+-------+-------+---------+------+--------+-------+------------+
| 7902 | FORD | 3000.00 | 7566 | 20 | 4 | RESEARCH |
| 7788 | SCOTT | 3000.00 | 7566 | 20 | 4 | RESEARCH |
| 7782 | CLARK | 2450.00 | 7839 | 10 | 4 | ACCOUNTING |
| 7698 | BLAKE | 2850.00 | 7839 | 30 | 4 | SALES |
| 7566 | JONES | 2975.00 | 7839 | 20 | 4 | RESEARCH |
| 7839 | KING | 5000.00 | NULL | 10 | 5 | ACCOUNTING |
+-------+-------+---------+------+--------+-------+------------+
23.列出与"SCOTT" 从事相同工作的所有员工及部门名称
select e.ename,d.dname,e.job from emp e left join dept d on e.deptno = d.deptno where e.job = (select job from emp where ename = 'scott');
+-------+----------+---------+
| ename | dname | job |
+-------+----------+---------+
| SCOTT | RESEARCH | ANALYST |
| FORD | RESEARCH | ANALYST |
+-------+----------+---------
24.列出薪金等于部门 30 中员工的薪金的其他员工的姓名和薪金.
#先创建一个除30部门的所有员工的薪资和姓名的视图
create view name_sal as select ename,sal from emp where deptno <> 30;
#然后用emp和它相连找出薪资在30部门的,
select n.ename,n.sal from name_sal n join (select sal from emp where deptno = 30) s on (n.sal = s.sal);
Empty set (0.00 sec);
25.列出薪金高于在部门 30 工作的所有员工的薪金的员工姓名和薪金. 部门名称
#找出薪资高的员工的信息做成视图
select deptno,ename,sal from emp where sal > (select max(sal) from emp where deptno = 30);
#再和dept连接选出部门名称
select e.ename,e.sal,d.dname from v_emp_high e join dept d on e.deptno = d.deptno;
+-------+---------+------------+
| ename | sal | dname |
+-------+---------+------------+
| JONES | 2975.00 | RESEARCH |
| SCOTT | 3000.00 | RESEARCH |
| KING | 5000.00 | ACCOUNTING |
| FORD | 3000.00 | RESEARCH |
+-------+---------+------------+
26.列出在每个部门工作的员工数量, 平均工资和平均服务期限
## 这里边有一个时间的计算,这个函数有点那啥
select d.deptno,count(ename) empnum,ifnull(avg(sal),0) avgsal,ifnull(avg(timestampdiff(YEAR, hiredate, now())),0) avgyear from emp right join dept d on emp.deptno = d.deptno group by deptno;
+--------+--------+-------------+----------------+
| deptno | ecount | avgsal | avgservicetime |
+--------+--------+-------------+----------------+
| 10 | 3 | 2916.666667 | 38.0000 |
| 20 | 5 | 2175.000000 | 35.8000 |
| 30 | 6 | 1566.666667 | 38.3333 |
| 40 | 0 | 0.000000 | 0.0000 |
+--------+--------+-------------+----------------+
##这个函数的使用:
在mysql当中怎么计算两个日期的“年差”,差了多少年?
TimeStampDiff(间隔类型, 前一个日期, 后一个日期)
timestampdiff(YEAR, hiredate, now())
间隔类型:
SECOND 秒,
MINUTE 分钟,
HOUR 小时,
DAY 天,
WEEK 星期
MONTH 月,
QUARTER 季度,
YEAR 年
27.列出所有员工的姓名、部门名称和工资。
# 需要一个外连接,因为是所有
select e.ename,e.sal,d.dname from emp e left join dept d on e.deptno = d.deptno;
+--------+---------+------------+
| ename | sal | dname |
+--------+---------+------------+
| SMITH | 800.00 | RESEARCH |
| ALLEN | 1600.00 | SALES |
| WARD | 1250.00 | SALES |
| JONES | 2975.00 | RESEARCH |
| MARTIN | 1250.00 | SALES |
| BLAKE | 2850.00 | SALES |
| CLARK | 2450.00 | ACCOUNTING |
| SCOTT | 3000.00 | RESEARCH |
| KING | 5000.00 | ACCOUNTING |
| TURNER | 1500.00 | SALES |
| ADAMS | 1100.00 | RESEARCH |
| JAMES | 950.00 | SALES |
| FORD | 3000.00 | RESEARCH |
| MILLER | 1300.00 | ACCOUNTING |
+--------+---------+------------+
28.列出所有部门的详细信息和人数
#这个是自己写的(比较啰嗦)
select d.*,ifnull(e.num,0) salnum from dept d left join (select deptno,count(ename) as num from emp group by deptno) e on d.deptno=e.deptno;
#简洁版本
select
d.*,count(e.ename)
from
emp e
right join
dept d
on
e.deptno = d.deptno
group by
d.deptno,d.dname,d.loc;
+--------+------------+----------+----------------+
| DEPTNO | DNAME | LOC | count(e.ename) |
+--------+------------+----------+----------------+
| 10 | ACCOUNTING | NEW YORK | 3 |
| 20 | RESEARCH | DALLAS | 5 |
| 30 | SALES | CHICAGO | 6 |
| 40 | OPERATIONS | BOSTON | 0 |
+--------+------------+----------+----------------+
29.列出各种工作的最低工资及从事此工作的雇员姓名
select
e.ename,a.*
from
(select job,min(sal) minsal from emp group by job) a
join
emp e
on
a.minsal = e.sal
order by
job asc;
+--------+-----------+---------+
| ename | job | minsal |
+--------+-----------+---------+
| SCOTT | ANALYST | 3000.00 |
| FORD | ANALYST | 3000.00 |
| SMITH | CLERK | 800.00 |
| CLARK | MANAGER | 2450.00 |
| KING | PRESIDENT | 5000.00 |
| WARD | SALESMAN | 1250.00 |
| MARTIN | SALESMAN | 1250.00 |
+--------+-----------+---------+
30.列出各个部门的 MANAGER( 领导) 的最低薪金
select
deptno, min(sal)
from
emp
where
job = 'MANAGER'
group by
deptno;
+--------+----------+
| deptno | min(sal) |
+--------+----------+
| 20 | 2975.00 |
| 30 | 2850.00 |
| 10 | 2450.00 |
+--------+----------+
31.列出所有员工的 年工资, 按 年薪从低到高排序
SELECT ename,((sal+ IFNULL(comm,0))*12) yearsal FROM emp ORDER BY yearsal;
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 9600.00 |
| JAMES | 11400.00 |
| ADAMS | 13200.00 |
| MILLER | 15600.00 |
| TURNER | 18000.00 |
| WARD | 21000.00 |
| ALLEN | 22800.00 |
| CLARK | 29400.00 |
| MARTIN | 31800.00 |
| BLAKE | 34200.00 |
| JONES | 35700.00 |
| SCOTT | 36000.00 |
| FORD | 36000.00 |
| KING | 60000.00 |
+--------+----------+
32.求出员工领导的薪水超过3000的员工名称与领导
#自己想得到,太麻烦了,还容易错(主要还是对表连接的原理不够熟悉)
SELECT
e.ename,a.*
FROM
(SELECT distinct a.ename ename,a.sal sal,a.empno empno FROM emp a join emp b on a.empno = b.mgr where a.sal > 3000) a
JOIN
emp e
ON
e.mgr = a.empno;
+-------+-------+---------+
| ename | ename | sal |
+-------+-------+---------+
| JONES | KING | 5000.00 |
| BLAKE | KING | 5000.00 |
| CLARK | KING | 5000.00 |
+-------+-------+---------+
#这个简单的:
select
a.ename '员工',b.ename '领导'
from
emp a
join
emp b
on
a.mgr = b.empno
where
b.sal > 3000;
33.求出部门名称中, 带’S’字符的部门员工的工资合计、部门人数
select
d.dname,
sum(e.sal) salall,
count(e.ename) nums
from
emp e
join
dept d
on
e.deptno = d.deptno
where
d.dname like '%S%'
group by
e.deptno;
+----------+----------+------+
| dname | salall | nums |
+----------+----------+------+
| RESEARCH | 10875.00 | 5 |
| SALES | 9400.00 | 6 |
+----------+----------+------+
34.给任职日期超过 30 年的员工加薪 10%.
update emp set sal = sal * 1.1 where timestampdiff(YEAR, hiredate, now()) > 30;
#更新之前
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
#更新之后
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 880.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1760.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1375.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 3272.50 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1375.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 3135.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2695.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3300.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5500.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1650.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1210.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 1045.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3300.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1430.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+