Skip to main content

数据库设计3个范式

这是一个很经典的数据库面试题。回答时不仅要说出定义,最好能结合例子说明,并适当提一下实际工程中不会严格遵循,因为过度拆分会影响查询性能。

标准的回答是这样的:

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

核心要求:数据库表的每一列都是不可再分的原子数据项,不能有“嵌套表”或“多值字段”。

  • 反例:有一列叫“联系方式”,里面存着“13800138000,北京朝阳区” —— 这包含了电话和地址,可以再分。
  • 正例:拆成“电话”和“地址”两列。
  • 面试话术:“保证字段的原子性,一个字段只存一个信息。”

2. 第二范式 (2NF):完全依赖

前提:先满足第一范式。 核心要求:表中的每一列都必须完全依赖于主键。对于联合主键的表,不能出现只依赖主键一部分的列。

  • 反例:一张“订单明细”表,联合主键是 (订单ID, 商品ID)。但表中有一列“订单日期”,它只依赖于订单ID,不依赖于商品ID。这就产生了部分依赖。
  • 后果:插入一个订单号时,如果只有1件商品,商品ID为空则主键不完整无法插入;修改订单日期可能要改多行,产生数据冗余。
  • 正例:拆分出“订单表”(存订单日期)和“订单明细表”(存商品和数量)。
  • 面试话术:“消除部分依赖,所有列必须完全依赖主键,主要针对联合主键的情况。”

3. 第三范式 (3NF):无传递依赖

前提:先满足第二范式。 核心要求:非主键列之间不能有依赖关系,即消除传递依赖

  • 反例:一张“学生表” (学号, 姓名, 学院ID, 学院名称)。学号决定学院ID学院ID决定学院名称。所以学院名称间接依赖于学号
  • 后果:如果学院改名,需要更新所有该学院的学生记录,容易产生数据不一致。
  • 正例:拆出“学院表”(学院ID, 学院名称),学生表只留学院ID作为外键。
  • 面试话术:“非主键列必须直接依赖于主键,不能依赖于其他非主键列。”

💡 加分回答(体现工程经验)

说完定义后,建议补上这段话:

在实际开发中,我们通常不要求严格遵循三大范式。 因为完全遵循范式意味着大量的表关联查询(JOIN),在高并发、大数据量场景下性能会很差。

我们会采用‘反范式设计’:比如在订单表里冗余存一个‘商品名称’,虽然违反第三范式(商品名称依赖商品ID,不直接依赖订单号),但好处是查询订单时不需要再关联商品表,速度更快。

口诀:1NF 字段不可分,2NF 完全依赖主键,3NF 消除传递依赖。设计原则:以业务读性能优先,在冗余和一致性之间做平衡。”