10子查询
子查询就是嵌套的select语句,可以理解为子查询是一张表
10.1 在where语句中使用子查询,也就是在where语句中加入select语句
- 查询员工信息,查询哪些人是管理者,要求显示出其员工编号和员工姓名
实现思路:
1、首先取得管理者的编号,去除重复的
select distinct mgr from emp where mgr is not null; distinct 去除重复行 |
2、查询员工编号包含管理者编号的
select empno, ename from emp where empno in(select mgr from emp where mgr is not null); |
- 查询哪些人的薪水高于员工的平均薪水,需要显示员工编号,员工姓名,薪水
实现思路
- 取得平均薪水
select avg(sal) from emp; |
- 取得大于平均薪水的员工
select empno, ename, sal from emp where sal > (select avg(sal) from emp); |
10.2 在from语句中使用子查询,可以将该子查询看做一张表
- 查询员工信息,查询哪些人是管理者,要求显示出其员工编号和员工姓名
首先取得管理者的编号,去除重复的
select distinct mgr from emp where mgr is not null; |
将以上查询作为一张表,放到from语句的后面
使用92语法: select e.empno, e.ename from emp e, (select distinct mgr from emp where mgr is not null) m where e.empno=m.mgr; 使用99语法: select e.empno, e.ename from emp e join (select distinct mgr from emp where mgr is not null) m on e.empno=m.mgr; |
- 查询各个部门的平均薪水所属等级,需要显示部门编号,平均薪水,等级编号
实现思路
1、首先取得各个部门的平均薪水
select deptno, avg(sal) avg_sal from emp group by deptno; |
select deptno,avg(sal) avg_sal from emp group by deptno; |
select * from salgrade; |
select a.deptno,a.avg_sal,g.grade from (select deptno,avg(sal) avg_sal from emp group by deptno ) a join salgrade g on a.avg_sal between g.losal and hisal; |
10.3 在select语句中使用子查询
select e.ename, (select d.dname from dept d where e.deptno=d.deptno) as dname from emp e;
11.union
11.1 union可以合并集合(相加)
1、查询job包含MANAGER和包含SALESMAN的员工
select * from emp where job in('MANAGER', 'SALESMAN'); |
2、采用union来合并
select * from emp where job='MANAGER' union select * from emp where job='SALESMAN' |
合并结果集的时候,需要查询字段对应个数相同。在Oracle中更严格,不但要求个数相同,而且还要求类型对应相同。
12.limit的使用
mySql提供了limit ,主要用于提取前几条或者中间某几行数据
select * from table limit m,n
其中m是指记录开始的index,从0开始,表示第一条记录
n是指从第m+1条开始,取n条。
select * from tablename limit 2,4
即取出第3条至第6条,4条记录
12.1 取得前5条数据
select * from emp limit 5; |
12.2 从第二条开始取两条数据
select * from emp limit 1,2; |
12.3 取得薪水最高的前5名
select * from emp e order by e.sal desc limit 5; |
13.表
13.1 创建表
- 语法格式
创建表的时候,表中有字段,每一个字段有: * 字段名 * 字段数据类型 * 字段长度限制 * 字段约束 |
- MySql常用数据类型
类型 | 描述 |
Char(长度) | 定长字符串,存储空间大小固定,适合作为主键或外键 |
Varchar(长度) | 变长字符串,存储空间等于实际数据空间 |
double(有效数字位数,小数位) | 数值型 |
Float(有效数字位数,小数位) | 数值型 |
Int( 长度) | 整型 |
bigint(长度) | 长整型 |
Date | 日期型年月日 |
DateTime | 日期型年月日时分秒毫秒 |
time | 日期型时分秒 |
BLOB | Binary Large OBject(二进制大对象) |
CLOB | Character Large OBject(字符大对象) |
其它………………… |
- 建立学生信息表,字段包括:学号、姓名、性别、出生日期、email、班级标识
|
- 向t_student表中加入数据,(必须使用客户端软件,我们的cmd默认是GBK编码,数据中设置的编码是UTF-8)
insert into t_student(student_id, student_name, sex, birthday, email, classes_id) values(1001, 'zhangsan', 'm', '1988-01-01', 'qqq@163.com', 10) |
- 向t_student表中加入数据(使用默认值)
|
13.2 增加/删除/修改表
采用alter table来增加/删除/修改表结构,不影响表中的数据
13.2.1、添加字段
如:需求发生改变,需要向t_student中加入联系电话字段,字段名称为:contatct_tel 类型为varchar(40)
alter table t_student add contact_tel varchar(40); |
13.2.2、修改字段
如:student_name无法满足需求,长度需要更改为100
alter table t_student modify student_name varchar(100) ; |
如sex字段名称感觉不好,想用gender那么就需要更爱列的名称
13.2.3、删除字段
如:删除联系电话字段
alter table t_student drop contact_tel; |
13.3 添加,修改和删除
13.3.1、insert
添加、修改和删出都属于DML,主要包含的语句:insert、update、delete
- Insert语法格式
Insert into 表名(字段,。。。。) values(值,………..) |
- 省略字段的插入
insert into emp values(9999,'zhangsan','MANAGER', null, null,3000, 500, 10); |
不建议使用此种方式,因为当数据库表中的字段位置发生改变的时候会影响到insert语句
- 指定字段的插入(建议使用此种方式)
insert into emp (empno,ename,job,mgr,hiredate,sal,comm,deptno) values(9999,'zhangsan','MANAGER', null, null,3000, 500, 10); |
如何插入日期:
第一种方法,插入的日期格式和显示的日期格式一致
insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(9997,'zhangsan','MANAGER', null, '1981-06-12',3000, 500, 10); |
第二种方法,采用str_to_date
insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) valu es(9996,'zhangsan','MANAGER',null,str_to_date('1981-06-12','%Y-%m-%d'),3000, 500, 10); |
第三种方法,添加系统日期(now())
insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values(9995,'zhangsan','MANAGER',null,now() ,3000, 500, 10); |
- 表复制
create table emp_bak as select empno,ename,sal from emp; |
13.3.3、delete
可以删除数据,可以根据条件删除数据
- 语法格式:
Delete from表名 where 。。。。。 |
- 删除津贴为500的员工
delete from emp where comm=500; |
- 删除津贴为null的员工
delete from emp where comm is null; |
13.4 创建表加入约束
- 常见的约束
- 非空约束,not null
- 唯一约束,unique
- 主键约束,primary key
- 外键约束,foreign key
- 自定义检查约束,check(不建议使用)(在mysql中现在还不支持)
13.4.1、非空约束,not null
非空约束,针对某个字段设置其值不为空,如:学生的姓名不能为空
|
以上错误为加入的学生姓名为空。
13.4.2、唯一约束,unique
唯一性约束,它可以使某个字段的值不能重复,如:email不能重复:
|
以上插入了重复的email,所以出现了“违反唯一约束错误”,所以unique起作用了
同样可以为唯一约束起个约束名
13.4.3、主键约束,primary key
每个表应该具有主键,主键可以标识记录的唯一性,主键分为单一主键和复合(联合)主键,单一主键是由一个字段构成的,复合(联合)主键是由多个字段构成的
|
向以上表中加入学号为1001的两条记录,出现如下错误,因为加入了主键约束
我们也可以通过表级约束为约束起个名称:
|
13.4.4、外键约束,foreign key
外键主要是维护表之间的关系的,主要是为了保证参照完整性,如果表中的某个字段为外键字段,那么该字段的值必须来源于参照的表的主键,如:emp中的deptno值必须来源于dept表中的deptno字段值。
建立学生和班级表之间的连接
首先建立班级表t_classes
|
在t_student中加入外键约束
|
向t_student中加入数据
insert into t_student(student_id, student_name, sex, birthday, email, classes_id) values(1001, 'zhangsan', 'm', '1988-01-01', 'qqq@163.com', 10) |
出现错误,因为在班级表中不存在班级编号为10班级,外键约束起到了作用
存在外键的表就是子表,参照的表就是父表,所以存在一个父子关系,也就是主从关系,主表就是班级表,从表就是学生表
14. 存储引擎(了解)
14.1 存储引擎的使用
- 数据库中的各表均被(在创建表时)指定的存储引擎来处理。
- 服务器可用的引擎依赖于以下因素:
- MySQL的版本
- 服务器在开发时如何被配置
- 启动选项
- 为了解当前服务器中有哪些存储引擎可用,可使用SHOW ENGINES语句:
mysql> SHOW ENGINES\G
- 在创建表时,可使用ENGINE选项为CREATE TABLE语句显式指定存储引擎。
CREATE TABLE TABLENAME (NO INT) ENGINE = MyISAM;
- 如果在创建表时没有显式指定存储引擎,则该表使用当前默认的存储引擎
- 默认的存储引擎可在my.ini配置文件中使用default-storage-engine选项指定。
- 现有表的存储引擎可使用ALTER TABLE语句来改变:ALTER TABLE TABLENAME ENGINE = INNODB;
- 为确定某表所使用的存储引擎,可以使用SHOW CREATE TABLE或SHOW TABLE STATUS语句:
mysql> SHOW CREATE TABLE emp\G
mysql> SHOW TABLE STATUS LIKE 'emp' \G
14.2 常用的存储引擎
14.2.1、MyISAM存储引擎
- MyISAM存储引擎是MySQL最常用的引擎。
- 它管理的表具有以下特征:
- 使用三个文件表示每个表:
- 格式文件— 存储表结构的定义(mytable.frm)
- 数据文件— 存储表行的内容(mytable.MYD)
- 索引文件— 存储表上索引(mytable.MYI)
- 灵活的AUTO_INCREMENT字段处理
- 可被转换为压缩、只读表来节省空间
14.2.2、InnoDB存储引擎
- InnoDB存储引擎是MySQL的缺省引擎。
- 它管理的表具有下列主要特征:
- 每个InnoDB表在数据库目录中以.frm格式文件表示
- InnoDB表空间tablespace被用于存储表的内容
- 提供一组用来记录事务性活动的日志文件
- 用COMMIT(提交)、SAVEPOINT及ROLLBACK(回滚)支持事务处理
- 提供全ACID兼容
- 在MySQL服务器崩溃后提供自动恢复
- 多版本(MVCC)和行级锁定
- 支持外键及引用的完整性,包括级联删除和更新
14.2.3、MEMORY存储引擎
- 使用MEMORY存储引擎的表,其数据存储在内存中,且行的长度固定,这两个特点使得MEMORY存储引擎非常快。
- MEMORY存储引擎管理的表具有下列特征:
- 在数据库目录内,每个表均以.frm格式的文件表示。
- 表数据及索引被存储在内存中。
- 表级锁机制。
- 不能包含TEXT或BLOB字段。
- MEMORY存储引擎以前被称为HEAP引擎。
14.3 选择合适的存储引擎
- MyISAM表最适合于大量的数据读而少量数据更新的混合操作。MyISAM表的另一种适用情形是使用压缩的只读表。
- 如果查询中包含较多的数据更新操作,应使用InnoDB。其行级锁机制和多版本的支持为数据读取和更新的混合操作提供了良好的并发机制。
- 可使用MEMORY存储引擎来存储非永久需要的数据,或者是能够从基于磁盘的表中重新生成的数据。
15.事务
15.1 概述
事务可以保证多个操作原子性,要么全成功,要么全失败。对于数据库来说事务保证批量的DML要么全成功,要么全失败。事务具有四个特征ACID
- 原子性(Atomicity)
- 整个事务中的所有操作,必须作为一个单元全部完成(或全部取消)。
- 一致性(Consistency)
- 在事务开始之前与结束之后,数据库都保持一致状态。
- 隔离性(Isolation)
- 一个事务不会影响其他事务的运行。
- 持久性(Durability)
- 在事务完成以后,该事务对数据库所作的更改将持久地保存在数据库之中,并不会被回滚。
事务中存在一些概念:
- 事务(Transaction):一批操作(一组DML)
- 开启事务(Start Transaction)
- 回滚事务(rollback)
- 提交事务(commit)
- SET AUTOCOMMIT:禁用或启用事务的自动提交模式
当执行DML语句是其实就是开启一个事务
关于事务的回滚需要注意:只能回滚insert、delete和update语句,不能回滚select(回滚select没有任何意义),对于create、drop、alter这些无法回滚.
事务只对DML有效果。
注意:rollback,或者commit后事务就结束了。
15.2 事务的提交与回滚演示
- 创建表
|
- 查询表中数据
- 开启事务START TRANSACTION;
- 插入数据
insert into user (username,password) values ('zhangsan','123');
- 查看数据
- 修改数据
- 查看数据
- 回滚事务
- 查看数据
15.3 自动提交模式
- 自动提交模式用于决定新事务如何及何时启动。
- 启用自动提交模式:
- 如果自动提交模式被启用,则单条DML语句将缺省地开始一个新的事务。
- 如果该语句执行成功,事务将自动提交,并永久地保存该语句的执行结果。
- 如果语句执行失败,事务将自动回滚,并取消该语句的结果。
- 在自动提交模式下,仍可使用START TRANSACTION语句来显式地启动事务。这时,一个事务仍可包含多条语句,直到这些语句被统一提交或回滚。
- 禁用自动提交模式:
- 如果禁用自动提交,事务可以跨越多条语句。
- 在这种情况下,事务可以用COMMIT和ROLLBACK语句来显式地提交或回滚。
- 自动提交模式可以通过服务器变量AUTOCOMMIT来控制。
- 例如:
mysql> SET AUTOCOMMIT = OFF;
mysql> SET AUTOCOMMIT = ON;
或
mysql> SET SESSION AUTOCOMMIT = OFF;
mysql> SET SESSION AUTOCOMMIT = ON;
show variables like '%auto%'; -- 查看变量状态
15.4 事务的隔离级别
15.4.1、隔离级别
- 事务的隔离级别决定了事务之间可见的级别。
- 当多个客户端并发地访问同一个表时,可能出现下面的一致性问题:
- 脏读取(Dirty Read)
一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交,这就出现了脏读取。
- 不可重复读(Non-repeatable Read)
在同一个事务中,同一个读操作对同一个数据的前后两次读取产生了不同的结果,这就是不可重复读。
- 幻像读(Phantom Read)
幻像读是指在同一个事务中以前没有的行,由于其他事务的提交而出现的新行。
15.4.2、四个隔离级别
- InnoDB 实现了四个隔离级别,用以控制事务所做的修改,并将修改通告至其它并发的事务:
- 读未提交(READ UMCOMMITTED)
允许一个事务可以看到其他事务未提交的修改。
- 读已提交(READ COMMITTED)
允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的。
- 可重复读(REPEATABLE READ)
确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果,不管其他事务是否提交这些修改。 (银行总账)
该隔离级别为InnoDB的缺省设置。
- 串行化(SERIALIZABLE) 【序列化】
将一个事务与其他事务完全地隔离。
例:A可以开启事物,B也可以开启事物
A在事物中执行DML语句时,未提交
B不以执行DML,DQL语句
15.4.3、隔离级别与一致性问题的关系
15.4.4、设置服务器缺省隔离级别
通过修改配置文件设置
- 可以在my.ini文件中使用transaction-isolation选项来设置服务器的缺省事务隔离级别。
- 该选项值可以是:
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERIALIZABLE
- 例如:
[mysqld]
transaction-isolation = READ-COMMITTED
通过命令动态设置隔离级别
- 隔离级别也可以在运行的服务器中动态设置,应使用SET TRANSACTION ISOLATION LEVEL语句。
- 其语法模式为:
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL <isolation-level>
其中的<isolation-level>可以是:
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
- 例如: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
15.4.5、隔离级别的作用范围
- 事务隔离级别的作用范围分为两种:
- 全局级:对所有的会话有效
- 会话级:只对当前的会话有效
- 例如,设置会话级隔离级别为READ COMMITTED :
mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
或:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
- 设置全局级隔离级别为READ COMMITTED :
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
15.4.6、查看隔离级别
- 服务器变量tx_isolation(包括会话级和全局级两个变量)中保存着当前的会话隔离级别。
- 为了查看当前隔离级别,可访问tx_isolation变量:
- 查看会话级的当前隔离级别:
mysql> SELECT @@tx_isolation;
或:
mysql> SELECT @@session.tx_isolation;
- 查看全局级的当前隔离级别:
mysql> SELECT @@global.tx_isolation;
15.4.7、并发事务与隔离级别示例
read uncommitted(未提交读) --脏读(Drity Read):
会话一 | 会话二 |
mysql> prompt s1> | mysql> use bjpowernode |
s1>use bjpowernode | mysql> prompt s2> |
s1>create table tx ( id int(11), num int (10) ); | |
s1>set global transaction isolation level read uncommitted; | |
s1>start transaction; | |
s2>start transaction; | |
s1>insert into tx values (1,10); | |
s2>select * from tx; | |
s1>rollback; | |
s2>select * from tx; |
read committed(已提交读)
会话一 | 会话二 |
s1> set global transaction isolation level read committed; | |
s1>start transaction; | |
s2>start transaction; | |
s1>insert into tx values (1,10); | |
s1>select * from tx; | |
s2>select * from tx; | |
s1>commit; | |
s2>select * from tx; |
repeatable read(可重复读)
会话一 | 会话二 |
s1> set global transaction isolation level repeatable read; | |
s1>start transaction; | s2>start transaction; |
s1>select * from tx; | |
s1>insert into tx values (1,10); | |
s2>select * from tx; | |
s1>commit; | |
s2>select * from tx; |
16.索引
16.1概述
什么是索引?有什么用?
索引就相当于一本书的目录,通过目录可以快速的找到对应的资源。
在数据库方面,查询一张表的时候有两种检索方式:
第一种方式:全表扫描
第二种方式:根据索引检索(效率很高)
索引为什么可以提高检索效率呢?
其实最根本的原理是缩小了扫描的范围。
索引虽然可以提高检索效率,但是不能随意的添加索引,因为索引也是数据库当中
的对象,也需要数据库不断的维护。是有维护成本的。比如,表中的数据经常被修改
这样就不适合添加索引,因为数据一旦修改,索引需要重新排序,进行维护。
添加索引是给某一个字段,或者说某些字段添加索引。
select ename,sal from emp where ename = 'SMITH';
当ename字段上没有添加索引的时候,以上sql语句会进行全表扫描,扫描ename字段中所有的值。
当ename字段上添加索引的时候,以上sql语句会根据索引扫描,快速定位。
16.2索引的实现原理
索引被用来快速找出在一个列上用一特定值的行。没有索引,MySQL不得不首先以第一条记录开始,然后读完整个表直到它找出相关的行。表越大,花费时间越多。对于一个有序字段,可以运用二分查找(Binary Search),这就是为什么性能能得到本质上的提高。MYISAM和INNODB都是用B+Tree作为索引结构
(主键,unique 都会默认的添加索引)
16.3 索引的创建和删除
创建索引对象:
create index 索引名称 on 表名(字段名);
删除索引对象:
drop index 索引名称 on 表名;
如果未使用索引,我们查询工资大于1500的会执行全表扫描
什么时候需要给字段添加索引:
-表中该字段中的数据量庞大
-经常被检索,经常出现在where子句中的字段
-经常被DML操作的字段不建议添加索引
索引等同于一本书的目录
主键会自动添加索引,所以尽量根据主键查询效率较高。
16.4 索引的失效
select ename from emp where ename like '%A%';
模糊查询的时候,第一个通配符使用的是%,这个时候索引是失效的。
16.5 索引的分类
单一索引:给单个字段添加索引
复合索引: 给多个字段联合起来添加1个索引
主键索引:主键上会自动添加索引
唯一索引:有unique约束的字段上会自动添加索引
....
17.视图
17.1 什么是视图
- 视图是一种根据查询(也就是SELECT表达式)定义的数据库对象,用于获取想要看到和使用的局部数据。
- 视图有时也被成为“虚拟表”。
- 视图可以被用来从常规表(称为“基表”)或其他视图中查询数据。
- 相对于从基表中直接获取数据,视图有以下好处:
- 访问数据变得简单
- 可被用来对不同用户显示不同的表的内容
用来协助适配表的结构以适应前端现有的应用程序
视图作用:
- 提高检索效率
- 隐藏表的实现细节【面向视图检索】
17.2 创建视图
注意:只有DQL语句才能以视图对象的方式创建出来。
如下示例:查询员工的姓名,部门,工资入职信息等信息。
select ename,dname,sal,hiredate,e.deptno from emp e,dept d where e.deptno = e.deptno and e.deptno = 10; |
为什么使用视图?因为需求决定以上语句需要在多个地方使用,如果频繁的拷贝以上代码,会给维护带来成本,视图可以解决这个问题
create view v_dept_emp as select ename,dname,sal,hiredate,e.deptno from emp e,dept d where e.deptno = e.deptno and e.deptno = 10; |
create view v_dept_avg_sal_grade as select a.deptno, a.avg_sal, b.grade from (select deptno, avg(sal) avg_sal from emp group by deptno) a, salgrade b where a.avg_sal between b.losal and b.hisal; /*注意mysql不支持子查询创建视图*/ |
17.3 修改视图
alter view v_dept_emp as select ename,dname,sal,hiredate,e.deptno from e mp e,dept d where e.deptno = 20; |
17.4 删除视图
drop view if exists v_dept_emp; |
18. DBA命令(了解)
18.1 新建用户
CREATE USER username IDENTIFIED BY 'password'; 说明:username——你将创建的用户名, password——该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器. 例如: create user p361 identified by '123'; --可以登录但是只可以看见一个库 information_schema |
18.2 授权
命令详解 mysql> grant all privileges on dbname.tbname to 'username'@'login ip' identified by 'password' with grant option;
首先以root用户进入mysql,然后键入命令:grant select,insert,update,delete on *.* to p361 @localhost Identified by "123"; 如果希望该用户能够在任何机器上登陆mysql,则将localhost改为 "%" 。
我们测试用户一般使用该命令授权, GRANT ALL PRIVILEGES ON *.* TO 'p361'@'%' Identified by "123"; 注意:用以上命令授权的用户不能给其它用户授权,如果想让该用户可以授权,用以下命令: GRANT ALL PRIVILEGES ON *.* TO 'p361'@'%' Identified by "123" WITH GRANT OPTION; privileges包括:
|
18.3 回收权限
命令详解 revoke privileges on dbname[.tbname] from username; revoke all privileges on *.* from p361; use mysql select * from user 进入mysql库中 修改密码; update user set password = password('qwe') where user = 'p646'; 刷新权限; flush privileges |
18.4 导出导入
18.4.1、导出
18.4.1.1、导出整个数据库
在windows的dos命令窗口中执行:mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p123
18.4.1.2、导出指定库下的指定表
在windows的dos命令窗口中执行:mysqldump bjpowernode emp> D:\ bjpowernode.sql -uroot –p123
18.4.2、导入
登录MYSQL数据库管理系统之后执行:source D:\ bjpowernode.sql
19.数据库设计的三范式
19.1第一范式
数据库表中不能出现重复记录,每个字段是原子性的不能再分
不符合第一范式的示例
学生编号 | 学生姓名 | 联系方式 |
1001 | 张三 | zs@gmail.com,1359999999 |
1002 | 李四 | ls@gmail.com,13699999999 |
1001 | 王五 | ww@163.net,13488888888 |
存在问题:
- 最后一条记录和第一条重复(不唯一,没有主键)
- 联系方式字段可以再分,不是原子性的
学生编号(pk) | 学生姓名 | 联系电话 | |
1001 | 张三 | zs@gmail.com | 1359999999 |
1002 | 李四 | ls@gmail.com | 13699999999 |
1003 | 王五 | ww@163.net | 13488888888 |
关于第一范式,每一行必须唯一,也就是每个表必须有主键,这是我们数据库设计的最基本要求,主要通常采用数值型或定长字符串表示,关于列不可再分,应该根据具体的情况来决定。如联系方式,为了开发上的便利行可能就采用一个字段了。
19.2 第二范式
第二范式是建立在第一范式基础上的,另外要求所有非主键字段完全依赖主键,不能产生部分依赖
示例:
学生编号 | 学生姓名 | 教师编号 | 教师姓名 |
1001 | 张三 | 001 | 王老师 |
1002 | 李四 | 002 | 赵老师 |
1003 | 王五 | 001 | 王老师 |
1001 | 张三 | 002 | 赵老师 |
确定主键:
学生编号(PK) | 教师编号(PK) | 学生姓名 | 教师姓名 |
1001 | 001 | 张三 | 王老师 |
1002 | 002 | 李四 | 赵老师 |
1003 | 001 | 王五 | 王老师 |
1001 | 002 | 张三 | 赵老师 |
以上虽然确定了主键,但此表会出现大量的冗余,主要涉及到的冗余字段为“学生姓名”和“教师姓名”,出现冗余的原因在于,学生姓名部分依赖了主键的一个字段学生编号,而没有依赖教师编号,而教师姓名部门依赖了主键的一个字段教师编号,这就是第二范式部分依赖。
解决方案如下:
学生信息表
学生编号(PK) | 学生姓名 |
1001 | 张三 |
1002 | 李四 |
1003 | 王五 |
教师信息表
教师编号(PK) | 教师姓名 |
001 | 王老师 |
002 | 赵老师 |
教师和学生的关系表
学生编号(PK) fkà学生表的学生编号 | 教师编号(PK) fkà教师表的教师编号 |
1001 | 001 |
1002 | 002 |
1003 | 001 |
1001 | 002 |
如果一个表是单一主键,那么它就复合第二范式,部分依赖和主键有关系
以上是一种典型的“多对多”的设计
19.3 第三范式
建立在第二范式基础上的,非主键字段不能传递依赖于主键字段。(不要产生传递依赖)
学生编号(PK) | 学生姓名 | 班级编号 | 班级名称 |
1001 | 张三 | 01 | 一年一班 |
1002 | 李四 | 02 | 一年二班 |
1003 | 王五 | 03 | 一年三班 |
1004 | 六 | 03 | 一年三班 |
从上表可以看出,班级名称字段存在冗余,因为班级名称字段没有直接依赖于主键,班级名称字段依赖于班级编号,班级编号依赖于学生编号,那么这就是传递依赖,解决的办法是将冗余字段单独拿出来建立表,如:
学生信息表
学生编号(PK) | 学生姓名 | 班级编号(FK) |
1001 | 张三 | 01 |
1002 | 李四 | 02 |
1003 | 王五 | 03 |
1004 | 六 | 03 |
班级信息表
班级编号(PK) | 班级名称 |
01 | 一年一班 |
02 | 一年二班 |
03 | 一年三班 |
以上设计是一种典型的一对多的设计,一存储在一张表中,多存储在一张表中,在多的那张表中添加外键指向一的一方的主键
19.4 三范式总结
第一范式:有主键,具有原子性,字段不可分割
第二范式:完全依赖,没有部分依赖
第三范式:没有传递依赖
数据库设计尽量遵循三范式,但是还是根据实际情况进行取舍,有时可能会拿冗余换速度,最终用目的要满足客户需求。
一对一设计,有两种设计方案:
第一种设计方案:主键共享
第二种设计方案:外键唯一