在关系数据库设计中,存在多个范式,通常从第一范式(1NF)到更高阶的范式。以下是一些常见的数据库范式:
确保每个表的每个列都包含原子值,不可再分。
不满足第一范式的后果:
违反第一范式的学生课程信息表:
学生ID | 学生姓名 | 所修课程 |
---|---|---|
1 | 小明 | 数学, 物理 |
2 | 小红 | 化学, 英语 |
3 | 小刚 | 生物, 历史, 地理 |
上述表违反了第一范式,因为 "所修课程" 这一列包含了多个值。正确的设计应该是将每门课程放在单独的行中:
学生ID | 学生姓名 | 所修课程 |
---|---|---|
1 | 小明 | 数学 |
1 | 小明 | 物理 |
2 | 小红 | 化学 |
2 | 小红 | 英语 |
3 | 小刚 | 生物 |
3 | 小刚 | 历史 |
3 | 小刚 | 地理 |
确保表中的非主键列完全依赖于整个主键,而不是部分依赖。
也就是说2NF针对复合组件和部分依赖的情况。如果表中有一个复合组件(即有多个列组成的主键),那么所有其他非主键列都必须完全依赖于这个复合组件。
以下表格是一个具有复合主键(StudentID, CourseID)且存在冗余的表格。因为 CourseName
和 Instructor
不完全依赖于整个复合主键,而只依赖于部分主键。
StudentCourses
| StudentID | CourseID | CourseName | Instructor |
|-----------|----------|------------|--------------|
| 1 | 101 | Math | Prof. Smith |
| 1 | 102 | English | Prof. Johnson|
| 2 | 101 | Math | Prof. Smith |
在原始表格中,Math和Prof. Smith的信息被重复存储了两次(两次在StudentID为1和2的行中出现),而这两门课实际上是相同的。这种冗余会导致以下问题:
1. 数据一致性:如果对Math
或Prof. Smith
的信息进行修改,需要在多个地方进行更新,容易出现数据不一致的情况。
2.?存储空间浪费: 存储相同的课程信息多次会浪费存储空间,尤其在大型数据库中可能成为一个问题。
3.?维护复杂性: 数据冗余增加了维护的复杂性,因为需要确保多个地方的数据保持一致。
通过拆分表格,我们可以消除这些冗余,如拆分后的Courses
表格所示:
Students
| StudentID | StudentName |
|-----------|-------------|
| 1 | John |
| 2 | Jane |
Courses
| CourseID | CourseName | Instructor |
|----------|------------|--------------|
| 101 | Math | Prof. Smith |
| 102 | English | Prof. Johnson|
StudentCourses
| StudentID | CourseID |
|-----------|----------|
| 1 | 101 |
| 1 | 102 |
| 2 | 101 |
现在,每门课程只有一条记录,避免了在原表格中重复存储相同的课程信息。这有助于提高数据一致性、减少存储空间的浪费,并简化了维护过程。这就是通过第二范式的原则来消除冗余的方式。
确保表中的每个非主键列不依赖于其他非主键列,即不存在传递依赖。
考虑一个包含学生信息、课程信息和学生成绩的表格:
在上述表格中,(StudentID, CourseID)
是复合主键。这个表格满足第二范式,因为非主键列(例如 Instructor
和 Department
)完全依赖于整个复合主键, 只有一个完整的(StudentID, CourseID)才能确定一个Instructor 或者Department
??。然而,该表不满足第三范式,因为 Instructor
?和Department列直接依赖于 CourseID
,而 CourseID
又依赖于 StudentID
。存在传递依赖关系,违反了第三范式的要求。
StudentCourses
| StudentID | CourseID | Instructor | Department |
|-----------|----------|--------------|--------------|
| 1 | 101 | Prof. Smith | Computer Sci |
| 1 | 102 | Prof. Johnson| English |
| 2 | 101 | Prof. Smith | Computer Sci |
为了满足第三范式,我们可以将表格拆分,将 Instructor
列从 StudentCourses
表中分离:
Courses
| CourseID | Department |
|----------|--------------|
| 101 | Computer Sci |
| 102 | English |
StudentCourses
| StudentID | CourseID |
|-----------|----------|
| 1 | 101 |
| 1 | 102 |
| 2 | 101 |
Instructors
| CourseID | Instructor |
|----------|--------------|
| 101 | Prof. Smith |
| 102 | Prof. Johnson|
也就是说对表格里的每一个候选键,当他们作为主键时,每个非主属性都要完全依赖于该候选键,才算满足BCNF范式。候选键是可以唯一标识一行数据的最小组合。
Orders
| OrderID | ProductID | ProductName | CustomerID | CustomerName | Price |
|---------|-----------|-------------|------------|--------------|-------|
| 1 | 101 | Laptop | 1001 | John | 1200 |
| 2 | 102 | Printer | 1002 | Jane | 300 |
| 3 | 101 | Laptop | 1003 | Bob | 1200 |
在上述表格中,(OrderID, ProductID)
是候选键。但是,CustomerName
仅依赖于 CustomerID
,而不依赖于整个候选键。为了满足 BCNF,可以将表格拆分:
Orders
| OrderID | ProductID | CustomerID | Price |
|---------|-----------|------------|-------|
| 1 | 101 | 1001 | 1200 |
| 2 | 102 | 1002 | 300 |
| 3 | 101 | 1003 | 1200 |
Customers
| CustomerID | CustomerName |
|------------|--------------|
| 1001 | John |
| 1002 | Jane |
| 1003 | Bob |
处理多值依赖,确保每个非主键属性完全依赖于候选键,而不是依赖于候选键的一部分。
考虑一个表格 Employees
:
Employees
| EmployeeID | Department | Skills |
|------------|--------------|-------------------|
| 1 | HR | Communication, HR|
| 2 | IT | Programming, IT |
| 3 | Marketing | Marketing, Sales |
在上述表格中,Skills
列包含多个值,表示一个员工可能有多个技能。为了满足第四范式,可以将表格拆分:
Employees
| EmployeeID | Department |
|------------|------------|
| 1 | HR |
| 2 | IT |
| 3 | Marketing |
EmployeeSkills
| EmployeeID | Skill |
|------------|--------------|
| 1 | Communication|
| 1 | HR |
| 2 | Programming |
| 2 | IT |
| 3 | Marketing |
| 3 | Sales |
处理联合依赖,确保每个非主键属性不依赖于候选键的某一部分。
考虑一个包含项目、部门和员工信息的表格:
Projects
| ProjectID | ProjectName | DepartmentID | DepartmentName | EmployeeID | EmployeeName |
|-----------|-------------|--------------|-----------------|------------|--------------|
| 1 | ProjectA | 101 | HR | 1001 | John |
| 2 | ProjectB | 102 | IT | 1002 | Jane |
| 3 | ProjectC | 103 | Marketing | 1003 | Bob |
在上述表格中,(ProjectID, DepartmentID)
是联合主键。如果考虑 (ProjectID, DepartmentID) -> EmployeeID
的联合依赖关系,那么我们可以将表格拆分
Projects
| ProjectID | ProjectName | DepartmentID |
|-----------|-------------|--------------|
| 1 | ProjectA | 101 |
| 2 | ProjectB | 102 |
| 3 | ProjectC | 103 |
Departments
| DepartmentID | DepartmentName |
|--------------|-----------------|
| 101 | HR |
| 102 | IT |
| 103 | Marketing |
Employees
| EmployeeID | EmployeeName |
|------------|--------------|
| 1001 | John |
| 1002 | Jane |
| 1003 | Bob |
ProjectEmployees
| ProjectID | DepartmentID | EmployeeID |
|-----------|--------------|------------|
| 1 | 101 | 1001 |
| 2 | 102 | 1002 |
| 3 | 103 | 1003 |
这样,联合依赖关系被消除,每个表都只包含必要的信息。
处理超关系(超关系是一个关系中的某些属性是另一个关系的关系键)。
是关系数据库理论中的一个较为高级和少用的范式。它主要涉及到多值依赖的概念,通常在包含多值依赖的情况下才会考虑使用。
考虑一个包含课程、学生和成绩的表格:
Grades
| CourseID | StudentID | Grade |
|----------|-----------|-------|
| 101 | 1001 | A |
| 102 | 1002 | B |
| 101 | 1003 | C |
在上述表格中,(CourseID, StudentID)
是联合主键。如果考虑 CourseID -> Grade
的函数依赖关系,我们可以将表格拆分:
Courses
| CourseID | CourseName |
|----------|------------|
| 101 | Math |
| 102 | English |
Students
| StudentID | StudentName |
|-----------|-------------|
| 1001 | John |
| 1002 | Jane |
| 1003 | Bob |
CourseGrades
| CourseID | Grade |
|----------|-------|
| 101 | A |
| 102 | B |
| 101 | C |
StudentCourseGrades
| StudentID | CourseID | Grade |
|-----------|----------|-------|
| 1001 | 101 | A |
| 1002 | 102 | B |
| 1003 | 101 | C |
第七范式通常涉及到更复杂的概念,主要用于处理多值依赖。在实际应用中,很少需要考虑到第七范式,因为这个范式要求的条件非常严格,而且可能导致过多的表和复杂的查询。