综合知识卷里数据库那几道题,最常见的问法是:给一个关系模式和一组函数依赖,问候选键是什么、最高属于第几范式、存在什么问题、怎么分解。这类题答案唯一,可以按固定顺序走,走完还能自己验算。下面这道题,建议先盖住后半篇自己判一遍。

先摆题目

关系模式 R(学号, 姓名, 系名, 系主任, 课程号, 课程名, 成绩),函数依赖集是:

  • 学号 → 姓名,学号 → 系名
  • 系名 → 系主任
  • 课程号 → 课程名
  • (学号, 课程号) → 成绩

这张表记的是“谁选了哪门课、得了多少分”。

函数依赖:知道左边,右边就定了

X→YX \to Y 的意思是:同一个 XX 只对应一个 YY 1。学号 → 系名,一个学号只属于一个系;系名 → 系主任,一个系只有一个主任。

注意方向:箭头反过来不一定成立。课程号 → 课程名 成立,课程名 → 课程号 不成立,不同课程重名是可能的。判断时随手反用箭头,后面的键和分解全错。另外,依赖集是题面给的业务规则,不要凭“现实里应该还有”去补。

第一步:用闭包找候选键

候选键是能推出全部属性、而且再删掉任一属性就推不出全部的最小属性组 1。手算方法叫闭包:从一组属性出发,凡是左边已被包含的依赖就可以拿来用,把右边不断加进来,直到不再增长。

  • 从 学号 出发:学号 → 姓名、学号 → 系名,再用 系名 → 系主任,得到 {学号, 姓名, 系名, 系主任}。缺课程号、课程名、成绩,不够。
  • 从 课程号 出发:只有 {课程号, 课程名},不够。
  • 从 (学号, 课程号) 出发:上面两组结果都能拿到,再加(学号, 课程号) → 成绩,七个属性全齐。

再看最小性:去掉学号只剩课程号,不够;去掉课程号只剩学号,也不够。候选键唯一,是 {学号, 课程号}。

顺手标出主属性:出现在候选键里的属性(学号、课程号)是主属性,其余五个(姓名、系名、系主任、课程名、成绩)是非主属性。后面两步的判断只针对非主属性。

第二步:1NF 只看字段还能不能再分

每个属性都是原子值,1NF 通过。软考很少在这一层设障,除非题面出现“地址”这类可拆字段,或者把多门课塞进一格。

第三步:2NF 看有没有只依赖半个键

2NF 要求:不存在非主属性对候选键真子集的依赖,这种依赖叫部分函数依赖。两个限定词都要看清:只有复合键才可能违反(单属性键没有真子集可用);而且被依赖出来的是非主属性。

本题的键是(学号, 课程号)。学号只是键的一半,而 学号 → 姓名、学号 → 系名 都成立,姓名、系名又是非主属性,这就是部分依赖。课程号 → 课程名 同理。所以最高范式是 1NF,连 2NF 都没到。

第四步:3NF 看有没有绕道

3NF 在 2NF 之上要求:不存在非主属性对候选键的传递函数依赖 2。判断传递依赖要三个条件同时成立:X→YX \to Y、Y→ZY \to Z,而且 YY 不能反过来决定 XX(否则两边互相决定,不算绕道),三个属性集互不相交 2。

本题:学号 → 系名 → 系主任。系名不决定学号(一个系有多个学生),所以 学号 → 系主任 是传递依赖。这条链即使把表拆到 2NF 也还在,卡住的就是 3NF。

问题到底出在哪

  • 冗余:一个系有几百个学生,系主任的名字就在几百行里各存一份,按选课记录算重复得更多。
  • 更新异常:换系主任要改一大批行,漏改一行,同一个系就冒出两个主任。
  • 插入、删除异常:新成立一个系、还没有人选课,系主任这条事实没地方写;把一个系的选课记录全删掉,系主任的信息也跟着消失。

范式的目标就是让一个事实只存在一个地方。只要还有这种跟着别的数据重复的非主属性,就值得拆。

怎么拆

先拆到 2NF:把依赖键一部分的属性和依赖整个键的属性分开。

  • R1(学号, 姓名, 系名, 系主任),键是学号
  • R2(课程号, 课程名),键是课程号
  • R3(学号, 课程号, 成绩),键是(学号, 课程号)

再拆到 3NF:R1 里 学号 → 系名 → 系主任 这条链还在,把“系”这一层独立出去:

  • R11(学号, 姓名, 系名),键是学号
  • R12(系名, 系主任),键是系名

R2、R3 保持不动。四个模式里,每个非主属性都只直接依赖自己那张表的键,全部到 3NF。

怎么知道自己判对了

一、回到每个新模式重算候选键。 R12 只剩系名和系主任,键就是系名。算出来和你设想的不一样,说明拆错了。

二、对每个非主属性问三句。 只依赖这个键?依赖整个键?除了这个键不依赖别的?这就是 “the key, the whole key, and nothing but the key” 2。

三、检查有没有丢信息。 每一步拆出来的两个模式,公共属性能决定其中一侧的其他属性:R11 和 R12 公共属性是系名,系名 → 系主任,拼得回原样;R2 和 R3 公共属性是课程号,课程号 → 课程名,同样成立。再看原依赖有没有丢,本题四条都完整落在某个模式里。

条件一变,答案就变

把 系名 → 系主任 从题面拿掉:R1 变成(学号, 姓名, 系名),键是学号,姓名和系名都直接依赖它,没有传递依赖,拆成三个模式就到 3NF,R12 不存在。可见决定范式的是函数依赖集,不是表名或字段长相,“学生选课表就是 1NF”这种记法不成立。

如果键只有单属性(比如把成绩拿掉,R(学号, 姓名, 系名, 系主任)),就不会有 2NF 问题,只可能卡在 3NF。所以必须按 1NF → 2NF → 3NF 的顺序判,不能跳步。

一处边界

2NF、3NF 的规则全都冲着非主属性去。如果所有属性都出现在某个候选键里(学生—教师—课程这类多候选键的结构就是这样),表里没有非主属性,按定义可同时满足 2NF 和 3NF,却可能因为某个依赖的左边推不出全部属性而不满足 BCNF 3。所以见到 BCNF 选项时,判法是另一句话:每条非平凡依赖的左边,是不是能推出全部属性。