在上一章中,我们看到数据团队与业务团队协作,创建了一个高层次的概念模型,用以表示组织的主要实体及其关系。概念模型有助于在不过度细化的情况下理解数据的整体结构与需求;而建模流程的下一阶段需要更进一步,开发一个可作为蓝图的详细模型,以便推进到物理数据库设计。
要完成逻辑模型,数据团队需要再次与业务领域专家合作,扩展将用于数据库的实体、属性与关系清单,并为每个要素补充数据类型与约束。与概念模型类似,逻辑模型不仅仅是迈向物理模型的跳板——它同样对新建设计与简化既有设计具有双向价值。
逻辑模型比概念模型更细致、更具颗粒度。虽然它不包含物理模型中特定于数据库的实现信息,但通过提供物理数据库无法承载的上下文信息加以补偿。一旦成型,逻辑模型就能为后续在特定数据库上的部署与适配提供基础。
本章将讨论以下主题:
- 识别并补充属性与数据类型
- 为实体关系添加结构性与技术性细节
- 解决多对多(M:M)关系
- 扩展弱实体
- 理解数据库模型中的继承
让我们从上一章的概念模型继续,演进为逻辑模型。
从概念到逻辑的扩展(Expanding from conceptual to logical modeling)
上一章中,我们使用 Kimball 的四步法来构建 bus 矩阵,并据此创建了一个概念模型。支撑 bus 矩阵的信息来自数据团队与业务专家的研讨与讨论,他们阐明了业务的运行模型,并产出了一个功能性产物——概念图。
下图展示了完成时的概念模型:
图 8.1 —— 第 7 章的最终概念图
尽管存在明显的不足(如缺少属性与 Snowflake 特定对象属性),该概念图并未尝试为实体之间的功能性关系(如子类型与多对多关联)添加任何上下文细节。
要找出应添加到各实体中的相关字段,我们需要再次邀请业务领域专家参与。
补充属性(Adding attributes)
维度中最重要的细节是其唯一标识符。它决定了在业务中什么构成实体的唯一实例。典型示例包括:零件的序列号、公司人力的员工编号。各业务领域的专家可以确认这些及其他必要细节。我们以 CUSTOMER 维度为例,识别相关细节并将其纳入模型。
设想我们与销售团队负责人沟通,了解组织如何识别客户及其相关属性。领域专家解释:除常见的姓名、地址外,组织还希望跟踪账户余额并识别客户所属的市场细分。销售团队还说明:客户按其 NATION 属性被分配到不同区域,而该属性维护在单独的 LOCATION 维度中。除了识别属性及其数据类型,我们还了解到:除可选的备注字段外,其余属性均为必填(不可为 NULL) 。
Snowflake 数据类型与 ANSI 兼容性
Snowflake 在表列上支持大多数基础 SQL 数据类型,也支持半结构化与地理空间数据类型。为符合 ANSI SQL 标准,Snowflake 还为许多常见类型提供别名:例如,虽然 Snowflake 以 VARCHAR 存储字符串,但在逻辑建模中可以写成 STRING 或 CHAR,部署时将创建为 VARCHAR。支持的数据类型与别名汇总见官方文档:
docs.snowflake.com/en/sql-refe…
基于销售团队的反馈,我们现在得到一个客户维度的逻辑模型:包含其主键(PK) 、外键(FK) 、数据类型以及必填/可选属性等细节。下图展示了从概念层到逻辑层细节的转变:
图 8.2 —— 从概念建模到逻辑建模
完成对所有既有维度的此类梳理后,我们将把焦点转向:这些维度如何组合以记录事实。
夯实关系(Cementing the relationships)
在维度中确定了属性以及 PK/FK 关系后,我们再次请业务领域专家参与,以充实事实表并确定其粒度。
在概念模型的基础上,我们要确保所有关系都明确了粒度与可选性(必填/可选) ,并让数据团队理解其业务语境。事实表定义中的失误是代价最高且最难补救的,可能带来昂贵的调整,因此务必谨慎。与领域专家合作,确保事实表捕捉到业务操作所记录信息的真实原子粒度,并且对可选性的理解准确无误。
在下面的示例中,物流团队确认:为便于退货与库存追踪,仓库会按供应商对零件进行分隔。简而言之,仓库可以存放来自多个供应商的多个零件。对于不那么显而易见的关系,此时也要核实并记录角色名。得到的逻辑模型如下:
图 8.3 —— 确定事实表粒度
不过,我们尚未定义描述该事实表的度量(measure) 。对于仓库库存,度量可以很简单:在库数量以及每个零件的进货成本(因为不同供应商可能不同)。
下图展示了最终的 INVENTORY 表:包含基数、粒度、角色名与所需度量:
图 8.4 —— 具有粒度与度量的事实表
对模型中的所有事实重复上述过程后,我们就获得了推进到后续设计阶段的完整视图。下一步是识别多对多关系。
多对多关系(Many-to-many relationships)
当两类实体彼此都可以拥有多条对方实例时,就存在 M:M 关系。比如在本组织中:一个供应商可以供应多个零件,而一个零件也可以由多个供应商提供。因此需要第三个实体(库存),也称关联/连接实体(associative/junction entity),来存储并追踪“哪个供应商提供了哪个零件”。
回忆第 2 章的例子:要罗列全部可用超能力,需要一张关联表来表示某个超级英雄的可能超能力:
图 8.5 —— 通过关联表解决 M:M 关系
在当前示例中,并不存在“未解决”的 M:M 关系,因为业务的真实操作要求有一个物理实体(仓库)来存放由多个供应商提供的多个零件的库存。不过,借助菱形符号来刻画这层业务语境,我们可以在超越数据库层的层面表达:库存并非独立存在,它是由供应商向公司采购零件这一交互(procurement)而派生的:
图 8.6 —— 在逻辑图上高亮既有的 M:M 关系
M:M 只是无法孤立存在的实体的一种情形;弱实体是另一种。
弱实体(Weak entities)
弱实体无法仅靠自身属性被唯一标识;它必须将外键(FK)与自身属性组合成主键(PK) 。换言之,弱实体的 PK 中包含 FK;而强实体完全依赖其唯一标识符。
在图上,弱实体以圆角矩形表示,强实体则是直角。但其影响不止于符号语义,还会影响模型如何处理数据变更。以 ORDERS 表为例。
乍看之下,ORDERS 有多个乌鸦脚连接器,似乎是一个关联性的 M:M 表。请注意下图中 ORDERS 与 INVENTORY 的相似之处:
图 8.7 —— 通过 PK 区分弱实体与 M:M
订单是必须被跟踪与审计的业务实体,拥有唯一编码。而 INVENTORY 则没有独立的唯一标识符,给它人为设置一个也不符合业务意义——记录零件 PK、供应商 PK、在库数量即已足够。
与销售团队沟通后我们了解到:订单在不同业务语境下有不同视角。对销售而言,订单是客户与公司的单次交易;但对物流而言,一个订单可能包含多条明细,对应被购买/退回/打折的单件商品。业务团队需要按订单进行引用与分析,而把所有东西混在一张表会让事情变得困难。
解决方案是将订单拆分为强实体(ORDERS)与弱实体(LINEITEM) ,以便存储、分析与维护。具体做法是:将ORDERKEY 作为 ORDERS 的 PK;新建弱实体 LINEITEM,它拥有自己的唯一标识符,并将先前存在的外键列作为其 PK 的一部分,同时 ORDERKEY 作为非 PK 的 FK 属性保留。
这一从“单一弱实体”到“强/弱实体分离”的转变如下图所示:
图 8.8 —— 将弱实体拆分为强实体与弱实体
如此一来,订单可以被快速检阅与计数(一行一单),而行项目的修改与更新则在独立的表中进行。
继承(Inheritance)
有面向对象背景的读者对继承并不陌生——子类从父类“继承”属性。在数据建模中,道理相同,只是称呼变为父类型(supertype)与子类型(subtype) 。子类型与父类型共享公共特征,但还拥有使其与众不同的附加属性。
假设销售团队会根据复购量维护一类忠诚客户。忠诚客户拥有与普通客户相同的属性(甚至共享同一标识符),并额外包含忠诚等级与积分等属性。
在物理层上,结果表 LOYALTY_CUSTOMER 在图上看起来可能像一张普通子表或弱实体:
图 8.9 —— CUSTOMER 与 LOYALTY_CUSTOMER 的物理视图
然而,这并未准确刻画其与 CUSTOMER 的真实关系。使用子类型标记(判别符)才能表达其本质:在 CUSTOMER 父类型与 LOYALTY_CUSTOMER 子类型之间加上判别符,即可补充数据库层无法表达的语境:
图 8.10 —— CUSTOMER 父类型与 LOYALTY_CUSTOMER 子类型的逻辑视图
与图 8.9 相比,图 8.10 传达了如下业务语境:
- CUSTOMER 是父类型/超类型,其属性可应用于其子类型(由判别符符号定义);
- LOYALTY_CUSTOMER 子类型是不完备的(判别符下为单线,而非双线),意味着未来可能新增其他子类型;
- LOYALTY_CUSTOMER 子类型是可包含(inclusive)的(中空圆圈、无 X),因此一个 CUSTOMER 既可以是 LOYALTY_CUSTOMER,也可以是未来将定义的其他类型。
至此,概念到逻辑的过渡完成。下图展示了完成的逻辑模型:
图 8.11 —— 完整的逻辑模型
我们在图 8.1 中起步的概念模型,是对组织数据需求的易懂表达,便于与干系人沟通;它不聚焦数据库的具体实现细节,而强调整体结构与实体间关系。
相对地,逻辑数据库建模则创建一个更细化的(面向未来的)数据库模型,增加结构细节,并以更高的技术粒度表述既有实体与关系。逻辑建模的目标是为实际数据库设计打下基础,同时捕捉那些无法仅靠物理数据库对象来表达的语义细节。
小结(Summary)
本章延续建模之旅:在上一章概念设计的基础上,补充结构性与技术性细节,将其扩展为逻辑模型,使之与后续章节将要创建与部署的物理模型更加接近。
我们继续与业务领域专家协作,明确用于承载关键主数据与交易数据的属性;并为每个业务实体定义了其标识符(Identifiers) 、属性与度量(Attributes & Measures)以及数据类型。
在属性就位后,我们审视了实体之间的关系,以把握关联的细微差异,从而按业务需求进行架构设计,并简化后续维护。
- 首先识别了多对多(M:M)关系。这类关系需要关联表(associative table)来记录实体间的交互,但关联表本身不被视为独立实体,因此没有业务键(Business Key) ,而是依赖相关实体的外键(FK) 。
- 随后检查了弱实体(无法仅凭自身属性唯一标识)。根据业务需要,弱实体可能增加维护与分析难度,可通过将其拆分为独立的强实体来化解问题。
- 最后讨论了继承在数据库设计中的体现,即**父类型/子类型(supertype/subtype)关系。通过判别符(discriminator)**识别子类型并刻画其业务语境,有助于将其与普通子表区分开来。
接下来,我们将把逻辑模型转换为 Snowflake 物理数据库。但在进入物理设计之前,还需要熟悉范式化(Normalization)的概念,并掌握在 Snowflake 中构建与维护物理对象的最佳实践——这些内容将于后续章节展开。