范式

范式 目标 关键要求
1NF 确保原子性 每个字段必须包含不可再分的值,表中每列的值是原子值。
2NF 消除部分依赖 满足1NF,且所有非主键字段完全依赖于主键(消除部分依赖)。
3NF 消除传递依赖 满足2NF,且非主键字段直接依赖于主键,避免传递依赖。
BCNF 每个决定因素必须是候选键 满足3NF,且每个非平凡的函数依赖的左边部分必须是候选键。
4NF 消除多值依赖 满足BCNF,且每个多值依赖被消除,避免冗余数据。

1.第一范式(1NF)-原子性

每个字段必须包含原子值,表中的每列不能包含集合、数组或重复的值

不符合1NF的例子:

学生ID | 学生姓名 | 电话号码
-------------------------------------------
1      | 张三    | 123456789, 987654321
2      | 李四    | 111222333, 444555666

在表中,电话号码列包含了多个电话号码,违反了1NF的的原子性要求

符合1NF的例子:

学生ID | 学生姓名 | 电话号码
---------------------------
1      | 张三    | 123456789
1      | 张三    | 987654321
2      | 李四    | 111222333
2      | 李四    | 444555666

我们将多个电话号码分到不同的行,每个字段只包含一个值,符合1NF。

2.第二范式(2NF) -消除部分依赖

满足1NF,并消除部分依赖。即所有非主键字段必须以来主键,不能仅依赖于主键的一部分(适用于符合主键)

不符合2NF的例子:

学生ID | 课程ID | 教师   | 成绩
---------------------------
1      | 101    | 王老师 | 85
1      | 102    | 李老师 | 90
2      | 101    | 王老师 | 88
2      | 103    | 张老师 | 92

这里,教师只依赖于课程ID,不依赖于复合主键(学生ID,课程ID)。这是部分依赖,不符合

2NF。

符合2NF的例子:

-- 学生成绩表
学生ID | 课程ID | 成绩
---------------------------
1      | 101    | 85
1      | 102    | 90
2      | 101    | 88
2      | 103    | 92

-- 课程信息表
课程ID | 教师
--------------
101    | 王老师
102    | 李老师
103    | 张老师

把教师信息移到单独的课程信息表,这样教师字段不再依赖于复合主键的部分,而依赖于课程ID

3.第三范式(3NF) -消除传递依赖

满足2NF,并且消除传递依赖。也就是非主键字段不能依赖于其他主键字段。所有非主键字段必须直接依赖于主键,而不是通过其他非主键字段传递依赖

不符合3NF的例子:

学生ID | 学生姓名 | 学生地址    | 地址邮政编码
------------------------------------------
1      | 张三    | 北京市      | 100000
2      | 李四    | 上海市      | 200000

这里,地址邮政编码依赖于学生地址,而学生地址依赖于学生姓名。这是一个传递依赖,因为学生地址地址邮政编码都是非主键字段,但地址邮政编码间接依赖于学生ID。不满足3NF的需求。

符合3NF的例子:

-- 学生信息表
学生ID | 学生姓名 | 学生地址
-------------------------
1      | 张三    | 北京市
2      | 李四    | 上海市

-- 地址信息表
学生地址 | 地址邮政编码
---------------------
北京市   | 100000
上海市   | 200000

地址邮政编码字段移动到一个单独的表中,确保地址邮政编码直接依赖于学生地址,而不再通过学生地址间接依赖于学生ID,符合3NF。

4.BCNF(博伊斯-科得范式)

BCNF要求每一个非平凡的函数依赖决定因素(determinant)必须是候选键。换句话说,BCNF要求每个函数依赖的左边必须是候选键。这样可以进一步消除数据冗余和异常。

1. BCNF与3NF的关系

  • 3NF要求:一个表必须消除传递依赖。非主键字段必须直接依赖于主键。
  • BCNF要求:更严格地要求每个依赖关系的决定因素(即函数依赖的左边部分)必须是候选键。

函数依赖的定义

  • 决定因素(determinant):一个字段或一组字段,决定了另一个字段的值。
  • 非平凡依赖:依赖关系中,决定因素不完全等于右边的字段。

3NF能够处理大多数的数据冗余问题,但对于一些特殊情况,3NF仍然不能完全消除冗余,BCNF则进一步加强了这一点。

2. 不符合BCNF的例子

我们来看一个不符合BCNF的例子:

假设我们有一个表格记录学生、课程和教师的信息:

学号  | 课程ID | 教师  | 成绩
--------------------------
1     | 101    | 王老师 | 85
1     | 102    | 李老师 | 90
2     | 101    | 王老师 | 88
2     | 103    | 张老师 | 92
存在的依赖关系
  • 学号, 课程ID → 成绩(每个学生在每门课程中的成绩是唯一的)
  • 课程ID → 教师(每门课程对应一个固定的教师)

根据这些依赖关系,表中的课程ID → 教师是一个依赖关系,但课程ID并不是一个候选键,而是一个普通的非主键字段。这样,表中的教师字段就依赖于课程ID,而课程ID并不是表的候选键。因此,这个表不符合BCNF。

符合BCNF的表结构

为了让表符合BCNF,我们需要调整表结构,确保每个非主键字段只依赖于候选键。我们可以将表拆分成两个表:

  1. 学生课程成绩表

    CREATE TABLE StudentCourse (
        学号 INT,
        课程ID INT,
        成绩 DECIMAL(5, 2),
        PRIMARY KEY (学号, 课程ID)
    );
    
  2. 课程教师表

    CREATE TABLE CourseTeacher (
        课程ID INT,
        教师 VARCHAR(50),
        PRIMARY KEY (课程ID)
    );
    

这样,我们将课程与教师的信息拆分成了一个单独的表,消除了课程ID → 教师的依赖,确保每个表的非主键字段完全依赖于其主键(即每个表中的字段都依赖于候选键)。

BCNF通过严格要求每个非平凡的依赖的决定因素必须是候选键,进一步消除了数据库中的冗余数据和不一致的情况。与3NF相比,BCNF处理了3NF无法解决的部分依赖问题,避免了数据冗余。

简单总结:

  • 3NF 解决了传递依赖问题,确保了非主键字段之间不会发生依赖。
  • 4NF 解决了多值依赖问题,通过将独立的多值属性分开存储,避免了数据冗余和一致性问题。
  • BCNF:进一步要求每个函数依赖的左边部分必须是候选键,解决了3NF中某些特殊情况下的冗余问题。

BCNF比3NF更严格,它能有效避免一些边缘情况的依赖问题,确保数据结构更加规范化。

5.第四范式(4NF) -消除多值依赖

满足BCNF,并且消除多值依赖。多值依赖是指一个字段依赖于多个其他字段,而这些依赖之间没有直接关系,通常涉及多个独立的多值属性

目的:消除多值依赖,减少数据冗余,避免数据不一致。

不符合4NF的例子:

学生ID | 学生姓名 | 学生爱好   | 学生课程
----------------------------------------
1      | 张三    | 游泳, 画画 | 数学, 物理
2      | 李四    | 足球      | 化学, 英语

在上面的表中,学生爱好学生课程字段同时依赖于学生ID,但是它们是独立的属性,没有直接关系。由于每个学生可以有多个爱好和多个课程,表中的数据变得冗余,而且插入和更新操作可能会导致数据的不一致性,这是典型的多值依赖。

符合4NF的例子:

sql复制编辑-- 学生爱好表
学生ID | 学生爱好
-------------------
1      | 游泳
1      | 画画
2      | 足球

-- 学生课程表
学生ID | 学生课程
-------------------
1      | 数学
1      | 物理
2      | 化学
2      | 英语

我们将学生的爱好和课程拆分成两个独立的表,这样就消除了多值依赖,确保每个表中的每一行都只能存储一个独立的值。这样既避免了冗余数据,又提高了数据的一致性和可维护性,符合4NF。