一、需求调研、需求分析(即应用场景)
ds:软件的存在就是处理数据的,而我们的数据是存储在所谓的仓库当中的。
二、框架的设计思想
ds:严格来说,mysql数据库中的数据也是存储在一个或多个文件上的。就像我们之前在学习使用javaSE时,使用ObjectOutputStream把java对象序列化,然后放到一个文件当中。我们还布置过一个作业,即使用流的方式对文件中的数据进行增删改查。实际上,以上的操作就是最原始的数据库,只不过这个“数据库”它有点low。
三、体系的组织结构设计(重要组件、模块划分、模块间交互)
数据库:
英文单词DataBase,简称DB。按照一定格式存储数据的一些文件的组合。
顾名思义:存储数据的仓库,实际上就是一堆文件。
这些文件中存储了具有特定格式的数据。
数据库管理系统:
DataBaseManagement,简称DBMS。
数据库管理系统是专门用来管理数据库中数据的,
数据库管理系统可以对数据库当中的数据进行增删改查。
常见的数据库管理系统:MySQL、Oracle、MS SqlServer、DB2、sybase等….
SQL:结构化查询语言
程序员需要学习SQL语句,程序员通过编写SQL语句,
然后DBMS负责执行SQL语句,最终来完成数据库中数据的增删改查操作。
SQL是一套标准,程序员主要学习的就是SQL语句,
这个SQL在mysql中可以使用,同时在Oracle中也可以使用,在DB2中也可以使用。
三者之间的关系?
DBMS–执行–> SQL –操作–> DB
四、工作原理、运行流程
- DBMS–>执行–> SQL –操作–> DB:程序员通过编写SQL语句,然后DBMS负责执行SQL语句,最终来完成数据库中数据的增删改查操作。sql运行流程是:编译sql语句,执行sql语句。即DBMS拿到一条sql语句,会先进行SQL语句的编译,不符合语法,编译报错。编译未完成后,才能执行sql语句。
- from –> where –> group by –> having –> select –> order by –> limit(排序总是在最后执行!)
from:从某张表中查询数据,
where:先经过where条件筛选出有价值的数据。
group by:对这些有价值的数据进行分组。
having:分组之后可以使用having继续筛选。
select:select查询出来。
order by:最后排序输出!
五、具体1:DQL语句
1、group by having
(1)序言:
- 在实际的应用中,可能有这样的需求,需要先进行分组,然后对每一组的数据进行操作。
(2)group by子句:
- 案例1:select ename,sal from emp where sal > min(sal);错在哪里? 首先,有因为分组函数在使用的时候必须先分组之后才能使用。其次,有执行顺序:from –> where –> group by –> having –> select –> order by。接着,从执行顺序看,where在执行时还没有分组。最后,所以where后面不能出现分组函数。因此,注意:分组函数在使用的时候必须先进行分组,然后才能用。如果你没有对数据进行分组,整张表默认为一组。
- 案例2:select sum(sal) from emp;对在哪里?这个没有分组,为啥sum()函数可以用呢?因为select在group by之后执行。从执行顺序看,select在执行时已经完成分组。即使此时你没有对数据进行分组,整张表默认为一组。
- 案例3:找出每个工作岗位的工资和?实现思路:按照工作岗位分组,然后对工资求和。select job,sum(sal) from emp group by job;以上这个语句的执行顺序?首先,从emp表中查询数据。然后,根据job字段进行分组。最后,对每一组的数据进行sum(sal)。
- 案例4:select ename,job,sum(sal) from emp group by job;错在哪里?错在:select后面不能加ename。以上语句在mysql中可以执行,但是毫无意义。以上语句在oracle中执行报错,oracle的语法比mysql的语法严格(mysql的语法相对来说松散一些,不那么严格!)。重点结论:在一条select语句当中,如果有group by语句的话,select后面只能跟:参加分组的字段,以及分组函数,其它的一律不能跟(没有意义,甚至在其它的语法要求比较严格的数据库中会报错)。
- 案例5:group by后面跟多个字段。找出“每个部门,不同工作岗位”的最高薪资?思路:首先,按照部门进行分组;然后,再按照工作岗位进行分组。技巧:两个字段联合成1个字段看。(两个字段联合分组)。select deptno, job, max(sal) from emp group by deptno, job;
(3)having子句:
使用having可以对分完组之后的数据进一步过滤。
having不能单独使用,having必须和group by联合使用。
having不能代替where。
- 案例1:找出每个部门最高薪资,要求显示最高薪资大于3000的?1
第一步:找出每个部门最高薪资。思路:按照部门编号分组,求每一组最大值:
select deptno,max(sal) from emp group by deptno;
第二步:要求显示最高薪资大于3000:select deptno,max(sal) from emp group by deptno having max(sal) > 3000;
-
案例2:只能使用group by having,而使用where实现不了的情况:
找出每个部门平均薪资,要求显示平均薪资高于2500的。第一步:找出每个部门平均薪资
select deptno,avg(sal) from emp group by deptno;
第二步:要求显示平均薪资高于2500的
select deptno,avg(sal) from emp group by deptno having avg(sal) > 2500;
2、连接查询
序言
什么是连接查询?
从一张表中单独查询,称为单表查询。
emp表和dept表联合起来查询数据(如:从emp表中取员工名字,从dept表中取部门名字),这种跨表查询(即多张表联合起来查询数据)被称为连接查询。
知识点
- 迪卡尔积
当两张表进行连接查询时,没有任何条件的限制。结果集:第1张表的记录数 * 第2张表的记录数。例如:select ename,dname from emp, dept;
ds:两张表连接查询的时候,工作流程永远是这样的:首先,拿第1张表的第1条数据,和第2张表的所有数据进行配对,得到配对成功的两条记录,连成一条长长的记录。其次,以此类推荐,即拿第1张表的第2条记录去和第2张表的所有记录进行配对。怎么避免笛卡尔积现象?连接时加连接条件,满足这个条件的两条记录被筛选出来,进行配对,连成一条长长的记录。
SQL92语法:select e.ename,d.dname from emp e, dept d where e.deptno = d.deptno;
思考:以上查询语句,最终查询的结果条数是14条,但是匹配的过程中,匹配的次数减少了吗?还是56次,只不过进行了四选一。因此,匹配次数并没有减少。
注意:通过笛卡尔积现象得出,表的连接次数越多效率越低,尽量避免表的连接次数。
- 内连接
- 等值连接:select … from a join b on a和b的连接条件 where 筛选条件
92语法:select e.ename,d.dname from emp e, dept d where e.deptno = d.deptno;
92的缺点:结构不清晰。表的连接条件和后期进一步筛选的条件,都放到了where后面。
99语法:select e.ename,d.dname from emp e join dept d on e.deptno = d.deptno;
//inner可以省略(带着inner可读性更好!!!一眼就能看出来是内连接)
select e.ename,d.dname from emp e inner join dept d on e.deptno = d.deptno
// 条件是等量关系,所以被称为等值连接。99优点:结构不清晰。表连接的条件是独立的,连接条件放在on后面。而连接之后,如果还需要进一步筛选,再往后继续添加where。
- 非等值连接:条件不是一个等量关系,称为非等值连接。
案例:找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级?
select e.ename, e.sal, s.grade from emp e join salgrade s on e.sal between s.losal and s.hisal; - 自连接:技巧:一张表看做两张表。
案例:查询员工的上级领导,要求显示员工名和对应的领导名?select a.ename as '员工名', b.ename as '领导名' from emp a join emp b on
a.mgr = b.empno;
//条件:员工的领导编号 = 领导的员工编号
- 等值连接:select … from a join b on a和b的连接条件 where 筛选条件
- 外连接:outer是可以省略的,带着可读性强。
- 左外连接(左连接):左边表的记录全查出来。如果右边表没有匹配记录时,补空记录
- 右外连接(右连接):右边表的记录全查出来。如果左边表没有匹配记录时,补空记录
- 注意:外连接的查询结果条数一定是 >= 内连接的查询结果条数?正确。
- 全连接(不讲):a表和b表都是主表,都查出来。极少用,不讲。
-
三张表,四张表怎么连接?语法:
select …
from a join b on a和b的连接条件
join c on a/b和c的连接条件
right join d on a/b/c和d的连接条件
一条SQL中内连接和外连接可以混合,即都可以出现,随便出现。
理解:a和b连接完成后的结果集(一条长长的记录),再去和c表连接,…..再和d表连接案例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,e.sal,d.dname,s.grade,l.ename
from emp e join dept d on e.deptno = d.deptno
join salgrade s on e.sal between s.losal and s.hisal
left join emp l on e.mgr = l.empno;
3、子查询
序言
什么是子查询?select语句中嵌套select语句,被嵌套的select语句称为子查询。
子查询都可以出现在哪里呢?
select ..(select).
from ..(select).
where ..(select).
知识点
- where子句中的子查询
案例:找出比最低工资高的员工姓名和工资?
思路:
第一步:查询最低工资是多少。select min(sal) from emp;
第二步:找出>800的。select ename,sal from emp where sal > 800;
第三步:合并。select ename,sal from emp where sal > (select min(sal) from emp);流程:先执行子查询,再执行主查询。
-
from子句中的子查询:from后面的子查询,可以将子查询的查询结果当做一张临时表
案例:找出每个岗位的平均工资的薪资等级。
第一步:找出每个岗位的平均工资(按照岗位分组求平均值)
select job,avg(sal) from emp group by job;
第二步:等级表(s表)
select * from salgrade;
第三步:t表和s表进行表连接,条件:t表avg(sal) between s.losal and s.hisal;
select
t.*, s.grade
from
(select job,avg(sal) as avgsal from emp group by job) t
join
salgrade s
on
t.avgsal between s.losal and s.hisal;
注意:克服心理障碍,把以上的查询结果就当做一张真实存在的表t。
注意:在子查询中使用函数的地方要起别名 -
select后面出现的子查询(这个内容不需要掌握,了解即可!!!)
案例:找出每个员工的部门名称,要求显示员工名,部门名?
select
e.ename,e.deptno,(select d.dname from dept d where e.deptno = d.deptno) as dname
from
emp e;
//错误:ERROR 1242 (21000): Subquery returns more than 1 row
select
e.ename,e.deptno,(select dname from dept) as dname
from
emp e;
注意:对于select后面的子查询来说,这个子查询只能一次返回1条结果,多于1条,就报错了。!
4、limit:分页查询
limit作用:将查询结果集的一部分取出来。通常使用在分页查询当中。
完整用法:limit startIndex, length。
startIndex是起始下标,length是长度。
起始下标从0开始。
缺省用法:limit 5; 这是取前5.
注意:mysql当中limit在order by之后执行!!!!!!
记公式:limit (pageNo-1)*pageSize , pageSize
5、其它语法
- and、or混用:and和or同时出现,and优先级较高。如果想让or先执行,需要加“小括号”。以后在开发中,如果不确定优先级,就加小括号就行了。
- in、or的关系:in表示包含,相当于多个 or。注意:in不是一个区间,in后面跟的是具体的值。
-
like转义字符:找出名字中有“_”的?select name from t_student where name like '%_%'; //这样不行,应该这样写:mysql> select name from t_student where name like '%\_%'; // \转义字符。
-
order by 多字段:select ename,sal from emp order by sal asc, ename asc; // sal在前,起主导,只有sal相等的时候,才会考虑启用ename(所以可能用不上)排序。
-
null的理解:null不是一个值,null是什么也没有。
-
单行处理函数与多行处理(分组)函数有区别:单行处理函数的特点:一个输入(一行记录)对应一个输出。和单行处理函数相对的是:多行处理函数。(多行处理函数特点:多个输入,对应1个输出!)
-
单行处理函数finull、null参与的运算:注意:NULL只要参与运算,最终结果一定是NULL。为了避免这个现象,需要使用ifnull函数。ifnull函数用法:ifnull(数据, 被当做哪个值)。
-
单行处理函数case..when..then..when..then..else..end:当员工的工作岗位是MANAGER的时候,工资上调10%,当工作岗位是SALESMAN的时候,工资上调50%,其它正常。(注意:不修改数据库,只是将查询结果显示为工资上调)
select ename,job, sal as oldsal,
(case job when 'MANAGER' then sal*1.1 when 'SALESMAN' then sal*1.5 else sal end) as newsal
from emp;
-
多行处理(分组)函数与分组的关系:(1)分组函数在使用的时候必须先进行分组,然后才能用。如果你没有对数据进行分组,整张表默认为一组。
-
多行处理(分组)函数使用注意事项:(1)分组函数自动忽略NULL,你不需要提前对NULL进行处理。(2)分组函数中count(*)和count(具体字段)有什么区别?count(具体字段):表示统计该字段下所有不为NULL的元素的总数。count(*):统计表当中的总行数。(只要有一行数据count则++)因为每一行记录不可能都为NULL,一行数据中有一列不为NULL,则这行数据就是有效的。(3)分组函数不能够直接使用在where子句中。(4)所有的分组函数可以组合起来一起用。
-
distinct最前方:select ename,distinct job from emp,这样编写是错误的,语法错误。(1)distinct只能出现在所有字段的最前方。(2)distinct出现在job,deptno两个字段之前,表示两个字段联合起来去重,如 select distinct job,deptno from emp。(3)以下是正确的:统计一下工作岗位的数量?select count(distinct job) from emp;
-
union。(1)union在进行结果集合并的时候,要求两个结果集的列数相同。(2)MYSQL可以,但oracle语法严格 ,不可以,报错。oracle中要求:结果集合并时列和列的数据类型也要一致。
六、具体2:关于mysql中的数据类型?
1、很多数据类型,我们只需要掌握一些常见的数据类型即可。
- varchar(最长255)
- 可变长度的字符串
- 比较智能,节省空间。
- 会根据实际的数据长度动态分配空间。
- 优点:节省空间
- 缺点:需要动态分配空间,速度慢。
- char(最长255)
- 定长字符串
- 不管实际的数据长度是多少。
- 分配固定长度的空间去存储数据。
- 使用不恰当的时候,可能会导致空间的浪费。
- 优点:不需要动态分配空间,速度快。
- 缺点:使用不当可能会导致空间的浪费。
- varchar和char我们应该怎么选择?
- 性别字段你选什么?因为性别是固定长度的字符串,所以选择char。
- 姓名字段你选什么?每一个人的名字长度不同,所以选择varchar。
- int(最长11):数字中的整数型。等同于java的int。
- bigint:数字中的长整型。等同于java中的long。
- float :单精度浮点型数据
- double:双精度浮点型数据
- date:短日期类型
- datetime:长日期类型
- clob
- 字符大对象
- 最多可以存储4G的字符串。
- 比如:存储一篇文章,存储一个说明。
- 超过255个字符的都要采用CLOB字符大对象来存储。
- Character Large OBject:CLOB
- blob
- 二进制大对象
- Binary Large OBject
- 专门用来存储图片、声音、视频等流媒体数据。
- 往BLOB类型的字段上插入数据的时候,例如插入一个图片、视频等,
- 你需要使用IO流才行。
2、时间类型与字符串类型的转换:
mysql的日期格式:
%Y 年
%m 月
%d 日
%h 时
%i 分
%s 秒
mysql短日期默认格式:date:%Y-%m-%d
mysql长日期默认格式:dateTime:%Y-%m-%d %h:%i:%s
drop table if exists t_user;
create table t_user(
id int,
name varchar(32),
birth date,
create_time datetime
);
str_to_date('字符串日期', '日期格式')。将字符串varchar类型转换成date类型。
// insert插入日期
insert into t_user(id,name,birth) values(1, 'zhangsan', str_to_date('01-10-1990','%d-%m-%Y'));
// (自动数据类型转换)即自动将insert语句中的字符串转换为日期类型date
insert into t_user(id,name,birth) values(2, 'lisi', '1990-10-01');
date_format(日期类型数据, '日期格式'):将date类型转换成具有一定格式的varchar字符串类型。
// select日期类型的数据
select id,name,date_format(birth,'%Y/%m/%d') as birth from t_user;
// 以下,的SQL语句实际上是进行了默认的日期格式化,自动将数据库中的date类型转换成varchar类型(自动数据类型转换)。并且采用的格式是mysql默认的日期格式:'%Y-%m-%d'
select id,name,birth from t_user;
3、date和datetime两个类型的区别?
date是短日期:只包括年月日信息。
datetime是长日期:包括年月日时分秒信息。
mysql短日期默认格式:%Y-%m-%d
mysql长日期默认格式:%Y-%m-%d %h:%i:%s
drop table if exists t_user;
create table t_user(
id int,
name varchar(32),
birth date,
create_time datetime
);
insert into t_user(id,name,birth,create_time) values(1,'zhangsan','1990-10-01','2020-03-18 15:49:50');
// 在mysql当中怎么获取系统当前时间?now() 函数,并且获取的时间带有:时分秒信息!!!!是datetime类型的。
insert into t_user(id,name,birth,create_time) values(2,'lisi','1991-10-01',now());
七、具体3:DDL语句
1、create语句
创建一个学生表?
学号、姓名、年龄、性别、邮箱地址
create table t_student(
no int, // int这里写也得不写也得,写也就是给个建议长度,超过也没问题
name varchar(32),
sex char(1) default 'm',
age int(3),
email varchar(255)
);
// 快速建表 表创建出来,同时表中的数据也存在了!!!
create table emp2 as select * from emp;
create table mytable as select empno,ename from emp where job = 'MANAGER';
2、drop语句:
drop table t_student; // 当这张表不存在的时候会报错!
drop table if exists t_student;// 如果这张表存在的话,删除
3、alert(建议使用工具!!!)
添加一个字段,删除一个字段,修改一个字段!!!
八、具体4:DML语句
1、insert
insert into 表名(字段名1,字段名2,字段名3…) values(值1,值2,值3);
注意:字段名和值要一一对应。什么是一一对应?数量要对应。数据类型要对应。
注意:没有给其它字段指定值的话,默认值是NULL。或者使用default,给出默认值。
注意:前面的字段名省略的话,等于都写上了!所以值也要都写上!
// 可以一次插入多条记录。
insert into t_user(id,name,birth,create_time) values
(1,'zs','1980-10-11',now()),
(2,'lisi','1981-10-11',now()),
(3,'wangwu','1982-10-11',now());
// 快速插入数据
insert into dept_bak select * from dept; //很少用!
2、update
update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3… where 条件;
注意:没有条件限制会导致所有数据全部更新。
3、delete
delete from 表名 where 条件;
注意:没有条件,整张表的数据会全部删除!
4、truncate
// 快速删除表中的数据
delete from dept_bak; //这种删除数据的方式比较慢。可恢复(回滚)。
truncate table dept_bak;// 快、不可恢复(回滚)。
九、具体5:性能与优化
- 连接查询时关联的表越多,匹配次数越多,查询效率越低。
优化策略:首先,减少关联的表的数量。其次,通过条件减少表连接记录之间匹配的次数,例如,可以通过where条件对某一张表的数据进行过滤再和另一张表进行连接。 - select * from tablename:这种方式的缺点:(1)效率低(底层会把*号转换为表的全部字段);(2)可读性差。在实际开发中不建议,可以自己玩没问题。
优化策略:可以把每个字段都写上,如select a,b,c,d,e,f… from tablename;
-
案例1:找出每个部门最高薪资,要求显示最高薪资大于3000的?
第一步:找出每个部门最高薪资。思路:按照部门编号分组,求每一组最大值:
select deptno,max(sal) from emp group by deptno;
第二步:要求显示最高薪资大于3000:
select deptno,max(sal) from emp group by deptno having max(sal) > 3000;思考:提出问题,以上的sql语句执行效率是不是低?比较低。
思路:实际上可以这样考虑:先将大于3000的都找出来,然后再分组。
高效:select deptno,max(sal) from emp where sal > 3000 group by deptno;
优化策略:能先用where过滤掉的,就先使用where过滤掉。where和having,优先选择where。where实在完成不了,再进行分组并使用having。
-
select ename,dname from emp, dept where emp.deptno = dept.deptno;
效率很低,因为select ename,dname中的ename,dname都有可能会去emp, dept中去查找。
优化策略:我们可以指明ename,dname分别到哪张表中去查找:
select emp.ename,dept.dname from emp, dept where emp.deptno = dept.deptno; -
案例:查询工作岗位是MANAGER和SALESMAN的员工?
方案1:select ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
方案2:select ename,job from emp where job in('MANAGER','SALESMAN');
方案3:select ename,job from emp where job = 'MANAGER'
union
select ename,job from emp where job = 'SALESMAN';
结论:union效率要高一些。
优化策略:union的效率要高一些。对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻。而union可以做到减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。使用union的话是:100次 + 100次 = 200次,即union把乘法变成了加法运算。 -
delete语句删除数据的原理?(delete属于DML语句!!!)
表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放!!!
这种删除缺点是:删除效率比较低。
这种删除优点是:支持回滚,后悔了可以再恢复数据!!!
truncate语句删除数据的原理?
这种删除效率比较高,表被一次截断,物理删除。
这种删除缺点:不支持回滚。
这种删除优点:快速。
大表非常大,上亿条记录????
删除的时候,使用delete,也许需要执行1个小时才能删除完!效率较低。
可以选择使用truncate删除表中的数据。只需要不到1秒钟的时间就删除结束。效率较高。
十、具体6:事务
1 生活问题
正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句搞定,那么事务则没有存在的价值了。
2解决方案
(1)思想
-
一个事务其实就是一个完整的业务逻辑。
(2)流原:事务是怎么做到多条DML语句同时成功和同时失败的呢?
-
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件。
-
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。
-
事务开启
-
提交事务?
-
清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。
-
提交事务标志着,事务的结束。并且是一种全部成功的结束。
-
-
回滚事务?(roolbak:回滚永远都是只能回滚到上一次的提交点!)
-
清空事务性活动的日志文件,并且将之前所有的DML操作全部撤销
-
回滚事务标志着,事务的结束。并且是一种全部失败的结束。
-
(3)mysql事务常用设置
- 只有DML语句才会有事务这一说,其它语句和事务无关!!!
- mysql默认情况下是支持自动提交事务的。(自动提交)。
什么是自动提交?每执行一条DML语句,则提交一次!这种自动提交实际上是不符合我们的开发习惯,因为一个业务
通常是需要多条DML语句共同执行才能完成的,为了保证数据
的安全,必须要求同时成功之后再提交,所以不能执行一条
就提交一条。
-
怎么将mysql的自动提交机制关闭掉呢?先执行这个命令:start transaction;
(4)事务包括4个特性:ACID
A:原子性
不可再分。
C:一致性
在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证前后数据的一致性。
I:隔离性
- 教室A和教室B之间有一道墙,这道墙就是隔离性。
- 两个事务同时去操作一张表:A事务在操作一张表的时候,另一个事务B也操作这张表会那样???这就是隔离性。
- 相当于多线程并发访问同一张表一样,此时就会有多线程带来的线程安全问题。为了解决事务之间的线程安全问题,就要使用事务的隔离性。
-
- D:持久性
将数据持久化到硬盘上的数据库中。
(5)重点研究一下事务的隔离性!!!
-
事务的隔离级别:A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄。这道墙越厚,表示隔离级别就越高。
-
分类:
-
读未提交(最低级别):没有提交就读到了
-
原因:事务A可以读取到事务B未提交的数据。
-
问题:脏数据,脏读现象
-
-
读已提交:提交之后才能读到。数据绝对真实,oracle默认。
-
原因:事务A只能读取到事务B提交之后的数据。
-
解决:读未提交
-
问题:不可重复读
什么是不可重复读取数据呢?在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。
-
-
可重复读:即使提交之后也读不到,永远读取的都是事务A刚开启事务时的数据。数据不够真实,出现幻影读。mysql默认。
-
序列化/串行化(最高级别):
-
原因:事务排队,需要等待其他事务的提交,不能并发!有类似于java中的synchronized,线程同步(事务同步)。
-
解决:解决了以上所有问题(读未提交、读已提交、可重复读)
-
问题:每一次读取到的数据都是最真实的,并且效率是最低的。
-
-
十一、具体7:数据库对象:视图
1、生活问题:通过对视图的操作,会影响到原表数据
2、解决方案
(1)创建视图对象:
create view dept2_view as select * from dept2;
注意:只有DQL语句才能以view的形式创建。
create view view_name as 后面的语句必须是DQL语句;
(2)删除视图对象:
drop view dept2_view;
(3)用视图做什么?
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据。)
(4)视图对象在实际开发中到底有什么用?《方便,简化开发,利于维护》
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。每一次使用这个sql语句的时候都需要重新编写,很长,很麻烦,怎么办?可以把这条复杂的SQL语句以视图对象的形式新建。在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的SQL语句。
我们以后面向视图开发的时候,使用视图的时候可以像使用table一样。可以对视图进行增删改查等操作。视图不是在内存当中,视图对象也是存储在硬盘上的,不会消失。
十二、具体8:数据库对象:索引
1、生活中的问题
- 提高查询效率
2、解决方案
(1)思想
-
索引相当于一本书的目录(多级)
(2)流原
-
索引是在数据库表的字段上添加的。一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
-
MySQL在查询方面主要就是两种方式:
-
第一种方式:全表扫描
-
第二种方式:根据索引检索。
-
-
案例:select * from t_user where name = 'jack';
-
以上的这条SQL语句会去name字段上扫描,为什么?因为查询条件是:name='jack'。
-
全表(字段)扫描:如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描,即会将name字段上的每一个值都比对一遍。效率比较低。
-
索引扫描:索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。先通过目录(索引)去定位一个大概的位置,然后直接定位到这个位置,做局域性扫描,缩小扫描的范围,快速的查找。这种查找方式属于通过索引检索,效率较高。
-
-
-
索引排序:
在实际中,汉语字典前面的目录是排序的,按照a b c d e f….排序,为什么排序呢?因为只有排序了才会有区间查找这一说!(缩小扫描范围其实就是扫描某个区间罢了!)
在mysql数据库当中索引也是需要排序的,并且这个所以的排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。
遵循左小又大原则存放。采用中序遍历方式遍历取数据。 -
索引实现原理
- 第一步:根据左小右大的原则,把索引字段排序到一棵B-Tree上。
- 第二步:同时把当前索引字段所在的记录的物理存储编号放在后面,可以理解成是一个map(key=索引字段值,value=当前索引字段所在的记录的物理存储编号)。
- 第三步:select * from t_user where id=101;
- 第四步:mysql发现id字段上有索引对象,所以会通过索引对象idIndex进行查找。
- 第五步:mysql从头节点开始查询,通过左小右大的排序规则在B-Tree树上查找。顺序是:100 > 120 >101,即定位到了101。
- 第六步:通过101得出物理编号:0x666。
- 第七步:此时马上把sql语句转换为select * from t_user where 物理编号 = 0x666,直接从物理硬盘上拿记录,并返回查询结果。
(3)索引的使用
1)索引分类
- 单一索引:一个字段上添加索引。
- 主键索引:主键上添加索引。
-
唯一性索引:具有unique约束的字段上添加索引。
- 复合索引:两个字段或者更多的字段上添加索引。
- 复合主键索引
- 复合唯一性索引
注意:唯一性比较弱的字段上添加索引用处不大。
2)什么条件下,我们会考虑给字段添加索引呢?
- 条件1:数据量庞大
- 条件2:这个字段总是被扫描,即该字段经常出现在where的后面,以条件的形式存在。
- 条件3:该字段很少变动,该字段很少的DML(insert delete update)操作。(因为DML之后,索引需要重新排序。)
- 建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。
- 建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。
3)索引的创建与删除
创建索引:
mysql> create index emp_ename_index on emp(ename);
给emp表的ename字段添加索引,起名:emp_ename_index
删除索引:
mysql> drop index emp_ename_index on emp;
将emp表上的emp_ename_index索引对象删除。
4)在mysql当中,怎么查看一个SQL语句是否使用了索引进行检索?
5)索引失效
-
失效的第1种情况:like + %
-
失效的第2种情况:or
-
失效的第4种情况:索引列参加了运算
-
失效的第5种情况:索引列使用了函数
-
失效的第3种情况:复合索引
什么是复合索引?
两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
create index emp_job_sal_index on emp(job,sal);
-
失效的第6…
-
失效的第7…
6)问题
十三、具体9:数据库存储引擎(了解)
1 生活的思想
2 解决方案
(1)思想
-
实际上存储引擎是一个表存储/组织数据的方式。
-
不同的存储引擎,表存储数据的方式不同。
-
mysql数据库中,每张表都可以指定存储引擎。表的存储引擎不一样,即表存储数据的方式不一样。
(2)怎么表的存储引擎
show create table t_student;
(3)怎么给表指定“存储引擎”呢?
建表时指定存储引擎,以及字符编码方式。
在建表的时候可以在最后小括号的")"的右边使用:
ENGINE来指定存储引擎。
CHARSET来指定这张表的字符编码方式。
结论:
mysql默认的存储引擎是:InnoDB
mysql默认的字符编码方式是:utf8
(4)怎么查看mysql支持哪些存储引擎呢?
数据库版本:select version();
当前版本支持的存储引擎: show engines \G
mysql支持九大存储引擎,当前mysql 5.5.36支持8个。版本不同支持情况不同。
(5)关于mysql常用的存储引擎介绍一下(了解)
1)MyISAM存储引擎的特点?如何存储数据?
它管理的表具有以下特征:
- 使用三个文件表示每个表:
- 格式文件 — 存储表结构的定义(mytable.frm)
- 数据文件 — 存储表行的内容(mytable.MYD)
- 索引文件 — 存储表上索引(mytable.MYI):索引是一本书的目录,缩小扫描范围,提高查询效率的一种机制。
- 可被转换为压缩、只读表来节省空间
MyISAM存储引擎的优点:
可被转换为压缩、只读表来节省空间
这是这种存储引擎的优势!!!!
MyISAM存储引擎的缺点:
不支持事务机制,安全性低
2)InnoDB存储引擎的特点?如何存储数据?
这是mysql默认的存储引擎,同时也是一个重量级的存储引擎。
InnoDB支持事务,支持数据库崩溃后自动恢复机制。
InnoDB存储引擎最主要的特点是:非常安全。
它管理的表具有下列主要特征:
每个 InnoDB 表在数据库目录中以.frm 格式文件表示
InnoDB 表空间 tablespace 被用于存储表的内容(表空间是一个逻辑名称。表空间存储数据+索引。)
提供一组用来记录事务性活动的日志文件
用 COMMIT(提交)、SAVEPOINT 及ROLLBACK(回滚)支持事务处理
提供全 ACID 兼容
在 MySQL 服务器崩溃后提供自动恢复
多版本(MVCC)和行级锁定
支持外键及引用的完整性,包括级联删除和更新
InnoDB最大的特点就是支持事务:
以保证数据的安全。效率不是很高,并且也不能压缩,不能转换为只读,不能很好的节省存储空间。
3)MEMORY存储引擎的特点?如何存储数据?
使用 MEMORY 存储引擎的表,其数据存储在内存中,且行的长度固定,
这两个特点使得 MEMORY 存储引擎非常快。
MEMORY 存储引擎管理的表具有下列特征:
– 在数据库目录内,每个表均以.frm 格式的文件表示。
– 表数据及索引被存储在内存中。(目的就是快,查询快!)
– 表级锁机制。
– 不能包含 TEXT 或 BLOB 字段。
MEMORY 存储引擎以前被称为HEAP 引擎。
MEMORY引擎优点:查询效率是最高的。不需要和硬盘交互。
MEMORY引擎缺点:不安全,关机之后数据消失。因为数据和索引都是在内存当中。
十四、约束
1、生活需求
在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性!!!约束的作用就是为了保证:表中的数据有效!!
2、解决方案
- 非空约束:not null
- 唯一性约束: unique
- 主键约束: primary key (简称PK)
-
自然主键:主键值是一个自然数,和业务没关系。
-
业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。这就是业务主键!
-
在实际开发中使用业务主键多,还是使用自然主键多一些?
自然主键使用比较多,因为主键只要做到不重复就行,不需要有意义。
业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,
可能会影响到主键值,所以业务主键不建议使用。尽量使用自然主键。
-
- 外键约束:foreign key(简称FK):
- 为了保证cno字段中的值都是100和101
-
思考:子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?
不一定是主键,但至少具有unique约束。
-
测试:外键可以为NULL吗?
外键值可以为NULL。
- 检查约束:check(mysql不支持,oracle支持)
- 列组约束
- 表级约束
3、典型案例
// 1、新需求:name和email两个字段联合起来具有唯一性!!!!
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255),
email varchar(255),
unique(name,email) // 约束没有添加在列的后面,这种约束被称为表级约束。
);
// 2、unique 和not null可以联合吗?
drop table if exists t_vip;
create table t_vip(
id int,
name varchar(255) not null unique
在mysql当中,如果一个字段同时被not null和unique约束的话,该字段自动变成主键字段。(注意:oracle中不一样!)
);
// 3、联合(复合)主键
create table t_vip(
id int,
name varchar(255),
email varchar(255),
primary key(id,name)
在实际开发中不建议使用:复合主键。建议使用单一主键!
);
十五 数据库三大范式
1 生活问题
设计表时有什么依据?三大范式。
设计数据库表的时候,按照三大范式进行设计,可以避免表中数据的冗余,空间的浪费。
2 解决方案
(1)思想
什么是数据库设计范式?
数据库表的设计依据。教你怎么进行数据库表的设计。
(2)第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
学生编号 学生姓名 联系方式
————————————————————————————
1001 张三 zs@gmail.com, 1359999999
1002 李四 ls@gmail.com,13699999999
1001 王五 ww@163.net,13488888888
以上是学生表,满足第一范式吗?
不满足。联系方式可以分为邮箱地址和电话
(3)第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
学生编号+教师编号(pk) 学生姓名 教师姓名
—————————————————————————————-
1001 001 张三 王老师
1002 002 李四 赵老师
1003 001 王五 王老师
1001 002 张三 赵老师
满足第二范式吗?
不满足,“张三”依赖1001,“王老师”依赖001,显然产生了部分依赖。
产生部分依赖有什么缺点?
数据冗余了。空间浪费了。“张三”重复了,“王老师”重复了。
背口诀:多对多怎么设计?
多对多,三张表,关系表两个外键!!!!!!!!!!!!!!!
(4)第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
学生编号(PK) 学生姓名 班级编号 班级名称
————————————————————————————————–
1001 张三 01 一年一班
1002 李四 02 一年二班
1003 王五 03 一年三班
1004 赵六 03 一年三班
分析以上表是否满足第三范式?
不满足。“一年一班” 依赖 “01”(非PK),“01” 依赖 “1001”(PK),产生了传递依赖。
背口诀:
一对多,两张表,多的表加外键!!!!!!!!!!!!
(4)总结表的设计?
- 一对多:口诀:一对多,两张表,多的表加外键!!!!!!!!!!!!
- 多对多:口诀:多对多,三张表,关系表两个外键!!!!!!!!!!!!!!!
- 一对一:口诀:一对一,外键唯一!!!!!!!!!!
一对一放到一张表中不就行了吗?为啥还要拆分表?
在实际的开发中,可能存在一张表字段太多,太庞大。这个时候要拆分表。
一对一怎么设计?没有拆分表之前:一张表。
拆分表,口诀:一对一,外键唯一!!!!!!!!!!
(5)嘱咐一句话:以满足客户需求为准
数据库设计三范式是理论上的。
实践和理论有的时候有偏差。
最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度。
因为在sql当中,表和表之间连接次数越多,效率越低。(笛卡尔积)
有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,
并且对于开发人员来说,sql语句的编写难度也会降低。
面试的时候把这句话说上:他就不会认为你是初级程序员了!
十五 DBA常用命令?
- 重点掌握:数据的导入和导出(数据的备份)
- 其它命令了解一下即可。(这个培训日志文档留着,以后忘了,可以打开文档复制粘贴。
- 数据导出?
注意:在windows的dos命令窗口中:mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p123456 - 可以导出指定的表吗?
mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p123456 - 数据导入?
注意:需要先登录到mysql数据库服务器上。
然后创建数据库:create database bjpowernode;
使用数据库:use bjpowernode
然后初始化数据库:source D:\bjpowernode.sql
十六 特殊需求
1 两个子查询结果相除
- https://zhidao.baidu.com/question/226428906.html
- 方式1:select语句中两条子查询结果相除。
- 方式2:首先,两个子查询join。其次,select语句中分别得到两个子查询的字段并相除。
- https://blog.csdn.net/being_towards_death/article/details/84527260
- 方式2。用函数处理结果:CAST、ROUND、COALESCE。
- https://easylearn.baidu.com/edu-page/tiangong/questiondetail?id=1711656412511576434&fr=search
- 方式1。备注:查询公共表:from dual
- https://wenku.baidu.com/view/2706526db007e87101f69e3143323968011cf401.html?_wkts_=1671077015204&bdQuery=select%E8%AF%AD%E5%8F%A5%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F
- sql语句的执行顺序
- https://cloud.tencent.com/developer/ask/sof/209391
- 方式1。方式2。条件聚合。
- https://huaweicloud.csdn.net/63357061d3efff3090b57041.html
- 方式1。as xxxxxx。
- https://www.nuomiphp.com/a/stackoverflow/zh/622ab5d1fb0ae3077501cd5f.html
- 方式1。
- https://blog.csdn.net/weixin_43865008/article/details/116263675
- 方式1。方式2。