Skip to content

画出关系

另有 28 种语言版本

English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · ไทย · Nederlands · العربية · עברית · Українська · हिन्दी · Русский · Svenska · Dansk · Norsk bokmål · Suomi · Español (México) · Français (Canada) · Deutsch (Schweiz)

A card model of a region from above: one distribution center joined by red threads to many small stores, and a second center with its own.
图片由 AI 生成

第 2 个模块,共 6 个,属于 Data Architect 课程第 2 章为含义建模数据。每个模块都可以单独阅读。 上一篇:聆听名词 · 下一篇:构建逻辑模型

**本模块回答的问题:**如何把候选实体变成一个概念数据模型?

名词已经上了白板。现在我想了解的是方框之间的连线,因为一堆实体还不算真正的数据模型。很多含义就存在于关系之中。一个零售合作伙伴运营着若干配送中心。一份装货清单列出若干配送。一次配送送达一位收货人。一个配送小组驾驶一辆卡车。每一句陈述都告诉我关于业务的一些东西,每一句也都给了我另一个要问的问题。

我仍然不想谈 Salesforce。我不是在问这应该是查找关系还是主从关系,不是在考虑 Salesforce 联接对象(Junction Object),也不是在决定父记录被删除时会发生什么。我是在努力理解业务上的关系。

拿**“一个零售合作伙伴运营着若干配送中心”**来说。听起来很简单。但一个零售合作伙伴有一个配送中心还是多个?在 Steve Trucking,每个零售合作伙伴在 Steve Trucking 服务的每个区域都运营着一个区域配送中心,所以是多个。一个配送中心可以属于多个零售合作伙伴吗?配送中心本身是否重要,有自己的员工和历史,还是只是一个地址?如果某个零售合作伙伴离开了,我们还在意它的配送中心吗?

这些不是 Salesforce 问题,而是业务问题,而答案可能彻底改变模型。

在深入之前,我想先给你一套从现在起每张图都会用到的语言。它很小。两个方框之间的一条线表示这两样东西相关,线两端的标记表示有多少。

从一条普通的线开始,没有任何标记。这是我在白板上的起点:我知道这两个名词相关,只是还没问怎么相关。然后线的两端加上标记。横穿线条的一道短竖线,也就是那个十字,表示一。鸦爪,也就是像鸟爪一样分成三叉的那一端,表示多。在本模块后面,你会看到第一个标记旁边出现第二个标记,一道竖线或一个圆圈,它回答的是另一个问题:可以没有吗?

要记住的唯一规则:**每个标记都在它所接触的方框那一端来读。**离方框最近的标记表示最多可以有多少;往外的下一个标记表示最少有多少。

读懂连线 本课程图形语言的图例。一条两端没有标记的普通的线,表示两者有关联,但数量还没定。线端的一道竖线表示一;鸦爪表示多。加上第二个表示可选性的标记:两道竖线表示恰好一个;一个圆圈加一道竖线表示零个或一个;一道竖线加一个鸦爪表示一个或多个;一个圆圈加一个鸦爪表示零个或多个。每个标记都在它接触的方框处读。 起点:一条普通的线 普通的线 这两者有关联;数量还没定 然后:一还是多 一个 一道竖线(横穿线):所接触方框的一个 多个 鸦爪:所接触方框的多个 然后:可以是零吗? 恰好一个 两道竖线:至少一个,至多一个 零个或一个 一个圆圈,再一道竖线:可能还不存在 一个或多个 一道竖线,再一个鸦爪:至少一个 零个或多个 一个圆圈,再一个鸦爪:没有也可以 每一端都在它接触的方框处读:离方框最近的标记是最多可以有多少;往外的下一个标记是最少。 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

在讲 Steve Trucking 之前,先回到模块 1 里的狗。我们有五个方框:主人、繁育者、狗、品种和技能。现在我要问它们之间的连线。

**一个还是多个?**一位主人可以有不止一只狗;Ana 就有两只。一只狗只有一位主人,至少在我们的模型里是这样。这就是一对多:主人一端画一道竖线,狗一端画鸦爪。繁育者也是同样的形状:一个繁育者繁育很多只狗。

**可以没有吗?**Max 来自收容所,所以他根本没有繁育者。繁育者一端加一个圆圈:一只狗有零个或一个繁育者。不过每只狗都有主人,所以那一端再加一道竖线:恰好一个。

**多对多?**一只狗会很多项技能,而一项技能,比如坐下,有很多只狗会。这就是多对多,而这正是我去寻找隐藏概念的地方。关于这只狗会这项技能,有没有什么要说的?她做得有多好,什么时候学会的。这个关系有它自己的事实,所以它成了一个独立的事物:狗狗技能(Dog Trick),狗的名字放在前面,因为驱动它的是狗。

**每条线是什么意思?**主人拥有一只狗。繁育者繁育它。一只狗会它的技能。给连线起好名字,任何人都能把模型读回给你听。

现在,还是随时间变化?有没有人在意 Bella 今天坐下做得有多好,或者她是怎么练到这一步的?如果训练师每隔几个月检查她一次,每次检查就是一条独立的记录:一条技能评估(Trick Assessment),带有日期、熟练程度和检查人,归属于它所属的那条狗狗技能。即使是一个已经变成事物的关系,也可以有它自己的历史。

品种我暂时不连线。它仍然是模块 1 留下的那个问题:是一个独立的方框,还是狗身上的一个词,模块 4 会解决它。

带有关系的狗狗模型 七个方框。一位主人拥有一只或多只狗,每只狗恰好有一位主人。一位繁育者繁育一只或多只狗,一只狗有零位或一位繁育者。一只狗会零项或多项狗狗技能,一项技能见于零条或多条狗狗技能;每条狗狗技能对应一只狗和一项技能。每条狗狗技能在零次或多次技能评估中接受评估。品种单独存在,尚未连线。 拥有 繁育 会 见于 评估于 主人 繁育者 狗 品种 狗狗技能 技能 技能评估 恰好一个 零个或一个 一个或多个 零个或多个 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

这些问题中的每一个,放到真实的业务上都同样适用。现在我们拿它们来问 Steve Trucking。

关于一个关系,我最先想了解的事情之一是它的基数。这是数据库术语;问题本身要简单得多:**这边的多少个可以与那边的多少个相关?**在概念层次上,有三种基本模式。

这是整个过程中重要的一步:拿两个不同的名词,理解它们之间的关系。在大多数情况下,它是一对多或多对一。只有在少数情况下,你才会碰到一对一。

一对多无处不在。一个零售合作伙伴有多个配送中心。一份装货清单列出多次配送。一条路线有大约十个停靠点。听到这样的话,我就开始画线。

现在这条线有了标记。每一端都说明它所接触的方框有多少:竖线表示一,鸦爪表示多。从每个方框出发去读:一个零售合作伙伴有多个配送中心。

基本连接符:一对多 两组方框,每组用一条基本鸦爪表示法的连线相连。一个零售合作伙伴运营多个配送中心:零售合作伙伴一端是一道竖线,配送中心一端是鸦爪。一份装货清单列出多次配送,标记方式相同。每一端都用文字作了说明。 一个零售合作伙伴 多个配送中心 运营 一个装货清单 多个配送 列出 零售合作伙伴 配送中心 装货清单 配送 一道竖线表示一,鸦爪表示多。每一端都从它接触的方框读起。 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

一对一关系是存在的,但很少见,一旦发现,我会问一些问题。这真的是两个不同的名词,还是同一个?如果是不同的,它们之间的一对一关系是什么?它会永远是一对一,还是只是现在一对一?这些都是设计数据模型时的关键问题。在任何一个早上,配送小组和卡车看起来都是一对一的:两个人,一辆卡车,一条路线。但放到一个月里看就不是了,而这个模型里最有意思的关系正藏在那里。

多对多关系特别有意思,因为它们常常告诉我们业务中还有另一个概念。一个配送小组会在一段时间里开不止一辆卡车吗?会。一辆卡车会由不止一个配送小组驾驶吗?在它的整个使用期内,当然会。所以配送小组和卡车是多对多关系。但现在我想了解更多。业务方是否在意某个配送小组什么时候开过某辆卡车?为哪条路线,在哪一天?是谁做的分配?如果关系本身承载着我们在意的信息,我们就发现了另一个业务概念:配送小组分配(Crew Assignment)。

这是一个重要的建模习惯。有时最有意思的东西并不是我们最初听到的某个名词,而是藏在两个名词之间的关系里。

这是我希望管理员开始换个角度思考的地方之一。在 Salesforce 里,我们习惯谈论对象和字段,所以很容易把方框当成重要的部分,把连线当成管道。但关系本身可以有业务含义。

想想一次配送和它所载的产品。一次配送可以载好几种产品,比如一台洗衣机和一台烘干机。一种产品,比如同一型号的冰箱,会出现在成千上万次配送中。这很简单。但如果我需要知道这次配送里有几台这种产品、是否要安装、是否要把旧的拉走呢?我感兴趣的就不再只是配送和产品之间的关系了。我感兴趣的是一个配送项目(Delivery Item),而这个概念值得拥有自己的实体。

如果你有 Salesforce 背景,你可能已经在想:“这是个联接对象。”你没错。但我不想从那里开始。**配送项目先是一个业务概念,然后才是一个 Salesforce 联接对象。**业务在先,实现在后。

对于这类关系实体,不管最后在哪里构建,我都有几个习惯。

给它起名字。如果业务方已经有一个词,就用他们的。如果没有,就把两个父实体的名字拼在一起。想想订单和产品。它们之间的东西是 Order Item(订单项目):订单,以及订单上的项目。它同时是两个父实体的子实体,在 Salesforce 里最后可能成为一个 Salesforce 联接对象。但哪个名字放在前面:Item Order,还是 Order Item?看看每个父实体是哪类数据。产品是参考数据;订单是交易数据,而驱动它的是订单。所以是 Order Item,或者 Order Product,就像 Salesforce 有 Opportunity Product(业务机会产品)一样。在 Steve Trucking,它是配送项目(Delivery Item)。起驱动作用的名字放在前面,这样在你将来看到的每一个列表里,这条记录都会排在它的所有者旁边。

**给它自己的键。**人们很容易说配送项目由配送加产品来识别,然后就此打住。那是一个复合键,而复合键要难处理得多:每个指向这条记录的系统都得带着两半。给关系实体一个自己的标识符,把配送加产品作为一条关于唯一性的规则,而不是作为身份。

两个父级,一个子级:给它自己的键 两个父级和一个子级。一次配送(记录 ID 为配送 ID;配送日期、状态)包含多个配送项目。一个产品(记录 ID 为产品 ID;名称、型号)出现在多个配送项目上。配送项目位于两者之间:它有自己的记录 ID,即配送项目 ID,并带有两个父级的配送 ID 和产品 ID,外加数量。下方的说明:配送 ID 加产品 ID 是一条关于唯一性的规则,而不是记录 ID。 交易数据 参考数据 包含 出现在 配送 配送 ID 记录 ID 配送日期 状态 配送项目 配送项目 ID 记录 ID 配送 ID 产品 ID 数量 产品 产品 ID 记录 ID 名称 型号 配送 ID + 产品 ID:一条关于唯一性的规则,而不是记录 ID。 一个 多个 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

注意它上面没有什么:序列号。数量和序列号不属于同一条记录。数量统计的是某样东西的若干件;序列号识别的是某一件特定的东西。如果业务需要序列号,每一件就成为自己的一条记录,五台冰箱就是五条记录。我想不出有什么理由让配送项目下面再挂一个配送项目明细。要么是这个,要么是那个:带数量的汇总,或者每件一条记录。这里用的是数量。

**留意想要历史的属性。**有些关系实体一旦创建就定下来了:这次配送里的洗衣机,就一直是这次配送里的洗衣机。另一些则不断变化。一个配送小组这周开 117 号卡车,下周这辆卡车就归了另一个小组。当关系会变化,而业务又在意它过去是什么样时,这个关系实体就需要日期,而配送小组分配正是需要留意的那一个。尽早问业务方在意的是现在还是随时间变化;我们马上会回到这个话题。

光说“一对多”不足以理解一个关系。我还想知道这个关系意味着什么。

一次配送有一个地点。哪一个?它出发的配送中心?还是它送达的收货人家里?这两者不能互换。两个模型可以有完全相同的方框和完全相同的基数,而连线却可以意味着完全不同的东西。这就是为什么好的模型不只是用线连起来的方框。**连线需要名字。**如果我把图交给另一位架构师,他们不应该需要去猜一个关系是什么意思;如果我拿给业务方看,它应该使用他们认得的语言。

连线需要名字 两个面板,每个面板里都有一个地点方框位于一个配送方框上方,两者之间有两条连线,每条线都标为一个地点对多次配送。在左边的面板“线上没有名字”中,两条线都没有标签,看起来一模一样。在右边的面板“有名字的线”中,一条线标为“出发自”,另一条标为“送达”。同样的方框,同样的基数:只有名字能告诉你哪条线是哪条。 线上没有名字 有名字的线 出发自 送达 地点 配送 地点 配送 同样的方框,同样的基数。只有名字能告诉你哪条线是哪条。 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

“有多少?”里还藏着另一个问题:这个关系是必需的吗?一次配送能在被排上路线之前存在吗?必须能:装货清单在配送前一两天到达,而路线要到前一天晚上才最终确定。一次配送能在没有零售合作伙伴的情况下存在吗?不能;每一次配送都来自某个合作伙伴的装货清单。这就是可选性。

同样,不要直接跳到 Salesforce 的必填复选框。说“每次配送都必须有一个零售合作伙伴”和说“Salesforce 应该在创建记录时要求填写零售合作伙伴字段”是有区别的。它们最后可能是同一种实现,但不是同一句话。前者是一条业务规则;后者是执行它的一种方式。作为架构师,我们要先理解前者,再决定后者。

在图上,可选性是每一端的第二个标记。离方框最近的标记表示最多可以有多少:竖线表示一,鸦爪表示多。往外的下一个标记表示最少有多少:又一道竖线表示至少一个,一个圆圈表示可以是零。所以每次配送都恰好在一份装货清单上,而一份装货清单列出一次或多次配送。一次配送位于零个或一个停靠点,在排上路线之前没有停靠点,而一个停靠点卸下零次或多次配送。

读懂每一端的两个标记 一排三个方框:装货清单、配送、停靠点。一份装货清单列出一次或多次配送,每次配送恰好在一份装货清单上:装货清单一端是两道竖线,配送一端是一个鸦爪和一道竖线。一个停靠点卸下零次或多次配送,每次配送位于零个或一个停靠点:配送一端是一个圆圈和一个鸦爪,停靠点一端是一个圆圈和一道竖线。离方框最近的是最多;往外的下一个标记是最少。 恰好一个装货清单 一个或多个配送 列出 零个或一个停靠点 零个或多个配送 卸下 装货清单 配送 停靠点 离方框最近的:最多(一或多)。往外的下一个标记:最少(竖线是至少一个,圆圈是零)。 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

有一个问题我经常问:你说的是现在,还是随时间变化?

一个配送小组驾驶一辆卡车。是现在吗?是为了今天的路线吗?还是在这两个人共事的整个期间?这会产生不同的模型。如果业务方只在意一个小组今天开的是哪辆卡车,模型可以保持简单。如果需要知道一个小组开过的每一辆卡车、什么时候、在哪条路线上,我们就需要历史,而我们发现的那个配送小组分配突然就变得非常重要了。

配送小组分配:哪个小组有哪辆卡车 两个父级和一个子级。一个配送小组(记录 ID 为配送小组 ID;名称、状态)承担多个配送小组分配。一辆卡车(记录 ID 为卡车 ID,又以车辆编号来标识;品牌、型号、状态)用于多个配送小组分配。每个配送小组分配都有自己的记录 ID,并带有配送小组 ID 和卡车 ID,没有日期。下方的说明:哪个小组有哪辆卡车,但没说从什么时候开始。 承担 用于 配送小组 配送小组 ID 记录 ID 名称 状态 配送小组分配 配送小组分配 ID 记录 ID 配送小组 ID 卡车 ID 卡车 卡车 ID 记录 ID 车辆编号 又称 品牌 型号 状态 哪个小组有哪辆卡车,但没说从什么时候开始。 一个 多个 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

这是建模错误的一个常见来源。我们为当前状态建模,因为那是我们在屏幕上能看到的。然后,几周后,某个零售合作伙伴提出一起货损索赔,有人问:“3 月 17 日是哪个小组开的 117 号卡车?”于是我们发现,每次分配变化时,我们都把答案覆盖掉了。

保留这种历史的一种方法,是让分配成为一条独立的记录。给某个小组分配时,我们创建一条带开始日期的配送小组分配。分配变化时,我们给旧记录加上结束日期,再创建一条带自己开始日期的新记录。现在,“六个月前是谁负责那项分配?”就成了一个简单的问题,而不需要去翻字段历史记录或数据变更日志。而一旦某个小组有过很多次分配,就会有人问哪一次是当前的,所以我还会加一个 Active(有效)标志。

随时间变化的配送小组分配:日期和 Active 标志 两个父级和一个子级,带有日期。一个配送小组(记录 ID 为配送小组 ID;名称、状态)承担多个配送小组分配。一辆卡车(记录 ID 为卡车 ID,又以车辆编号来标识;品牌、型号、状态)用于多个配送小组分配。每个配送小组分配都有自己的记录 ID,并带有配送小组 ID 和卡车 ID、开始日期、结束日期和一个 Active 标志。下方的说明:每次变化都结束一项分配并开始下一项,Active 标出当前的那一项。 承担 用于 配送小组 配送小组 ID 记录 ID 名称 状态 配送小组分配 配送小组分配 ID 记录 ID 配送小组 ID 卡车 ID 开始日期 结束日期 Active 卡车 卡车 ID 记录 ID 车辆编号 又称 品牌 型号 状态 每次变化都结束一项分配并开始下一项。Active 标出当前的那一项。 一个 多个 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

所以,当有人告诉我一个关系时,我不只问这些东西如何相关。我会问:这个关系会变化吗?我们在意它的历史吗?

我们的 Steve Trucking 模型正在变得有用。我们知道哪些关系是一对多,多对多关系在哪里藏着另一个概念,哪些关系是可选的,哪些会随时间变化。而我们仍然没有创建任何 Salesforce 对象。这是有意的。

模块 1 和模块 2 之后的概念模型 Steve Trucking 的概念模型,按客户数据、交易数据和参考数据分组。一个零售合作伙伴发送装货清单,装货清单列出若干配送;零售合作伙伴还运营若干配送中心。每次配送从一个配送中心出发,送达一位收货人,并包含若干配送项目。路线有若干停靠点,停靠点卸下配送。配送小组分配把一个配送小组和一辆卡车与一条路线联系起来,这样模型就记下了谁在何时开了哪辆卡车。 客户数据 交易数据 参考数据 运营 发送 列出 包含 出发自 送达 有 卸下 执行 承担 用于 零售合作伙伴 我们为其配送的零售商 收货人 收货的人 装货清单 合作伙伴的配送清单 配送 一笔订单送一位收货人 配送项目 车上装的东西 路线 一天跑的停靠点 停靠点 路线上的一个地址 配送小组分配 谁在何时开了哪辆卡车 配送中心 配送的出发地 配送小组 两个人 卡车 车辆 恰好一个 零个或一个 一个或多个 零个或多个 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

这张图应该是我可以摆到业务方面前的东西,而且他们不需要了解 Salesforce 就能告诉我它对不对。如果因为图里满是 Salesforce 术语而导致他们无法验证,那我就过快地进入物理设计了。

在梳理概念模型中的关系时,我要决定的是:

  • 主要的业务实体彼此如何关联。
  • 每个关系现在以及随时间变化后,是一对一、一对多还是多对多。
  • 每个关系是必需的还是可选的。
  • 某个多对多关系是否藏着一个有意义的业务概念。
  • 关系是否随时间变化,以及我们是否需要它的历史。

查找关系还是主从关系、联接对象的配置、级联行为和共享,这些都是物理层面的决定;我们会讲到。现在我们仍在回答那个更重要的问题:这个业务实际上是如何运作的?一旦理解了实体以及它们之间的关系,我们就可以开始添加描述它们的细节。那时,我们的概念模型就开始变成一个逻辑数据模型了。