【数据库】AI 设计规范化数据库结构

摘要:用 AI 帮你把一张乱表拆成清爽、不重复的规范数据库。

一、痛点引入

你有没有过这种表——一张 Excel 销售表,从左拉到右二三十列:订单号、客户、电话、地址、产品、单价、数量、销售员、部门……一个订单买三件东西,客户的姓名电话就得重复抄三遍。哪天要改一次客户电话,得满表找;更怕手滑把某一行的"单价"改串了,谁都不知道哪行才是真的。

这种"又宽又乱"的表,行话叫"未规范化"的表(也就是还没按规矩收拾过)。它短期能凑合用,可人一多、数据一涨,就成了出错的温床——改一处漏一处,对不齐、查不准。

咱们今天就用 AI(通义)当参谋,把这样一张乱表正正规规地拆一遍,拆成几张清爽、不重复的小表。不用死记硬背教科书,跟着做就行。

二、目标产出

学完这一篇,你能拿到三样东西:

  1. 一套规范结构:原来一张大宽表,被拆成 5 张各管各事的小表——客户表、产品表、销售员表、订单表、订单明细表。
  2. 清清楚楚的主外键:每张表哪个字段是主键(Primary Key,PK,能唯一锁定一行的小标签,比如"订单号"),哪个字段是外键(Foreign Key,FK,指向别家主键、用来"认亲戚"的字段,比如订单表里的"客户编号"指向客户表),一眼看明白。
  3. 一份可直接执行的建表 SQL:SQL(Structured Query Language,结构化查询语言,专门用来建库、建表、查数据的命令)。下面给的是 MySQL 语法,Access 也能改改就用。

顺带把三个"范式(Normal Form,NF,一套让表格不重复、不乱套的规矩)"——1NF、2NF、3NF——用人话讲明白,以后别人甩你一张表,你一眼能看出它卡在第几范式。

三、案例实战

3.1 先看看这张"乱表"长啥样

假设你是小公司负责订单的同事,手头有一张从系统导出来的「销售总表」。最原始的样子是这样的(注意最后一列"买的商品"):

订单号 下单日期 客户 客户电话 客户地址 买的商品(编号|名称|数量) 销售员 销售员部门
D20240001 2024-01-05 张三 13800001111 北京市朝阳区 P001 机械键盘 1;P002 无线鼠标 2 小李 华北大区
D20240002 2024-01-06 李四 13900002222 上海市浦东区 P003 显示器 1 小王 华东大区

问题一眼就能看出来

  • "买的商品"这一个格子里塞了多个产品(编号、名称、数量混在一起),Excel 看着都费劲,更别说拿它去建库了。
  • 一个订单买几件,信息就挤在一个格子里,没法分别统计"机械键盘卖了多少"。

这种"一个格子装多个值"的情况,正是没过第一范式的典型毛病。下面咱们用 AI 一步步拆。

3.2 第一步:让 AI 把表"摊平"到 1NF

1NF(第一范式)是什么:严格地说,1NF 看的是列原子性——每个格子(字段)只装一个值,不能再拆。上面那种"一个格子塞多个产品"的,先拆开。通俗记成"一行只说一件事"也行,但那只是把多值拆平后的效果,不是 1NF 的形式化定义(1NF 本身只看"格子是不是原子的")。

专业点说:1NF 要求字段是"原子的"(Atomic,不可再分的最小单位),并且不能有重复的"组"。

怎么做:把"买的商品"拆成"产品编号、产品名称、数量"三个独立字段,同一个订单买几件,就拆成几行。张三那单买了键盘+鼠标,就变成两行:

订单号 下单日期 客户 客户电话 客户地址 产品编号 产品名称 产品类别 单价 数量 销售员 销售员部门
D20240001 2024-01-05 张三 13800001111 北京市朝阳区 P001 机械键盘 外设 299.00 1 小李 华北大区
D20240001 2024-01-05 张三 13800001111 北京市朝阳区 P002 无线鼠标 外设 99.00 2 小李 华北大区
D20240002 2024-01-06 李四 13900002222 上海市浦东区 P003 显示器 显示器 899.00 1 小王 华东大区

注意现在一张表的"钥匙"变成了两把拼一起:光"订单号"锁不住一行(同一个订单有两行),得加上"产品编号"才行。这种"两把钥匙拼一起当主键"的,叫复合主键(Composite Primary Key)——记住这个词,下一步要用。

到这儿,表已经过了 1NF:每个格子都是不可再分的一个值(列原子性达标)。拆平之后,直观表现就是"一行只说一件事(一个订单里的一件商品)"——但记住,这是拆平多值后的效果,1NF 本身只看格子是否原子。毛病还在——张三的信息在 D20240001 的两行里各抄了一遍,冗余得厉害。接着上 2NF。

3.3 第二步:让 AI 拆掉"部分依赖"到 2NF

2NF(第二范式)是什么:在 1NF 的基础上,不能"只看一半的钥匙就确定内容"。

专业点说:2NF 要求没有部分依赖(Partial Dependency)——也就是"非主键字段不能只依赖复合主键里的某一部分,必须依赖整把钥匙"。

对照上面的表,复合主键是(订单号,产品编号)。咱们挨个看非主键字段"听谁的":

  • 客户、电话、地址、下单日期、销售员、部门:其实只看"订单号"这一半钥匙就能确定,跟"产品编号"半毛钱关系没有 → 这是部分依赖,要拆走。
  • 产品名称、产品类别、单价:只看"产品编号"这一半钥匙就能确定,跟"订单号"无关 → 也是部分依赖,也要拆走。
  • 数量:必须"订单号+产品编号"两把钥匙一起才确定(同一个订单同一个产品才有唯一数量)→ 它依赖整把钥匙,留着。

拆法(让 AI 按这个思路分表):

  • 把"只跟订单号走"的信息,连同订单号,单独拉成一张订单表
  • 把"只跟产品编号走"的信息,连同产品编号,单独拉成一张产品表
  • 剩下的"订单号+产品编号+数量",单独成一张订单明细表(它就是那个多对多的桥)。

拆完长这样(先别管客户、销售员要不要再拆,那是 3NF 的事):

  • 订单表:订单号(PK)、下单日期、客户、客户电话、客户地址、销售员、销售员部门
  • 产品表:产品编号(PK)、产品名称、产品类别、单价
  • 订单明细表:订单号 + 产品编号(合起来当复合主键)、数量

到这儿 2NF 达标:订单明细表里的每个非主键字段(目前只有"数量")都老老实实依赖整把钥匙了。

3.4 第三步:让 AI 拆掉"传递依赖"到 3NF

3NF(第三范式)是什么:在 2NF 的基础上,不能"绕个弯才确定内容"。

专业点说:3NF 要求没有传递依赖(Transitive Dependency)——也就是"非主键字段 A 决定了非主键字段 B,B 再决定 C,于是 C 是绕弯依赖了主键",这种情况要拆。

盯着刚拆出来的订单表看:里面有"销售员"和"销售员部门"两个字段。一个销售员只属于一个部门,所以"销售员 → 销售员部门"是成立的;而"销售员"本身又是跟着"订单号"(主键)走的。于是链条变成了:订单号 → 销售员 → 销售员部门

这就叫传递依赖——"销售员部门"是绕了个弯才依赖主键的。3NF 要把它拆掉:把销售员单独提成一张销售员表,订单表里只留一个"销售员编号"去指向它。

同理,客户信息(客户、电话、地址)本质上属于"客户"这个独立的人/单位,也该有自己的表,别跟订单搅在一起——这样既去冗余,也避免客户换电话时满表改。于是再提成一张客户表,订单表里只留"客户编号"指向它。

最终 3NF 的 5 张表

表名 字段 主键 / 外键
客户表 客户编号、客户姓名、客户电话、客户地址 客户编号(PK)
产品表 产品编号、产品名称、产品类别、单价 产品编号(PK)
销售员表 销售员编号、销售员姓名、销售员部门 销售员编号(PK)
订单表 订单号、下单日期、客户编号、销售员编号 订单号(PK);客户编号(FK→客户表)、销售员编号(FK→销售员表)
订单明细表 订单号、产品编号、数量 (订单号,产品编号)复合主键;订单号(FK→订单表)、产品编号(FK→产品表)

小提示:每张"实体表"(客户、产品、销售员)都配了一个稳定的编号当主键。别用"姓名"当主键——姓名会重名(两个"张三")、会改,当钥匙不靠谱。编号才是稳的。

3.5 设计图:表与表怎么"认亲戚"

把上面的关系画出来,就是一张清晰的"雪花"结构。表与表之间靠外键(FK)连起来:

        ┌──────────┐
        │  客户表   │
        │ PK客户编号 │
        └────┬─────┘
             │ 1
             ▼ N
  ┌────────┐      ┌──────────────┐      ┌────────┐
  │ 订单表  │1────N│  订单明细表   │N────1│ 产品表  │
  │ PK订单号 │      │ PK(订单号,产品编号)│      │ PK产品编号│
  └───┬────┘      └──────────────┘      └────────┘
      │ 1
      ▼ N
  ┌──────────┐
  │ 销售员表  │
  │ PK销售员编号│
  └──────────┘

关系一句话说清:

  • 客户表 1 : N 订单表:一个客户能下多个订单。
  • 销售员表 1 : N 订单表:一个销售员能接多个订单。
  • 订单表 1 : N 订单明细表:一个订单里能有多件商品。
  • 产品表 1 : N 订单明细表:同一件产品能出现在很多个订单明细里。

外键落点对应:

  • 订单表.客户编号 → 客户表.客户编号
  • 订单表.销售员编号 → 销售员表.销售员编号
  • 订单明细表.订单号 → 订单表.订单号
  • 订单明细表.产品编号 → 产品表.产品编号

3.6 一份可直接用的建表 SQL

下面这段是给 MySQL 的建表语句,复制进数据库就能跑(表名用中文)。若你的服务器标识符字符集支持中文(MySQL 8 默认 utf8mb4),建议给中文标识符包上反引号更稳(如 `客户表`)——下面示例为保持易读未逐一加反引号,你在自己库里加上即可;若仍不支持中文表名,再换成英文如 t_customer。每段都加了注释,照着看就懂:

sql
-- 客户表:存"谁买的" CREATE TABLE 客户表 ( 客户编号 CHAR(6) PRIMARY KEY, -- 主键:唯一标识一个客户 客户姓名 VARCHAR(20) NOT NULL, 客户电话 VARCHAR(20), 客户地址 VARCHAR(100) ); -- 产品表:存"卖的是什么" CREATE TABLE 产品表 ( 产品编号 CHAR(6) PRIMARY KEY, -- 主键:唯一标识一件产品 产品名称 VARCHAR(50) NOT NULL, 产品类别 VARCHAR(20), 单价 DECIMAL(10,2) NOT NULL -- DECIMAL 存金额,避免小数算错 ); -- 销售员表:存"谁卖的"(部门跟着销售员走,不进订单表,避免传递依赖) CREATE TABLE 销售员表 ( 销售员编号 CHAR(4) PRIMARY KEY, 销售员姓名 VARCHAR(20) NOT NULL, 销售员部门 VARCHAR(20) ); -- 订单表:一次购买的"头信息" CREATE TABLE 订单表 ( 订单号 CHAR(10) PRIMARY KEY, 下单日期 DATE NOT NULL, 客户编号 CHAR(6) NOT NULL, 销售员编号 CHAR(4) NOT NULL, -- 外键:认回客户表和销售员表 FOREIGN KEY (客户编号) REFERENCES 客户表(客户编号), FOREIGN KEY (销售员编号) REFERENCES 销售员表(销售员编号) ); -- 订单明细表:订单和产品的"多对多桥",用两把钥匙拼复合主键 CREATE TABLE 订单明细表 ( 订单号 CHAR(10) NOT NULL, 产品编号 CHAR(6) NOT NULL, 数量 INT NOT NULL, -- 复合主键:同一订单里同一产品只许出现一行 PRIMARY KEY (订单号, 产品编号), FOREIGN KEY (订单号) REFERENCES 订单表(订单号), FOREIGN KEY (产品编号) REFERENCES 产品表(产品编号) );

用 Access 的同学注意:Access 的 SQL 语法略有不同,主要三处——

  1. 文本类型用 TEXT(n) 而不是 VARCHAR(n),长文本用 MEMO
  2. 日期类型用 DATETIME 而不是 DATE
  3. 自增主键用 AUTOINCREMENT(如 ID AUTOINCREMENT PRIMARY KEY),外键约束建议在 Access 的"关系"视图里手动拖,比写 SQL 更稳。

其余的主键、外键、复合主键思路完全一样,照搬到 Access 里建表、建关系即可。

3.7 给通义的完整提示词(可直接复制)

把下面这段贴给通义,它就能替你出拆分方案和 SQL。你照着 3.2~3.6 的验收清单核对就行:

我有一张 Excel 销售表,原始字段是:订单号、下单日期、客户、客户电话、客户地址、买的商品(一个格子里写了"编号 名称 数量",可能含多件)、销售员、销售员部门。
请帮我做下面几件事:
1)先判断这张表现在违反了第几范式(1NF/2NF/3NF),说清楚违反在哪;
2)按 1NF → 2NF → 3NF 的顺序,一步一步把它拆成多张规范的小表,每一步说明"拆掉了哪种依赖";
3)给出每张表的表名、主键、字段,以及表与表之间的主外键关系;
4)给一份可直接执行的建表 SQL(MySQL 语法),表名可用中文,字段加中文注释;
5)最后告诉我,如果要在 Access 里实现,语法上要改哪几处。

拿到回答后怎么验收(照着勾)

  • 是否先判断了"卡在哪一范式",而不是一上来就甩 SQL?
  • 拆分时有没有讲清"拆掉的是部分依赖还是传递依赖"?
  • 每张表是否都有主键,跨表的字段是否用外键连上?
  • SQL 能不能建成功(先在小测试库建两张表、插几行试试)?
  • 原来"一个订单多件商品"的信息,拆完有没有丢?

四、原理小结

范式(NF)的本质就四个字——分而治之。每升一级,就消灭一类"数据重复/对不齐"的隐患:1NF 管"一个格子一个值",先把乱糟糟的多值拆平;2NF 管"别只看一半钥匙",把只跟某部分主键相关的信息请出去自成一张表;3NF 管"别绕弯",把"甲决定乙、乙决定丙"这种传递链条打断,让每个非主键字段都只直接听主键的话。一路拆下来,数据各归各表、靠编号(主键/外键)认亲戚,改一处只动一处,重复和矛盾自然就少了。

五、避坑指南

  1. 别一上来就死磕 3NF。 小项目、单人用、数据量小,拆到 2NF 往往就够用了。先让 AI 判断你卡在哪一范式,按需拆,别为"规范"而规范,把简单事搞复杂。
  2. 别在表里存"算出来的"列。 比如"金额 = 数量 × 单价"是算出来的,落库后容易跟明细对不上(改了数量忘了改金额)。要查金额就用查询(SELECT 数量*单价 …)或视图(View,存好的查询当表用)现算,别写死进表。
  3. 主键别用"会变的"字段当钥匙。 客户姓名会重名、会改,产品名也可能改。给每张实体表配一个稳定的编号(客户编号、产品编号、销售员编号)当主键,最稳。
  4. AI 给的 SQL 别直接上生产。 先在测试库建两张表、插几行、跑一条查询验证,确认能建、能查、关系没错,再正式用。AI 偶尔会把字段类型或外键方向写反,肉眼过一遍最保险。
  5. 范式不是越高越好。 做报表、跑统计时常用的"宽表"(把多张表拼成一大张),恰恰是故意反规范化(Denormalization)——牺牲一点规范换查询速度。规范化和性能要按场景取舍,别一刀切。

六、进阶延展

如果"什么是表、怎么在 Access 里建第一张表"还生疏,先回看 B13《设计数据表》 打地基;拆完表想自己写查询把数据捞出来,去 B14《AI 写查询(SQL)不求人》 学怎么跟 AI 要 SQL。

想再上一层、从"能建库"走到"用得溜",接下来的硬菜是:JOIN(多表联查,把拆开的表按关系拼回来)索引(Index,给表加的"目录",让查询飞快)视图(View,把常用查询结果存成一张"虚表"随用随取)——那是 L4 才展开的进阶内容,咱们下回见。