事实、身份与数据模型
区分事实、对象身份、关系和派生值,建立能够变化和查询的数据表示。
从需要保存的事实开始
数据模型规定哪些信息可以表达、用什么结构表达,以及哪些关系具有明确含义。可以先把一个事实写成句子,例如“记录 r 的标题是 t”“对象 a 引用对象 b”“事件 e 在时间 u 发生”。这些句子中的主体、关系和值,决定随后需要哪些记录和字段。
同一字段名可能承担不同事实。“当前名称”允许随对象变化,“发生事件时的名称”保存历史。当后者用于独立历史记录时,它与当前名称各有时间条件;把它们合并会丢失历史,把可推导的当前名称复制到许多记录又会产生同步问题。
身份与属性分开判断
身份用于指认同一个对象。属性描述对象在某个范围或时点的状态。两个对象可以同名,同一个对象可以改名,因此名称只有满足所需的唯一性、稳定性和范围条件时才适合作为键。
键是能唯一指认记录的一组属性。单列键和组合键都需要明确定义唯一性范围。使用系统生成编号时,仍需声明业务上的唯一规则;编号成功生成并不能阻止两条业务重复记录。跨系统身份还需要来源或命名空间,例如 (source, external_id),单独的外部编号可能只在来源内部唯一。
关系模型怎样表达联系
关系模型把同一类属性组合表示为关系中的元组。工程上的 SQL 表还具有 NULL、重复行等实现规则,不能把数学集合的全部性质直接套到 SQL 查询。
一对多关系通常在“多”的一侧保存另一方的键。多对多关系需要单独表示配对,例如 links(left_id, right_id);若配对还能有时间、权重或角色,这些属性属于联系本身。关系的方向、基数和可选性应先于表结构确定,否则一列外键无法表达“一个对象同时关联多项”的要求。
分解重复与保留独立历史
如果同一事实随着每次事件重复保存,修改它需要同时更新许多位置,插入或删除事件还可能连带丢失对象事实。把对象状态与事件记录分开,可以减少这种更新、插入和删除异常。
规范化检查属性依赖。函数依赖 X → Y 表示在所声明关系中,相同 X 必须对应相同 Y。分解应尽量保持需要的依赖,并能通过连接恢复原来允许的记录组合。把复合值拆成字符并不自动得到合理模型;“原子值”依赖模型允许的操作和属性域。
派生信息可以出于读取代价而保存,例如聚合计数或搜索索引。这时应明确原始事实、更新触发和重建办法。历史快照保存独立的过去事实,派生缓存保存可以重新计算的信息,维护责任不同。
不同表示如何取舍
| 表示 | 主要操作 | 需要核对的边界 |
|---|---|---|
| 键值 | 通过键取得值 | 值内部查询、跨键约束与批量原子性 |
| 关系 | 按条件筛选、连接与聚合 | 属性依赖、约束、NULL 和重复语义 |
| 文档 | 取得或改变一棵结构化内容 | 嵌套身份、部分更新、跨文档关系 |
| 图 | 沿节点和边追踪联系 | 边属性、遍历条件与一致性 |
这些名称表示组织和操作取向。某个产品可能同时提供多种表示;具体性能要由操作、数据规模和实现判断。模型选择需要能表达必须保存的事实,也需要支撑实际查询和变化。
依据与范围
本页的例子是教学模型。关系约束的具体实现参见 PostgreSQL 官方 Data Definition 和 Constraints。一般化边界通过区分身份、事实时点和派生信息展开,未据此声称所有数据模型均已完整介绍。
最后更新于