本节摘要:字段类型决定数据的生存规则,规范化的目标是让每个事实只在一个地方存储。本节给出 Access 常用类型的选型口诀、三大范式的通俗版解释,以及"该反规范化时就反"的工程判断。
接上节,概念统一了,现在为华彩商贸设计真正的表。这一节的操作密度开始上升,建议边读边在软件里照做。
Access 的类型菜单有十余项,日常真正高频使用的其实就六七种。挑出最容易纠结的几组做对比:
| 你要存的东西 | 推荐类型 | 理由与备注 |
|---|---|---|
| 名称、编号、电话号码 | 短文本 | 电话是文本不是数字——不参与运算且有前导零 |
| 详细地址、备注说明 | 长文本 | 超过 255 字符或需要换行时用 |
| 数量、单价、金额 | 数字(双精度)或货币 | 金额务必用货币型,避免二进制浮点误差累积 |
| 日期与时间 | 日期/时间 | 一列只放一个时刻;"上午或下午"这类模糊值另建说明列 |
| 是否已完成 | 是/否 | 三态(未定)时改用数字并约定含义 |
| 归档照片、扫描件 | 附件 | 比嵌文件路径可靠,随库走随库备份 |
| 仅在客户端计算的展示列 | 计算字段 | 引擎自动维护结果,不必手工同步 |
新手最常见的三类误选值得点名:其一,把电话存成数字导致丢掉前导零还可能出现科学计数法;其二,金额存成普通小数后对账总差几分钱——货币类型内部按整数分单位运算,天然免疫这种漂移;其三,为了省事把"备注"当垃圾桶,什么都往里塞,日后想统计连列都拆不出来。
先看反例。华彩的原始 Excel 里有一张客户订单流水大宽表,每行既含订单信息又重复抄写客户的地址、联系人:
订单日期 客户名称 客户地址 商品 数量 单价 2026-03-02 恒信文具 幸福路12号 A4复印纸 50 18.5 2026-03-05 恒信文具 幸福路12号 订书机 10 12
恒信文具搬了办公室之后会发生什么?历史几百行里的旧地址要么全部更新(漏一行错一行),要么留着污染通讯录。这就是更新异常。除此之外还有两类病:插入异常(还没下过单的客户在订单表里没有容身之处)、删除异常(删掉最后一笔订单,把这个客户的全部资料也连带抹掉了)。
药方是逐步拆表,教科书叫范式,我们说人话:
第一步(第一范式):每个格子只放一个值。"商品一 / 商品二 / 商品三"三列的电话分期式布局不合格,拆成一行一件商品的明细行。
第二步(第二范式):非主键字段必须完整依赖整个主键。订单明细表的主键是"订单 + 商品行",商品名称只依赖"商品",所以它不属于明细表,该住在货品表里靠编号引用。
第三步(第三范式):非主键字段之间不能互相传递依赖。"客户等级"决定"信用额度",两者若同时塞进订单表,额度规则一变就要全表刷数——它们应该整体搬家到客户表。
范式不是越多越光荣。实际交付里常见两处合理松动:一是报表频繁要"客户名称 + 当月合计",每次跑三表联接嫌慢,可以接受在汇总区留一份冗余快照并注明刷新时点;二是下拉选项类的小字典(比如十来种结算方式),有些人懒得单独建表就用文本框加查阅列表凑合,数据量小时无伤大雅。
判断准则只有一条:冗余必须是有名有姓的决定,不能是无知的结果。写进数据字典,注明谁冗余、为什么、何时刷新(数据字典怎么做见 2.4),将来接手的人才能理解这份宽容。
结合以上原则,第一批表的字段草案如下(节选关键列):
客户表 ID自动编号 | 客户名称 短文本 必填 | 结算方式 短文本 查阅列表 | 备注 长文本 货品表 ID自动编号 | 货品编码 短文本 唯一索引 | 品名 规格建议长文本? 否 用短文本 | 单价 货币 订单表 ID自动编号 | 业务单号 短文本 | 下单日期 日期 默认当天 | 客户ID 数字 外键 订单明细 ID自动编号 | 订单ID 数字 外键 | 货品ID 数字 外键 | 数量 数字 整型 | 成交单价 货币 | 小计 计算字段 单价乘数量
注意两个细节:成交单价独立于货品表的单价列——同一批纸三月卖十八元五月促销十六元,历史订单必须凝固当时的成交价;小计交给计算字段而不是手工填写,杜绝乘错账。字段的必填、默认值、有效性规则这三件小事在设计视图里顺手设置完,比事后补漏便宜十倍。
有效性规则值得单独示范一下,因为它把"域约束"变成了可以直接写条件表达式的关卡:
字段级有效性规则示例(在表设计视图的字段属性里填写) 折扣 Between 0 And 1 有效性文本:折扣须在0到1之间 数量 >0 拒绝零与负数出库 下单日期 <=Date() 不许出现未来的订单 手机号 Like "1##########" 十一位且以1开头才放行
规则旁边配套写上"有效性文本",用户看到的是人话提示而不是干巴巴的报错编号。三组高频追问也一并回答掉。问:金额为什么不用普通数字而要货币型? 货币型是定点小数,精确到万分之一元;普通数字默认是二进制浮点,0.1 加不上 0.2 这类舍入误差在对账场景就是事故。问:计算字段既然能存结果,能不能什么都用它? 不行——计算列随行落盘,参与统计前要想清楚它是否随来源变化刷新;多数汇总更适合放到查询里现算。问:什么时候该用附件字段而不是短文本存路径? 附件适合数量少、体积小的凭证照片;大图、成目录的材料仍建议文件系统保管加编号引用,别让数据库长成一个文件仓库。
Access 有个颇具争议的特性值得表态:在设计视图里给短文本字段挂"查阅向导",可以让它在数据表视图里直接显示下拉值。新手爱上它的所见即所得,老手痛恨它的两面性——同一列在数据表里显示的是"已发货",实际存的是编号 3,写 SQL 或排错的人稍不留神就被显示层骗过。
我们的折中约定:界面层的下拉一律放在窗体组合框(上一节的主角们),表级查阅字段只允许用在极少数一次性小字典上,且必须开启"限于列表"。理由回到第 1 章的价值观——约束要诚实可见,藏在类型系统里的翻译魔法省下的那点点击,远不够偿还歧义带来的排查时间。
有了好表,接下来要让表与表咬合成有机整体——关系与参照完整性登场。