首页 新闻 会员 周边 捐助

1NF→2NF→3NF→BCNF

0
[已解决问题] 解决于 2025-09-13 16:20
一、候选键 vs 组合键(先给定义,再给例子)
  1. 候选键 = 所有能“唯一定一行”的最小字段集,不管它是 1 列还是 n 列。
    • 1 列就能唯一 → 单属性候选键
    • n 列拼一起才能唯一 → 组合候选键(简称组合键)
    两者都是候选键,只是字段个数不同。
  2. 复制代码
    表:学生(学号, 身份证号, 姓名, 年龄)
    {学号} 就能唯一 → 它是一个候选键,而且是单属性的,不是组合键。
    {身份证号} 也能唯一 → 另一个候选键,也是单属性。
    {(学号, 身份证号)} 虽然也能唯一,但不是最小(去掉任一仍唯一),所以不是候选键,只是“超键”。
    结论
    这张表有两个候选键,但零个组合键;因此检查 2NF 时“部分依赖”压根儿不会出现。
    复制代码

     

  3. “函数依赖”就是一句大白话约束
    如果给我左边的值,我就能唯一确定右边的值。
    继续上面的学生表:
    • 学号 → 姓名
      意思是:只要你知道学号,就一定能说出唯一一个姓名,不会出现“学号 1001 既叫张三又叫李四”。
    • 学号 → 年龄
    • 身份证号 → 姓名
    • 身份证号 → 年龄
    这些都叫函数依赖
    学号姓名年龄
    1001 张三 20
    1002 李四 21
    • 你知道学号 = 1001,就只能查到张三和20,不会出现第二行叫“张小三”或 19 岁。
      于是说:
      学号 → 姓名
      学号 → 年龄
      这就是两条函数依赖
问题补充:
  • 候选键:能唯一定一行、且字段最少。
  • 组合键:候选键里字段数 ≥ 2 的那个。
  • 函数依赖:A → B 就是“知道 A 就能唯一说出 B”
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-09-13 16:03
< >
分享
最佳答案
0

1NF:

只要格子里的内容还能用逗号、空格、分号、连字符等再拆出更小含义,就违反 1NF;一格只放一项,就满足 1NF。

不及格的电话列(违反 1NF)
姓名    电话
张三    1380000,1390000
电话列里用逗号把两个号码拼在一起 → 它还能再拆成两条有用信息(13800001390000)。
于是这列不是原子值 → 不满足 1NF。
合格的电话列(满足 1NF)
把多值拆成多行,每格只放一个号码:
姓名    电话
张三    1380000
张三    1390000
现在“电话”列里没有逗号、空格、分号等再分符号,一格就是一个不可分割的号码 → 原子值 → 满足 1NF。

其他常见“非原子”陷阱
地址列写“北京市 海淀区 学院路”,如果业务里经常要单独查“区”,这列就可再拆 → 非原子。
课程列写“语文,数学,英语” → 非原子。
成绩列写“90-100” → 非原子。

2NF:

组合主键时,非主属性不能只依赖主键的一部分

 

原始业务:
学校要存“哪个学生选了哪门课,得了多少分,这门课有多少学分”。
最直观地把所有字段塞进一张大表:
学号姓名课程号课程名成绩学分
下面用这张表依次“升级”,看它在哪一级被卡住。

例子 1:1NF 关卡——“列里还能再拆吗?”
题目:上表是否满足 1NF?
检查:每一列都是不可再分的原子值(没有逗号、空格列表)。
结论:满足 1NF ✅
如果不满足:会出现“电话”列里存“138,139”这种多值,拆成多行即可。

例子 2:2NF 关卡——“组合主键时,非主属性不能只依赖主键的一部分”
  1. 先找主键
    能唯一确定一行的必须是 (学号, 课程号),因为:
    • 一个学生可以选多门课
    • 一门课可以被多个学生选
      → 只有学号+课程号合在一起才能唯一确定一行。
  2. 划分“主属性 / 非主属性”
    主属性:学号、课程号
    非主属性:姓名、课程名、成绩、学分
  3. 看函数依赖
    • (学号,课程号) → 成绩 ✅(整个主键决定非主属性,没问题)
    • 课程号 → 学分 ❌(非主属性“学分”只依赖主键的一部分)
    • 学号 →姓名 ❌(同理,部分依赖)
  4. 结论
    存在部分依赖 → 不满足 2NF ❌
    最高只到 1NF。

“拆表”升到 2NF:

把“部分依赖”单独提出去:
表 R1(选课成绩)
| 学号 | 课程号 | 成绩 |
主键:(学号,课程号)
表 R2(课程信息)
| 课程号 | 课程名 | 学分 |
主键:课程号
表 R3(学生信息)
| 学号 | 姓名 |
主键:学号
现在每张表的非主属性都完全依赖整个主键 → 满足 2NF ✅
 
3NF 检查(重点:传递依赖) 对每张表再扫一遍“有没有 A→B→C 链条”。
  • R1:主键直接决定成绩,没有中间跳板 → 满足 3NF ✅
  • R2:课程号→课程名,课程号→学分,都是主键直接决定,没有传递 → 满足 3NF ✅
  • R3:学号→姓名,直接决定,无传递 → 满足 3NF
BCNF 检查(左边都得是“超码”) 再看**每一个函数依赖的“左边”**是不是都能当主键(即超码)。
  • R1:唯一依赖 (学号,课程号)→成绩,左边就是主键 → 满足 BCNF ✅
  • R2:课程号→课程名,课程号本身已是主键 → 满足 BCNF ✅
  • R3:学号→姓名,学号本身已是主键 → 满足 BCNF ✅
 
3NF 关卡——“不能传递依赖”,隔山打牛
 
表:R(学号, 系别, 系地址)
主键:学号 (单属性,已满足 2NF)
函数依赖:
  1. 学号 → 系别 (学生属于哪个系)
  2. 系别 → 系地址 (一个系只有一个办公楼)
于是链条出现: 学号 → 系别 → 系地址
(主键→非主属性→另一个非主属性)
“系地址”对主键“学号”是传递依赖,违反 3NF
怎么升到 3NF?
把“传递”的那一段劈出去:
R1(学号, 系别)
R2(系别, 系地址)
现在每张表的非主属性都直接依赖主键,不再隔山打牛 → 满足 3NF
 
BCNF例子:
BCNF 一句话:“凡是决定别人的字段,自己都必须是候选键”
反例表
R(教师, 课程, 教室)
业务规则:
  1. 每位老师只上一门课(多门课就多个老师);
  2. 每门课只固定在一个教室上;
  3. 一个教室可以上多门课。
函数依赖
① 教师 → 课程 (老师决定上哪门课)
② 课程 → 教室 (课决定在哪上)
候选键
(教师, 教室) 才能唯一确定一行 → 唯一候选键。

检查 BCNF
  • 依赖①:教师 → 课程
    左边“教师”不是候选键(单独定不了整行)→ 违反 BCNF ❌
  • 依赖②:课程 → 教室
    左边“课程”也不是候选键 → 再次违反 BCNF ❌

如何拆成 BCNF
把“决定者”都提成主键:
R1(教师, 课程) 主键:教师
R2(课程, 教室) 主键:课程
现在两张表的决定因素(教师、课程)都是各自的主键,满足 BCNF
_java_python | 小虾三级 |园豆:984 | 2025-09-13 16:10

R(教师, 课程, 教室)

它仅仅代表“一张表”而已,
后面括号里列出的是这张表包含的字段名。
看到
R(教师, 课程, 教室)
直接脑补成:
“有一张表,表名叫 R,表里有三列:教师、课程、教室。”
_java_python | 园豆:984 (小虾三级) | 2025-09-13 16:25
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册