数据库设计范式

    技术2025-02-04  45

    数据库规范化,又称正规化、标准化,是数据库设计的一系列原理和技术,以减少数据库中数据冗余,增进数据的一致性。关系模型的发明者埃德加·科德最早提出这一概念,并于1970年代初定义了第一范式、第二范式和第三范式的概念,还与Raymond F. Boyce于1974年共同定义了第三范式的改进范式——BC范式。

    除外还包括针对多值依赖的第四范式,连接依赖的第五范式、DK范式和第六范式。

    现在数据库设计最多满足3NF,普遍认为范式过高,虽然具有对数据关系更好的约束性,但也导致数据关系表增加而令数据库IO更易繁忙,原来交由数据库处理的关系约束现更多在数据库使用程序中完成。


    第一范式(1NF)

    第一范式(1NF)是为了要排除 重复组 的出现,所采用的方法是要求数据库的每个列的值域都是由 原子值 组成;每个字段的值都只能是单一值。

    第一范式存在 部分函数依赖 和 完全函数依赖

    举个栗子

    重复组

    重复组是指每一条记录可能有不定个数的值

    顾客日期数量PeteMonday19.00 -28.20PeteWednesday-84.00SarahFriday100.00 150.00 -40.00

    应将每条记录都转换为单一记录,即消除 重复组

    顾客日期数量PeteMonday19.00PeteMonday-28.20PeteWednesday-84.00SarahFriday100.00SarahFriday150.00SarahFriday-40.00

    缺乏唯一标识符

    在同一张表中有两条相同的记录,无法分辨这两条记录

    顾客日期数量PeteMonday19.00PeteMonday19.00

    应给每条记录设置一个唯一标识符,可以是一个字段,也可以是一组字段

    id顾客日期数量1PeteMonday19.002PeteMonday19.00

    第二范式(2NF)

    第二范式(2NF)是要求数据表里的所有非主属性都 完全函数依赖 于候选关键字。

    数据表的属性 完全依赖 于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。

    如果一个数据表的键只有单个字段的话,它就一定符合第二范式。

    如果一个数据表满足第二范式应该

    符合第一范式所有 非主键字段 都不能 部分函数依赖 候选关键字

    举个栗子

    因为同一个组件有可能由不同的供应商提供,所以得把 [组件ID] 和 [供应商ID] 合在一起组成一个主键。

    本表主键:{ 组件ID,供应商ID } 组件 ID价格供应商 ID供应商名称供应商地址6559.991AliBaba杭州7320.001AliBaba杭州6569.992Tencent深圳

    [组件ID] 、 [供应商ID] 和 [价格] 之间的关系很正确:同一个组件在不同供应商有可能会有不同的报价,所以价格 完全依赖 主键。

    另一方面,[供应商名称] 和 [供应商住址] 就只和 [供应商 ID] 有关(部分依赖),这不符合第二范式的原则。仔细看就会发现 “AliBaba” 这个名称和 “杭州” 这个地址重复出现了两次;要是它改名了或是被其他公司并购了怎么办?这时候最好把这些数据存到第二个数据表中:

    供应商 ID供应商名称供应商地址1AliBaba杭州2Tencent深圳 组件 ID价格供应商 ID6559.9917320.0016569.992

    检查数据表里的每个字段,确认它们是不是都和关键词完全相关, 这样才能知道这个数据表是不是符合第二范式; 如果不是的话,就把那些不完全相关的字段移到独立的数据表里


    第三范式

    第三范式(3NF)要求每一个非主属性都不 传递函数依赖 于候选关键字

    举个栗子

    本表主键:{OrderID} OrderIDOrderDateCustomerIDCustomerNameCustomerAddrCustomerCity12018.07.291001源志历下区山大路 47 号济南市22019.05.201002张三东城区天坛东里甲 1 号北京市32019.05.221003李四浦东新区耀体路 701 号上海市

    其中 [OrderDate],[CustomerID],[CustomerName],[CustomerAddr],[CustomerCity] 等非主键列都完全依赖于主键(OrderID),所以符合 2NF。

    不过问题是 [CustomerName],[CustomerAddr],[CustomerCity] 直接依赖的是 [CustomerID](非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合3NF。

    通过拆分 Order 为 Order 和 Customer 从而达到 3NF。

    OrderIDOrderDateCustomerID12018.07.29100122019.05.20100232019.05.221003 CustomerIDCustomerNameCustomerAddrCustomerCity1001源志历下区山大路 47 号济南市1002张三东城区天坛东里甲 1 号北京市1003李四浦东新区耀体路 701 号上海市

    再举个栗子

    本表主键:{订单编号}

    订单编号(Order Number)客户名称(Customer Name)单价(Unit Price)数量(Quantity)总价(Total)1000Jacob$35.003$105.001001Tim$25.002$50.001000Yuanzhibx$25.003$75.00

    第三范式要求非主键字段之间不能有依赖关系,显然 [总价] 依赖于非主键字段 [单价] 和 [数量] ,不符合第三范式

    将 [总价] 字段删除,使用时通过 [单价] * [数量] 既可获得 [总价] 字段内容

    即原本使用 SQL 语句:

    SELECT Order.Total FROM Order

    修改为:

    SELECT UnitPrice * Quantity FROM Order 订单编号客户名称单价数量1000Jacob$35.0031001Tim$25.0021000Yuanzhibx$25.003

    第二范式(2NF) 和 第三范式(3NF) 的概念很容易混淆,区分它们的关键点在于

    2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分(部分函数依赖)3NF:非主键列是直接依赖于主键,还是直接依赖于非主键列(传递函数依赖)

    参考

    WiKi - 维基百科
    Processed: 0.013, SQL: 9