数据库设计范式可以保证数据表的维护以及扩充性,以一种更加合理的方式来作为数据库设计的标准参考,常见的数据库设计一共有三个范式。
1、第一范式(单表,参考salgrade表)
第一范式:数据表中的每个字段都不可再分。
为了发现这条范式,那么下面定义以下的数据库创建脚本。
CREATE TABLE 成员(
编号 NUMBER PRIMARY KEY ,
姓名 VARCHAR2(30) NOT NULL ,
联系方式 VARCHRA2(200)
) ;
此时的确创建了一张表,但是这张表的创建字段里面联系方式字段一定是可以再分的,所以此时的设计不符合于第一设计范式,应该修改如下:
CREATE TABLE 成员(
编号 NUMBER PRIMARY KEY ,
姓名 VARCHAR2(30) NOT NULL ,
手机 VARCHRA2(200) ,
固定电话 VARCHRA2(200) ,
邮政编码 VARCHRA2(200) ,
城市 VARCHRA2(200) ,
地址 VARCHRA2(200) ,
QQ VARCHRA2(200)
) ;
以上的字段已经无法再分了,所以这就符合于第一设计范式。但是针对于第一范式也有两点说明:
国情问题:如果现在在中国,那么姓名只需要一个字段即可,如果在国外需要fisrtName、lastName两个字段,这个不属于讨论范围之内;
关于生日的字段,不可能拆分为三个字段:
CREATE TABLE 成员(
编号 NUMBER PRIMARY KEY ,
姓名 VARCHAR2(30) NOT NULL ,
生日_年 NUMBER(4) ,
生日_月 NUMBER(2) ,
生日_天 NUMBER(2)
) ;
很明显以上的设计也不符合于第一范式,第一范式最保险的做法就是使用所有提供的基本数据类型完成。
2、第二范式(多对多)
第二范式:数据库表之中不存在非关键字段对任意一候选关键字段的部分函数依赖。
如过要想解释第二范式,那么在之前先需要解释下什么叫函数依赖。
如果说现在有如下的一张数据表:
NO | 电影名称 | 导演 | 演员 | 上映日期 | 公司 |
---|---|---|---|---|---|
1 | 甲方乙方 | 冯小刚 | 葛优、徐帆、李期 | 1999 | 华谊兄弟 |
2 | 大腕 | 冯小刚 | 葛优、关之琳 | 2000 | 华谊兄弟 |
现在如果由“电影名称、公司、上映日期”可以确定一个导演,没有函数依赖;
如果现在由“电影名称、公司、导演”不可以确定一个演员,存在函数依赖。
而对于函数还有第二个层次,指的是数据表的多个字段之间不允许存在函数关系。
CREATE TABLE 定单 (
定单编号 NUMBER PRIMARY KEY ,
下单日期 DATE ,
购买数量 NUMBER ,
商品单价 NUMBER ,
总价 NUMBER
) ;
现在可以发现,总价 = 商品单价 * 商品数量,这是存在函数关系的。
而对于关键字段的设置本身又成为了第二范式的一个重点,现在为了说明此类问题,举一个例子:要求完成一个学生选课的数据表,学生针对不同的课程可以有不同的成绩,那么现在如果按照第一范式设计,则数据库创建脚本:
CREATE TABLE 学生选课(
学生编号 NUMBER PRIMARY KEY ,
学生姓名 VARCHAR2(50) NOT NULL ,
课程名称 VARCHAR2(50) ,
课程学分 NUMBER ,
成绩 NUMBER
) ;
INSERT INTO 学生选课(学生编号,学生姓名,课程名称,课程学分,成绩) VALUES (1,'张三','Oracle',2,89) ;
INSERT INTO 学生选课(学生编号,学生姓名,课程名称,课程学分,成绩) VALUES (2,'李四','Java',3,90) ;
INSERT INTO 学生选课(学生编号,学生姓名,课程名称,课程学分,成绩) VALUES (1,'张三','Java',3,99) ;
此时的设计存在一下问题:
- 主键的设计很麻烦;
- 数据重复(冗余),如果有一门课程有1W学生参加,那么更新一门课程学分的时候要修改1W条;
如果假设一门课程没有任何一个学生参加,那么这门课程的信息就彻底消失了。
所以现在第一设计范式不可能满足于需求,那么现在继续来分析已经提出的要求:一个学生可以参加多门课程,一门课程可以有多个学生参加(一个运动员可以参加多个项目,一个项目可以有多个运动员参加),这应该可以是一个多对多的操作关系,既然是多对多坑定是三张数据表完成。
CREATE TABLE 学生 (
学生编号 NUMBER PRIMARY KEY ,
学生姓名 VARCHAR2(50) NOT NULL
) ;
CREATE TABLE 课程 (
课程编号 NUMBER PRIMARY KEY ,
课程名称 VARCHAR2(50) ,
课程学分 NUMBER
) ;-- student_course
CREATE TABLE 学生_选课(
学生编号 NUMBER REFERENCES 学生(学生编号) ON DELETE CASCADE ,
课程编号 NUMBER REFERENCES 课程(课程编号) ON DELETE CASCADE ,
成绩 NUMBER
) ;
INSERT INTO 学生(学生编号,学生姓名) VALUES(1,'张三') ;
INSERT INTO 学生(学生编号,学生姓名) VALUES(2,'李四') ;
INSERT INTO 课程(课程编号,课程名称,课程学分) VALUES (10,'Java',3) ;
INSERT INTO 课程(课程编号,课程名称,课程学分) VALUES (11,'Oracle',2) ;
INSERT INTO 课程(课程编号,课程名称,课程学分) VALUES (12,'马克思主义哲学',6) ;
INSERT INTO 学生_选课(学生编号,课程编号,成绩) VALUES (1,10,80) ;
INSERT INTO 学生_选课(学生编号,课程编号,成绩) VALUES (1,11,90) ;
INSERT INTO 学生_选课(学生编号,课程编号,成绩) VALUES (2,10,90) ;
就是之前的运动员程序。
3、第三范式(一对多)
如果说现在要求定义以下的一种关系:一个学校有多个学生,每个学生只能在一个学校。那么肯定现在无法使用第一方式,也不能勾使用第二范式,因为第二范式之中可以表示出一个学校有多个学生,但是也会同时出现另外一个问题:
一个学生可以同时出现在多个学校,所以在这样的情况下,就可以参考之前学习的”dept-emp”表的关系。
范例:进行数据库的设计
CREATE TABLE 学校 (
学校编号 NUMBER PRIMARY KEY ,
名称 VARCHAR2(50)
) ;
CREATE TABLE 学生 (
学生编号 NUMBER PRIMARY KEY ,
学生姓名 VARCHAR2(50) NOT NULL ,
学校编号 NUMBER REFERENCES 学校(学校编号)
) ;
INSERT INTO 学校(学校编号,名称) VALUES (1,'清华大学') ;
INSERT INTO 学校(学校编号,名称) VALUES (2,'家里顿大学') ;
INSERT INTO 学生(学生编号,学生姓名,学校编号) VALUES(1,'张三',1) ;
INSERT INTO 学生(学生编号,学生姓名,学校编号) VALUES(2,'李四',1) ;
INSERT INTO 学生(学生编号,学生姓名,学校编号) VALUES(3,'王五',2) ;
在实际工作中,第三范式应该是有限考虑的,也是使用最多的,不过以上所有的讲解过的设计范式,只能给你提供一个思考,或者一个参考,并不能100%作为数据库设计的唯一准则。