数据库原理学习笔记(二)数据库范式

范式可以理解成在设计数据表时的规范级别,常见的范式有

  • 第一范式(1NF)
  • 第二范式(2NF)
  • 第三范式(3NF)
  • BC范式(BCNF)

第一范式

要满足第一范式,要求数据表的每个属性无法再分,也就是需要满足原子性。可以把“不可再分”理解成无法用一个单独的值表示,比如说“系”这个属性,平时常说的“计算机系”,“化学系”实际上指的是系名,而系的管理者被称为系主任,所以对于“系”这个属性,只能通过将它拆分成若干属性的组合(系名,系主任,…)来表示。

所以如果数据表中某个属性指的是类似上面的“系”这样的属性,就不满足第一范式,解决办法就是将这个可以再分的属性继续分解

CREATE TABLE student ( 
    id INT NOT NULL AUTO_INCREMENT, /* 学号 */
    name VARCHAR(20) NOT NULL, /* 姓名 */
    faculty /* 系,应该用什么类型表示?它包括系名,系主任等若干子属性 */
    ...
);

可以看到,如果某个属性可以继续分解,那么是无法在创建数据表时表示它的,所以一般可以成功创建的数据表(前提是语义正确)都是符合第一范式的要求的

CREATE TABLE student ( 
    student_id INT, 
    student_name VARCHAR(20), 
    faculty_name VARCHAR(20), 
    faculty_boss VARCHAR(20), 
    PRIMARY KEY (student_id) 
);

第二范式

判断一个数据表是否满足第二范式,可以观察是否存在非主属性对于码的部分函数依赖,方法是

  • 找出数据表中所有的码
  • 根据第一步找出的码,找出所有主属性
  • 在数据表中的所有属性中去除主属性,得到非主属性
  • 查看是否存在某个非主属性对于码的部分函数依赖

如果存在部分函数依赖,则这个数据表就不符合第二范式,解决方法就是模式分解,将违反第二范式的属性单独提出创建一个新表或者分解表使得该表的码只包含一个属性

对于部分函数依赖,码至少要包含两个属性,所以上面的student表是满足第二范式的,下面考虑另一个表student_course,它描述了学生的选课信息,包含

  • 学生信息
  • 课程名,课程成绩
  • 学生所在系信息
CREATE TABLE student_course ( 
    student_id INT NOT NULL, 
    student_name VARCHAR(20), 
    course_name VARCHAR(20), 
    course_score INT, 
    faculty_name VARCHAR(20), 
    faculty_boss VARCHAR(20), 
    PRIMARY KEY (student_id, course_name) 
);

由于课程成绩必须通过确定是哪个学生的哪门课来获取,所以这个表的主码(主键)是(student_id, course_name),现在来一步步分析这个表是否符合第二范式

分析student_course表

第一步,找出数据表中的所有的码。由上面的分析可得这个表只有唯一的码(student_id, course_name)

第二步,找出所有的主属性。码中的每个属性都是主属性,所以主属性包括student_id和course_name

第三步,找出所有的非主属性。除去主属性的都是非主属性,所以非主属性包括student_name, course_score, faculty_name, faculty_boss

第四步,判断非主属性是否对于码存在部分函数依赖

每个属性之间的依赖关系

  • student_id可以推导出student_name,所以student_name完全依赖于student_id
  • student_id可以推导出faculty_name和faculty_boss,所以二者完全依赖于student_id
  • (student_id, course_name)可以推导出course_score,所以course_score完全依赖于(student_id, course_name)

由上述三个关系可以分析出部分依赖关系

  • 由于faculty_name完全函数依赖于student_id,所以faculty_name部分函数依赖于(student_id, course_name)
  • 由于faculty_boss完全函数依赖于student_id,所以faculty_name部分函数依赖于(student_id, course_name)

所以这个表不满足第二范式,可以通过模式分解解决这一问题,比如将课程相关的信息提出去单独作为一个表,这样student_course表的码就只剩一个属性student_id了,自然不存在部分函数依赖

CREATE TABLE student ( 
    student_id INT NOT NULL, 
    student_name VARCHAR(20), 
    faculty_name VARCHAR(20), 
    faculty_boss VARCHAR(20) ,
    PRIMARY KEY (student_id)
);

CREATE TABLE course ( 
    student_id INT NOT NULL, 
    course_name VARCHAR(20), 
    course_score INT, 
    PRIMARY KEY(student_id, course_name) 
);

第三范式

判断一个数据表是否满足第三范式,可以观察是否存在非主属性对于码的传递函数依赖,解决方法也和第二范式相同,将导致不符合第三范式的那个属性提出,单独创建一个表

可以对上面的student和course表进行分析,判断这两个表是否满足第三范式

分析course表

第一步,找出所有的码。course的码只有(student_id, course_name)

第二步,找出所有的主属性。主属性有student_id和course_name

第三步,找出所有的非主属性。非主属性有course_score

第四步,判断是否存在非主属性对于码的传递函数依赖。因为传递函数依赖至少需要有两个非主属性,所以course表不存在对于码的传递函数依赖

所以course表满足第三范式

分析student表

第一步,找出所有的码。student的码只有(student_id)

第二步,找出所有的主属性。主属性为student_id

第三步,找出所有的非主属性。非主属性有student_name,faculty_name和faculty_boss

第四步,判断是否存在非主属性对于码的传递函数依赖。从客观事实的角度出发,如果知道系名,就可以知道系主任是谁,所以faculty_boss完全函数依赖于faculty_name。又因为faculty_name完全函数依赖于码student_id,所以faculty_boss传递函数依赖于码student_id

所以student表不满足第三范式。

可以通过将系相关的信息提出去单独创建一张表,比如

CREATE TABLE student ( 
    student_id INT NOT NULL, 
    student_name VARCHAR(20), 
    faculty_name VARCHAR(20), 
    PRIMARY KEY (student_id)
);

CREATE TABLE faculty ( 
    faculty_name VARCHAR(20) NOT NULL, 
    faculty_boss VARCHAR(20) NOT NULL, 
    PRIMARY KEY (faculty_name) 
);

BC范式

BC范式可以通过判断是否存在主属性对于码的部分函数依赖。出现这种情况的原因通常是由于数据表中存在着多个码。比如说(A,C)和(B,C)都是这个数据表的码,同时B完全函数依赖于A,那么主属性B就部分函数依赖于码(A,C),可以通过继续分解成两个表分别是

  • 表1,包含属性A,B
  • 表2,包含属性A,C

解决

参考资料

解释一下关系数据库的第一第二第三范式? - 刘慰的回答 - 知乎 https://www.zhihu.com/question/24696366/answer/29189700

你可能感兴趣的:(数据库)