第八章 三范式
8.1 什么是范式
范式是指:设计数据库表的规则(Normal Form) 好的数据库设计对数据的存储性能和后期的程序开发,都会产生重要的影响。建立科学的,规范的数据库就需要满足一些规则来优化数据的设计和存储
8.2 范式的基本分类
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。
8.3 第一范式
即数据库表的每一列都是不可分割的原子数据项,而不能是集合、数组、记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中每个列的值只能是表的一个属性或一个属性的一部分。简而言之,第一范式每一列不可再拆分,称为原子性。 第一范式:每一列不能再拆分
总结:如果不遵守第一范式,查询出数据还需要进一步处理(查询不方便)。遵守第一范式,需要什么字段的数据就查询什么数据(方便查询)。
8.4 第二范式
第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。例如在员工表中的身份证号码即可实现每个员工的区分,该身份证号码即为候选键,任何一个候选键都可以被选作主键。在找不到候选键时,可额外增加属性以实现区分。 第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
第二范式:1. 一张表只描述一件事情
2. 表中的每一个字段都依赖于主键
总结:如果不准守第二范式,数据冗余,相同数据无法区分。遵守第二范式减少数据冗余,通过主键区分相同数据。
8.5 第三范式
在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖) 第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。
总结:如果不准守第三范式,可能会有相同数据无法区分,修改数据的时候多张表都需要修改(不方便修改)。遵守第三范式通过id可以区分相同数据,修改数据的时候只需要修改一张表(方便修改)。
第九章 多表查询
9.1 什么是多表查询
同时查询多张表获取到需要的数据 比如:我们想查询到开发部有多少人,需要将部门表和员工表同时进行查询
准备数据
创建部门表 CREATE TABLE dept ( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20) ); INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部'); ‐‐ 创建员工表 CREATE TABLE emp ( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), gender CHAR(1), ‐‐ 性别 salary DOUBLE, ‐‐ 工资 join_date DATE , ‐‐ 入职日期 dept_id INT ); INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013‐02‐24',1); INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010‐12‐02',2); INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008‐08‐08',2); INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015‐10‐07',3); INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011‐03‐14',1);
9.2 笛尔卡乘积现象
9.2.1 什么是笛尔卡乘积现象
多表查询时左表的每条数据和右表的每条数据组合,这种效果成为笛卡尔积需求:查询每个部门有哪些人具体操作:
SELECT * FROM dept, emp;
以上数据其实是左表的每条数据和右表的每条数据组合。左表有3条,右表有5条,最终组合后3*5=15条数据。
9.2.2 如何清除笛尔卡乘积现象
我们发现不是所有的数据组合都是有用的,只有员工表.dept_id = 部门表.id 的数据才是有用的。所以需要通过条件过滤掉没用的数据。
SELECT * FROM dept, emp WHERE emp.`dept_id`=dept.`id`;
9.3 内连接
用左边表的记录去匹配右边表的记录,如果符合条件的则显示
9.3.1 隐式内连接
隐式内连接:看不到JOIN关键字,条件使用WHERE指定 SELECT 字段名 FROM 左表, 右表 WHERE 条件;
9.3.2 显示内连接
显示内连接:使用INNER JOIN ... ON语句, 可以省略INNER SELECT 字段名 FROM 左表 INNER JOIN 右表 ON 条件;
具体操作:查询唐僧的信息,显示员工id,姓名,性别,工资和所在的部门名称,我们发现需要联合2张表同时才能查询出需要的数据,我们使用内连接
1. 确定查询哪些表
SELECT * FROM dept INNER JOIN emp;
1. 确定表连接条件,员工表.dept_id = 部门表.id 的数据才是有效的
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id`;
1. 确定表连接条件,我们查询的是唐僧的信息,部门表.name='唐僧'
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id` AND emp.`NAME`='唐僧';
1. 确定查询字段,查询唐僧的信息,显示员工id,姓名,性别,工资和所在的部门名称
SELECT emp.`id`, emp.`NAME`, emp.`gender`, emp.`salary`, dept.`NAME` FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id` AND emp.`NAME`='唐僧';
1. 我们发现写表名有点长,可以给表取别名,显示的字段名也使用别名
SELECT e.`id` 员工编号, e.`NAME` 员工姓名, e.`gender` 性别, e.`salary` 工资, d.`NAME` 部门名称 FROM dept d INNER JOIN emp e ON e.`dept_id`=d.`id` AND e.`NAME`='唐僧';
总结内连接查询步骤:
1. 确定查询哪些表
2. 确定表连接条件
3. 确定查询字段
9.4 左外连接
左外连接:使用LEFT OUTER JOIN ... ON,OUTER可以省略 SELECT 字段名 FROM 左表 LEFT OUTER JOIN 右表 ON条件; 用左边表的记录去匹配右边表的记录,如果符合条件的则显示;否则,显示NULL 可以理解为:在内连接的基础上保证左表的数据全部显示
具体操作:在部门表中增加一个销售部
INSERT INTO dept (NAME) VALUES ('销售部');
使用内连接查询
SELECT * FROM dept INNER JOIN emp ON emp.`dept_id`=dept.`id`;
使用左外连接查询
SELECT * FROM dept LEFT OUTER JOIN emp ON emp.`dept_id`=dept.`id`;
9.5 右外连接
右外连接:使用RIGHT OUTER JOIN ... ON,OUTER可以省略 SELECT 字段名 FROM 左表 RIGHT OUTER JOIN 右表ON 条件; 用右边表的记录去匹配左边表的记录,如果符合条件的则显示;否则,显示NULL 可以理解为:在内连接的基础上保证右表的数据全部显示
具体操作:
在员工表中增加一个员工 INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('沙僧','男',6666,'2013-02-24',NULL);
使用内连接查询 SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id; 使用右外连接查询 SELECT * FROM dept RIGHT OUTER JOIN emp ON emp.dept_id=dept.id;