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
from:从某张表中查询数据,
where:先经过where条件筛选出有价值的数据。
group by:对这些有价值的数据进行分组。
having:分组之后可以使用having继续筛选。
select:select查询出来。
order by:最后排序输出!
(1)序言:
(2)group by子句:
(3)having子句:
使用having可以对分完组之后的数据进一步过滤。
having不能单独使用,having必须和group by联合使用。
having不能代替where。
第一步:找出每个部门最高薪资。思路:按照部门编号分组,求每一组最大值:
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;
序言
什么是连接查询?
从一张表中单独查询,称为单表查询。
emp表和dept表联合起来查询数据(如:从emp表中取员工名字,从dept表中取部门名字),这种跨表查询(即多张表联合起来查询数据)被称为连接查询。
知识点
怎么避免笛卡尔积现象?连接时加连接条件,满足这个条件的两条记录被筛选出来,进行配对,连成一条长长的记录。
SQL92语法:select e.ename,d.dname from emp e, dept d where e.deptno = d.deptno;
思考:以上查询语句,最终查询的结果条数是14条,但是匹配的过程中,匹配的次数减少了吗?还是56次,只不过进行了四选一。因此,匹配次数并没有减少。
注意:通过笛卡尔积现象得出,表的连接次数越多效率越低,尽量避免表的连接次数。
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 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的连接条件
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;
序言
什么是子查询?select语句中嵌套select语句,被嵌套的select语句称为子查询。
子查询都可以出现在哪里呢?
select ..(select).
from ..(select).
where ..(select).
知识点
流程:先执行子查询,再执行主查询。
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条,就报错了。!
limit作用:将查询结果集的一部分取出来。通常使用在分页查询当中。
完整用法:limit startIndex, length。
startIndex是起始下标,length是长度。
起始下标从0开始。
缺省用法:limit 5; 这是取前5.
注意:mysql当中limit在order by之后执行!!!!!!
记公式:limit (pageNo-1)*pageSize , pageSize
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中要求:结果集合并时列和列的数据类型也要一致。
1、很多数据类型,我们只需要掌握一些常见的数据类型即可。
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());
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(建议使用工具!!!)
添加一个字段,删除一个字段,修改一个字段!!!
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;// 快、不可恢复(回滚)。
优化策略:可以把每个字段都写上,如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秒钟的时间就删除结束。效率较高。
正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句搞定,那么事务则没有存在的价值了。
一个事务其实就是一个完整的业务逻辑。
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件。
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。
事务开启
提交事务?
清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。
提交事务标志着,事务的结束。并且是一种全部成功的结束。
回滚事务?(roolbak:回滚永远都是只能回滚到上一次的提交点!)
清空事务性活动的日志文件,并且将之前所有的DML操作全部撤销
回滚事务标志着,事务的结束。并且是一种全部失败的结束。
这种自动提交实际上是不符合我们的开发习惯,因为一个业务
通常是需要多条DML语句共同执行才能完成的,为了保证数据
的安全,必须要求同时成功之后再提交,所以不能执行一条
就提交一条。
怎么将mysql的自动提交机制关闭掉呢?先执行这个命令:start transaction;
A:原子性
不可再分。
C:一致性
在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证前后数据的一致性。
I:隔离性
将数据持久化到硬盘上的数据库中。
事务的隔离级别:A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄。这道墙越厚,表示隔离级别就越高。
分类:
读未提交(最低级别):没有提交就读到了
原因:事务A可以读取到事务B未提交的数据。
问题:脏数据,脏读现象
读已提交:提交之后才能读到。数据绝对真实,oracle默认。
原因:事务A只能读取到事务B提交之后的数据。
解决:读未提交
问题:不可重复读
什么是不可重复读取数据呢?在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。
可重复读:即使提交之后也读不到,永远读取的都是事务A刚开启事务时的数据。数据不够真实,出现幻影读。mysql默认。
原因:事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。
解决:不可重复读
问题:可以会出现幻影读。每一次读取到的数据都是幻象。不够真实!
案例:以下需求应该使用什么样的隔离级别?可重复读即可。
下午1点开始开启了事务,只要事务不结束,到下午3点,读到的数据还是那样!
原理:就是做了数据的一个备份,或者说对数据进行了一个快照。
序列化/串行化(最高级别):
原因:事务排队,需要等待其他事务的提交,不能并发!有类似于java中的synchronized,线程同步(事务同步)。
解决:解决了以上所有问题(读未提交、读已提交、可重复读)
问题:每一次读取到的数据都是最真实的,并且效率是最低的。
create view dept2_view as select * from dept2;
注意:只有DQL语句才能以view的形式创建。
create view view_name as 后面的语句必须是DQL语句;
drop view dept2_view;
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据。)
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。每一次使用这个sql语句的时候都需要重新编写,很长,很麻烦,怎么办?可以把这条复杂的SQL语句以视图对象的形式新建。在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的SQL语句。
我们以后面向视图开发的时候,使用视图的时候可以像使用table一样。可以对视图进行增删改查等操作。视图不是在内存当中,视图对象也是存储在硬盘上的,不会消失。
索引相当于一本书的目录(多级)
索引是在数据库表的字段上添加的。一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
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数据结构。
遵循左小又大原则存放。采用中序遍历方式遍历取数据。
索引实现原理
1)索引分类
唯一性索引:具有unique约束的字段上添加索引。
注意:唯一性比较弱的字段上添加索引用处不大。
2)什么条件下,我们会考虑给字段添加索引呢?
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)问题
实际上存储引擎是一个表存储/组织数据的方式。
不同的存储引擎,表存储数据的方式不同。
mysql数据库中,每张表都可以指定存储引擎。表的存储引擎不一样,即表存储数据的方式不一样。
show create table t_student;
建表时指定存储引擎,以及字符编码方式。
在建表的时候可以在最后小括号的")"的右边使用:
ENGINE来指定存储引擎。
CHARSET来指定这张表的字符编码方式。
结论:
mysql默认的存储引擎是:InnoDB
mysql默认的字符编码方式是:utf8
数据库版本:select version();
当前版本支持的存储引擎: show engines \G
mysql支持九大存储引擎,当前mysql 5.5.36支持8个。版本不同支持情况不同。
1)MyISAM存储引擎的特点?如何存储数据?
它管理的表具有以下特征:
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引擎缺点:不安全,关机之后数据消失。因为数据和索引都是在内存当中。
在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性!!!约束的作用就是为了保证:表中的数据有效!!
自然主键:主键值是一个自然数,和业务没关系。
业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。这就是业务主键!
在实际开发中使用业务主键多,还是使用自然主键多一些?
自然主键使用比较多,因为主键只要做到不重复就行,不需要有意义。
业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,
可能会影响到主键值,所以业务主键不建议使用。尽量使用自然主键。
思考:子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?
不一定是主键,但至少具有unique约束。
测试:外键可以为NULL吗?
外键值可以为NULL。
// 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)
在实际开发中不建议使用:复合主键。建议使用单一主键!
);
设计表时有什么依据?三大范式。
设计数据库表的时候,按照三大范式进行设计,可以避免表中数据的冗余,空间的浪费。
什么是数据库设计范式?
数据库表的设计依据。教你怎么进行数据库表的设计。
学生编号 学生姓名 联系方式
------------------------------------------------------------------------------------
1001 张三 [email protected], 1359999999
1002 李四 [email protected],13699999999
1001 王五 [email protected],13488888888
以上是学生表,满足第一范式吗?
不满足。联系方式可以分为邮箱地址和电话
学生编号+教师编号(pk) 学生姓名 教师姓名
----------------------------------------------------------------------------------------
1001 001 张三 王老师
1002 002 李四 赵老师
1003 001 王五 王老师
1001 002 张三 赵老师
满足第二范式吗?
不满足,“张三”依赖1001,“王老师”依赖001,显然产生了部分依赖。
产生部分依赖有什么缺点?
数据冗余了。空间浪费了。“张三”重复了,“王老师”重复了。
背口诀:多对多怎么设计?
多对多,三张表,关系表两个外键!!!!!!!!!!!!!!!
学生编号(PK) 学生姓名 班级编号 班级名称
--------------------------------------------------------------------------------------------------
1001 张三 01 一年一班
1002 李四 02 一年二班
1003 王五 03 一年三班
1004 赵六 03 一年三班
分析以上表是否满足第三范式?
不满足。“一年一班” 依赖 “01”(非PK),“01” 依赖 “1001”(PK),产生了传递依赖。
背口诀:
一对多,两张表,多的表加外键!!!!!!!!!!!!
数据库设计三范式是理论上的。
实践和理论有的时候有偏差。
最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度。
因为在sql当中,表和表之间连接次数越多,效率越低。(笛卡尔积)
有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,
并且对于开发人员来说,sql语句的编写难度也会降低。
面试的时候把这句话说上:他就不会认为你是初级程序员了!