💻 第八章 一用就会的数据库
📄 一句话概括

规范化=拆表+整理,让一条事实只存一处——重复没了,改漏也就没了。

📝 本节和前面几节的关系

设计表时最容易犯错的地方就是【什么都塞进一张表】。本节讲怎么拆、为什么要拆。

一、反面教材:一张大表包打天下

把订单号、客户、商品名、数量全塞进一张表——同一个商品名在每条记录里重复抄写。 麻烦随即而来:改一次商品名要改很多行,漏改一处,同一个商品就有了两个名字; 存储也浪费:重复的字符串占着多份空间——数据越大,浪费越大。

二、拆表:一条事实只存一处

拆:商品只留一张商品表(商品号、商品名),商品名只出现一次; 指:订单表改存商品号,指向商品表——要用名字时顺着编号去取; 整:确认每个字段都【直接依赖】它所在表的主键,不为别的表的信息背锅。 拆完再看:改商品名只需动一处,想漏也漏不了。

三、规范化的账

收益:消除重复存储、消除改漏风险、表各管一事便于理解; 代价:查一次要拼多张表(8.5 的 JOIN 就是干这个的),设计时也要多费脑子; 结论:先把【不重复】做对,性能问题用索引等手段另算——顺序不能颠倒。

四、动手 Demo:拆表消除重复

一张重复抄写商品名的订单表,拆成商品表+订单表,数一数省下多少重复。

orders = [("1001", "铅笔", 2), ("1001", "橡皮", 1),
          ("1002", "铅笔", 5), ("1003", "铅笔", 3)]
names = [o[1] for o in orders]
print("商品名出现", len(names), "次")
print("不同商品只有", len(set(names)), "种")
# 拆表: 商品表只存编号->名字, 订单表存编号
goods = {1: "铅笔", 2: "橡皮"}
print("拆表后商品名只存", len(goods), "次")

实跑输出:

--------------------------------------------------------------------
规范化——通过拆表和整理数据消除浪费
--------------------------------------------------------------------

反面教材: 一张【订单】大表把商品名抄在每条记录里——
同一个商品名重复出现, 改一次要改很多行, 还容易改漏。

    规范化前的订单表(部分):
        订单1001 波仔 买了 铅笔 x2
        订单1001 波仔 买了 橡皮 x1
        订单1002 小墨 买了 铅笔 x5
        订单1002 小墨 买了 直尺 x2
        订单1003 阿池 买了 铅笔 x3

    商品名共出现 5 次, 不同商品其实只有 3 种:
        橡皮、直尺、铅笔

规范化的做法: 拆成两张表——商品表(商品号, 商品名) 只存一次;
订单表改存(订单号, 客户, 商品号, 数量), 用商品号指过去。

  方案       商品名出现次数     改一次商品名要动几处
  ---------------------------------------------------
  拆表前     5 次               5 处
  拆表后     3 次               1 处

规范化的目标: 一条事实只存一处——重复没了, 改漏也就没了。

5 条记录里【铅笔】抄了 3 遍——记录一多,重复就是成倍的内存与改漏风险。 规范化的判断标准可以记成一句话:每个字段只描述它所在表的【主角】。

30 秒实验

  1. 找一张你见过的 Excel 宽表,指出哪几列在重复、可以拆出去。
  2. 用【一条事实只存一处】的标准,检查自己项目里的一张表。

常见误区

  1. 以为表越少越简单——一张大表重复满天飞,改一次漏三处才是真麻烦。
  2. 以为拆得越细越好——拆过头查一次要拼七八张表,该合的时候也要合。
自测三问(点开对答案) 1. 规范化的目标一句话? —— 一条事实只存一处,消除重复与改漏。 2. 拆表之后订单表怎么指向商品? —— 存商品号(编号),用编号去商品表取名字。 3. 规范化的代价是什么? —— 查询要拼多张表,设计时更费脑。

相关笔记