Snowflake 数据建模——逻辑建模实战

67 阅读11分钟

在上一章中,我们看到数据团队与业务团队协作,创建了一个高层次的概念模型,用以表示组织的主要实体及其关系。概念模型有助于在不过度细化的情况下理解数据的整体结构与需求;而建模流程的下一阶段需要更进一步,开发一个可作为蓝图详细模型,以便推进到物理数据库设计。

要完成逻辑模型,数据团队需要再次与业务领域专家合作,扩展将用于数据库的实体、属性与关系清单,并为每个要素补充数据类型约束。与概念模型类似,逻辑模型不仅仅是迈向物理模型的跳板——它同样对新建设计简化既有设计具有双向价值。

逻辑模型比概念模型更细致、更具颗粒度。虽然它不包含物理模型中特定于数据库的实现信息,但通过提供物理数据库无法承载的上下文信息加以补偿。一旦成型,逻辑模型就能为后续在特定数据库上的部署与适配提供基础。

本章将讨论以下主题:

  • 识别并补充属性数据类型
  • 实体关系添加结构性与技术性细节
  • 解决多对多(M:M)关系
  • 扩展弱实体
  • 理解数据库模型中的继承

让我们从上一章的概念模型继续,演进为逻辑模型

从概念到逻辑的扩展(Expanding from conceptual to logical modeling)

上一章中,我们使用 Kimball 的四步法来构建 bus 矩阵,并据此创建了一个概念模型。支撑 bus 矩阵的信息来自数据团队与业务专家的研讨与讨论,他们阐明了业务的运行模型,并产出了一个功能性产物——概念图

下图展示了完成时的概念模型:

image.png

图 8.1 —— 第 7 章的最终概念图

尽管存在明显的不足(如缺少属性Snowflake 特定对象属性),该概念图并未尝试为实体之间的功能性关系(如子类型与多对多关联)添加任何上下文细节。

要找出应添加到各实体中的相关字段,我们需要再次邀请业务领域专家参与。

补充属性(Adding attributes)

维度中最重要的细节是其唯一标识符。它决定了在业务中什么构成实体的唯一实例。典型示例包括:零件的序列号、公司人力的员工编号。各业务领域的专家可以确认这些及其他必要细节。我们以 CUSTOMER 维度为例,识别相关细节并将其纳入模型。

设想我们与销售团队负责人沟通,了解组织如何识别客户及其相关属性。领域专家解释:除常见的姓名、地址外,组织还希望跟踪账户余额并识别客户所属的市场细分。销售团队还说明:客户按其 NATION 属性被分配到不同区域,而该属性维护在单独的 LOCATION 维度中。除了识别属性及其数据类型,我们还了解到:除可选的备注字段外,其余属性均为必填(不可为 NULL)

Snowflake 数据类型与 ANSI 兼容性
Snowflake 在表列上支持大多数基础 SQL 数据类型,也支持半结构化地理空间数据类型。为符合 ANSI SQL 标准,Snowflake 还为许多常见类型提供别名:例如,虽然 Snowflake 以 VARCHAR 存储字符串,但在逻辑建模中可以写成 STRINGCHAR,部署时将创建为 VARCHAR。支持的数据类型与别名汇总见官方文档:
docs.snowflake.com/en/sql-refe…

基于销售团队的反馈,我们现在得到一个客户维度的逻辑模型:包含其主键(PK)外键(FK)数据类型以及必填/可选属性等细节。下图展示了从概念层逻辑层细节的转变:

image.png

图 8.2 —— 从概念建模到逻辑建模

完成对所有既有维度的此类梳理后,我们将把焦点转向:这些维度如何组合以记录事实

夯实关系(Cementing the relationships)

在维度中确定了属性以及 PK/FK 关系后,我们再次请业务领域专家参与,以充实事实表并确定其粒度

在概念模型的基础上,我们要确保所有关系都明确了粒度可选性(必填/可选) ,并让数据团队理解其业务语境。事实表定义中的失误是代价最高且最难补救的,可能带来昂贵的调整,因此务必谨慎。与领域专家合作,确保事实表捕捉到业务操作所记录信息的真实原子粒度,并且对可选性的理解准确无误

在下面的示例中,物流团队确认:为便于退货库存追踪,仓库会按供应商零件进行分隔。简而言之,仓库可以存放来自多个供应商的多个零件。对于不那么显而易见的关系,此时也要核实并记录角色名。得到的逻辑模型如下:

image.png

图 8.3 —— 确定事实表粒度

不过,我们尚未定义描述该事实表的度量(measure) 。对于仓库库存,度量可以很简单:在库数量以及每个零件的进货成本(因为不同供应商可能不同)。

下图展示了最终的 INVENTORY 表:包含基数粒度角色名所需度量

image.png

图 8.4 —— 具有粒度与度量的事实表

对模型中的所有事实重复上述过程后,我们就获得了推进到后续设计阶段的完整视图。下一步是识别多对多关系

多对多关系(Many-to-many relationships)

当两类实体彼此都可以拥有多条对方实例时,就存在 M:M 关系。比如在本组织中:一个供应商可以供应多个零件,而一个零件也可以由多个供应商提供。因此需要第三个实体(库存),也称关联/连接实体(associative/junction entity),来存储并追踪“哪个供应商提供了哪个零件”。

回忆第 2 章的例子:要罗列全部可用超能力,需要一张关联表来表示某个超级英雄的可能超能力:

image.png

图 8.5 —— 通过关联表解决 M:M 关系

在当前示例中,并不存在“未解决”的 M:M 关系,因为业务的真实操作要求有一个物理实体(仓库)来存放由多个供应商提供的多个零件的库存。不过,借助菱形符号来刻画这层业务语境,我们可以在超越数据库层的层面表达:库存并非独立存在,它是由供应商向公司采购零件这一交互(procurement)而派生的:

image.png

图 8.6 —— 在逻辑图上高亮既有的 M:M 关系

M:M 只是无法孤立存在的实体的一种情形;弱实体是另一种。

弱实体(Weak entities)

弱实体无法仅靠自身属性被唯一标识;它必须将外键(FK)与自身属性组合主键(PK) 。换言之,弱实体的 PK 中包含 FK;而强实体完全依赖其唯一标识符

在图上,弱实体以圆角矩形表示,强实体则是直角。但其影响不止于符号语义,还会影响模型如何处理数据变更。以 ORDERS 表为例。

乍看之下,ORDERS 有多个乌鸦脚连接器,似乎是一个关联性的 M:M 表。请注意下图中 ORDERSINVENTORY 的相似之处:

image.png

图 8.7 —— 通过 PK 区分弱实体与 M:M

订单是必须被跟踪与审计的业务实体,拥有唯一编码。而 INVENTORY 则没有独立的唯一标识符,给它人为设置一个也不符合业务意义——记录零件 PK、供应商 PK、在库数量即已足够。

与销售团队沟通后我们了解到:订单在不同业务语境下有不同视角。对销售而言,订单是客户与公司的单次交易;但对物流而言,一个订单可能包含多条明细,对应被购买/退回/打折的单件商品。业务团队需要按订单进行引用与分析,而把所有东西混在一张表会让事情变得困难

解决方案是将订单拆分为强实体(ORDERS)弱实体(LINEITEM) ,以便存储、分析与维护。具体做法是:将ORDERKEY 作为 ORDERS 的 PK;新建弱实体 LINEITEM,它拥有自己的唯一标识符,并将先前存在的外键列作为其 PK 的一部分,同时 ORDERKEY 作为非 PK 的 FK 属性保留。

这一从“单一弱实体”到“强/弱实体分离”的转变如下图所示:

image.png

图 8.8 —— 将弱实体拆分为强实体与弱实体

如此一来,订单可以被快速检阅与计数(一行一单),而行项目的修改与更新则在独立的表中进行。

继承(Inheritance)

有面向对象背景的读者对继承并不陌生——子类从父类“继承”属性。在数据建模中,道理相同,只是称呼变为父类型(supertype)子类型(subtype)子类型父类型共享公共特征,但还拥有使其与众不同的附加属性

假设销售团队会根据复购量维护一类忠诚客户。忠诚客户拥有与普通客户相同的属性(甚至共享同一标识符),并额外包含忠诚等级积分等属性。

物理层上,结果表 LOYALTY_CUSTOMER 在图上看起来可能像一张普通子表弱实体

image.png

图 8.9 —— CUSTOMER 与 LOYALTY_CUSTOMER 的物理视图

然而,这并未准确刻画其与 CUSTOMER 的真实关系。使用子类型标记(判别符)才能表达其本质:在 CUSTOMER 父类型与 LOYALTY_CUSTOMER 子类型之间加上判别符,即可补充数据库层无法表达的语境

image.png

图 8.10 —— CUSTOMER 父类型与 LOYALTY_CUSTOMER 子类型的逻辑视图

与图 8.9 相比,图 8.10 传达了如下业务语境:

  • CUSTOMER父类型/超类型,其属性可应用于其子类型(由判别符符号定义);
  • LOYALTY_CUSTOMER 子类型是不完备的(判别符下为单线,而非双线),意味着未来可能新增其他子类型;
  • LOYALTY_CUSTOMER 子类型是可包含(inclusive)的(中空圆圈、无 X),因此一个 CUSTOMER 既可以是 LOYALTY_CUSTOMER,也可以是未来将定义的其他类型

至此,概念到逻辑的过渡完成。下图展示了完成的逻辑模型

image.png

图 8.11 —— 完整的逻辑模型

我们在图 8.1 中起步的概念模型,是对组织数据需求的易懂表达,便于与干系人沟通;它不聚焦数据库的具体实现细节,而强调整体结构实体间关系

相对地,逻辑数据库建模则创建一个更细化的(面向未来的)数据库模型,增加结构细节,并以更高的技术粒度表述既有实体与关系。逻辑建模的目标是为实际数据库设计打下基础,同时捕捉那些无法仅靠物理数据库对象来表达的语义细节。

小结(Summary)

本章延续建模之旅:在上一章概念设计的基础上,补充结构性技术性细节,将其扩展为逻辑模型,使之与后续章节将要创建与部署的物理模型更加接近。

我们继续与业务领域专家协作,明确用于承载关键主数据交易数据属性;并为每个业务实体定义了其标识符(Identifiers)属性与度量(Attributes & Measures)以及数据类型

在属性就位后,我们审视了实体之间的关系,以把握关联的细微差异,从而按业务需求进行架构设计,并简化后续维护。

  • 首先识别了多对多(M:M)关系。这类关系需要关联表(associative table)来记录实体间的交互,但关联表本身不被视为独立实体,因此没有业务键(Business Key) ,而是依赖相关实体的外键(FK)
  • 随后检查了弱实体(无法仅凭自身属性唯一标识)。根据业务需要,弱实体可能增加维护与分析难度,可通过将其拆分为独立的强实体来化解问题。
  • 最后讨论了继承在数据库设计中的体现,即**父类型/子类型(supertype/subtype)关系。通过判别符(discriminator)**识别子类型并刻画其业务语境,有助于将其与普通子表区分开来。

接下来,我们将把逻辑模型转换为 Snowflake 物理数据库。但在进入物理设计之前,还需要熟悉范式化(Normalization)的概念,并掌握在 Snowflake 中构建与维护物理对象的最佳实践——这些内容将于后续章节展开。