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 brick house with a glass room built onto its side, and a separate timber garden studio across the lawn, joined to it only by a stepping-stone path.
图片由 AI 生成

第 7 个模块,共 10 个,属于 Data Architect 课程第 3 章Salesforce 数据架构。每个模块都可以单独阅读。 上一篇:字段也是架构 · 下一篇:所有权与生命周期

**本模块回答的问题:**当我连接两个 Salesforce 对象时,我真正在选择的是什么?

画一个关系很容易。在第 2 章里,我们画了线并给它们起了名字:一个零售合作伙伴发送很多份装货清单,一次配送载有很多个项目,配送小组和卡车在配送小组分配中相遇。我们讨论了基数、可选性和历史。现在我们要在 Salesforce 上实现这些线,而图上的一条线开始承载多得多的分量。

当我在 Salesforce 上选择一种关系类型时,我不只是在说**这两条记录是相连的。**我可能还在决定谁拥有子记录,它能否在没有父记录的情况下存在,谁能看到它,删除父记录时会发生什么,它能否移到另一个父记录下,我能否把它汇总到父记录上,以及当子记录有几十万条时会发生什么。关系不只是一条线,它承载着行为。

在谈查找关系还是主从关系之前,回到业务。拿一次配送和它的配送项目来说:冰箱、安装、拉走旧货。一个项目能脱离它的配送而存在吗?不能。如果配送被删除,它的项目也应该一起删除吗?应该。这些项目需要自己的所有者或自己的安全设置吗?其实不需要。会有人把一个项目从一次配送移到另一次配送吗?但愿不会是无意中的。

再看一次配送和它所在的路线。配送在被排上路线之前就存在,计划变化时它可以被移到另一条路线上,而删除一条路线永远不应该删除路线上的配送。两条在图上看起来一样的一对多连线,行为却完全不同。注意,我们仍然还没有说“查找关系”或“主从关系”。先描述行为,配置只是去实现它。

打开新建字段界面,Salesforce 会给你几种关系类型(Object Relationships Overview (opens in a new tab),英文):

  • 查找关系(Lookup):连接两个对象。Salesforce 关系里的家常便饭。
  • 主从关系(Master-detail):一种父子绑定,由父记录控制子记录。
  • 多对多(Many-to-many):它本身不是一种字段类型,而是一种模式:一个 Salesforce 联接对象(Junction Object),即带有两个主从关系的自定义对象。
  • 自关系(Self-relationship):从一个对象指向它自己的查找关系。
  • 层级关系(Hierarchical):一种只存在于 User 对象上的特殊查找关系,比如一个用户的经理。
  • 外部查找关系(External lookup)和间接查找关系(Indirect lookup):与外部对象之间的关系,也就是模块 4 里的 __x 对象。外部查找关系指向一个作为父对象的外部对象;间接查找关系则从一个外部对象指向一个标准对象或自定义对象,按一个唯一的外部 ID 来匹配。

你的设计时间大多花在前三种上。我们一个一个来看。

查找关系说的是:**这条记录与那条记录相关联。**子记录保留自己的身份、自己的所有人和自己的生命。一次配送指向它的路线。如果路线被取消了,配送不会消失;它们会回去重新排路线。

有意思的是:查找字段放在子记录上,但父记录也会得到一样东西,即它的子记录组成的相关列表。所以创建查找关系时,要给两端都起好名字。Related List Label(相关列表标签)是人们在父记录上看到的,Child Relationship Name(子关系名称)是开发人员和集成从父记录走到子记录时使用的。不要接受默认值。

查找关系可以是必需的,也可以是可选的。当它是可选的时,你要决定它所指向的记录被删除时会发生什么,共有三种选择(Considerations for Object Relationships (opens in a new tab),英文):

  • 清空这个字段的值,这是默认选项。子记录保留下来,查找字段为空。
  • 不允许删除被查找的记录。试着删除这条路线,Salesforce 会拒绝:它是某个关系的一部分。
  • 同时删除这条记录,也就是级联删除,就像主从关系那样。它只提供给自定义对象上的自定义查找关系,默认是禁用的,而且必须由 Salesforce 为你的 org 开启,因为级联删除会绕过安全和共享:用户能删除他们根本看不到的记录。

这是一个藏在设置界面里的生命周期决定。

查找筛选器会缩小用户可以选择的范围。假设配送上有一个指向联系人的 Installer(安装人员)查找关系,而安装人员只是众多联系人记录类型中的一种。查找筛选器只显示安装人员,它可以按当前记录、按被查找的记录,以及按用户(他们的简档、角色或权限)来筛选(Lookup Filters (opens in a new tab),英文)。这是让查找关系保持诚实的一种便宜办法。

主从关系:这条子记录属于那条父记录

Section titled “主从关系:这条子记录属于那条父记录 ”

主从关系做出的声明要强得多。我在课上把它叫作一种非常紧密的绑定。没有主记录就不能有从记录;主记录字段是必需的。从记录失去自己的所有人字段,继承其父记录的所有人。从记录没有单独的共享表,所以你不能单独共享一条子记录;它继承父记录的共享设置。这就是为什么从对象不能有共享规则、手动共享或队列:它们都需要一个所有人字段(Object Relationships Overview (opens in a new tab),英文)。在设置中,它的共享设置显示为 Controlled by Parent(由父项控制)。而在主从关系字段本身上,你要选择一个 Sharing Setting(共享设置):用户需要对主记录有只读还是读/写权限,才能创建、编辑或删除它的从记录。删除主记录会删除它的从记录。而且主记录可以使用汇总字段。

所以如果配送项目是配送的从记录,我就是在说:这个项目在它的整个生命周期中都属于这次配送。这是我希望在任何人在设置中选择“主从关系”之前就得到回答的问题。

这是我最喜欢的检验我们是否想清楚了的方法:**如果有人删除了父记录,子记录应该怎样?**每个人都会不自在。很好。现在我们是在做架构了。

一个零售合作伙伴有几十万条历史配送。有人删除了这个合作伙伴。这些配送应该消失吗?几乎肯定不应该:索赔、计费和历史都依赖于它们。也许根本就不应该允许删除合作伙伴,而应该把它停用。在主从关系中,删除主记录会级联到从记录及其从记录;恢复主记录会把它们一起恢复(Considerations for Object Relationships (opens in a new tab),英文)。所以当有人说“主从关系能给我们汇总字段”时,我的回答是:很好。你刚才还同意了什么?

在查找关系中,子记录可以有自己的所有者。在主从关系中则不能;访问权限跟随主记录。如果从记录真的是父记录的一部分,这完全正确。但如果子记录需要自己的所有者、自己的共享或自己的生命周期,模型就在告诉你主从关系是错误的选择。下一个模块会更深入地讨论所有权,而 Sharing and Visibility 课程会讲得更远。现在请记住:**关系设计和安全设计不是两场分开的对话。**在任何人写下一条共享规则之前,数据模型就已经在塑造安全模型了。

子记录能否在我们知道它的父记录之前就存在?这比听起来更重要。如果配送在前一天晚上随装货清单到达,而路线是之后才建的,那么在配送上把路线设为必需,就意味着必须先排好路线才能创建这条记录。物理关系必须支撑真实的生命周期。所以我不会只凭一张静态的图来设计关系。我想知道:它们如何诞生,能否单独存在,能否移动,又如何消亡?

默认情况下,主从关系中的子记录不能移到另一个父记录下。你可以通过在关系上选择允许重设父级来允许这样做(Considerations for Object Relationships (opens in a new tab),英文)。我的直觉是保守的:如果业务说子记录终生属于一个父记录,我就不会让移动它变得容易。一个项目从一次配送移到另一次配送,应该是一个有意的业务事件,而不是一次字段编辑。同样,设置本身不是架构。生命周期规则才是,设置只是去实现它。

还有两件事会在后来让团队措手不及。你不能在一个已经有数据的对象上创建主从关系;你要先创建一个查找关系,在每条记录上填好值,然后再转换它。而且只有当主记录上没有汇总字段时,才能把它转换回查找关系。还要留意转换对安全的影响:把查找关系变成主从关系,会把子对象的共享设置改为 Controlled by Parent;把主从关系变回查找关系,会把子对象的组织范围默认设置改为 Public Read/Write(公共读/写)(Considerations for Object Relationships (opens in a new tab),英文)。昨天还是私有的记录,今天就敞开了,直到有人发现为止。早做选择。

汇总字段很有用,但不是免费的

Section titled “汇总字段很有用,但不是免费的 ”

我喜欢汇总字段。统计一次配送上的项目数,汇总一张发票上的行金额,找出最晚的日期:原生功能,无需代码。主从关系字段放在子记录上,汇总字段则放在父记录上。不过它们有附加条款(Roll-Up Summary Field (opens in a new tab),英文):

  • 在某些变更之后(新建汇总字段、修改它所依赖的公式、强制重新计算),计算汇总值可能需要长达 30 分钟,取决于受影响的记录数量。我见过有人纳闷父记录上的数字为什么还没变。
  • 它们不能汇总所有东西。自动编号字段不能作为被汇总的字段。只有当一个公式字段不跨到另一个对象、也不使用会自己变化的函数(比如 NOW() 或 TODAY())时,汇总字段才能汇总它。一个基于 TODAY() 的“逾期天数”公式每晚都在变,却没有任何人保存记录,所以汇总字段没有可以据以重新计算的东西。这类数字需要一个计划触发的 Flow,或者一张报表。
  • 筛选条件也有限制:不能用长文本区域、多选选项列表、描述、Last Activity 这样的系统字段、跨对象公式或查找字段。
  • 汇总字段不在编辑页面上,所以验证规则可以使用它,但不能在那里显示它的错误。

而且不要把决定倒过来做。不要说:“我们需要一个汇总字段,所以这必须是主从关系。”要说:“这条子记录确实属于这条父记录,所有权和生命周期都契合,而好处之一是有原生的汇总字段。”如果语义不契合,就用别的方法解决计算问题。不要为了得到一个功能而扭曲数据模型。

那么别的方法是什么?你自己构建汇总:在父记录上放一个保存计算值的字段,再用自动化让它保持最新。假设零售合作伙伴是从配送出发的一个查找关系,这本来就该如此,而业务希望在合作伙伴上看到“未完成配送数”(Open Deliveries)。在合作伙伴上加一个数字字段 Open Deliveries。然后用一个记录触发的 Flow,或者在数据量或逻辑需要代码时用一个 Apex 触发器,在每次配送变化时重新计算它。它可以跨查找关系工作,可以按任何条件筛选,还可以使用原生汇总字段用不了的日期和公式。

但现在要把它做对就是你的事了,而这类方案正是在这里出错的。自动化必须在每一个重要的变化上触发:新建子记录、编辑子记录、删除子记录、恢复子记录,以及子记录移到另一个父记录下,这意味着旧父记录和新父记录都要重新计算。它必须能处理一次 200 条记录的数据加载而不触及限制。要重新计数,而不是加加减减,这样漏掉一个事件也不会让数字永远错下去。要准备一种把所有东西重新计算一遍的办法,用于首次加载和数字出现偏差的那一天。还要记住,现在每一次保存子记录都会更新父记录,所以一个有几千条子记录的父记录会把我们马上要讲到的锁定问题带回来。原生汇总字段会替你做好这一切;自定义汇总则是一小段归你维护的软件。

平台有它的形状,而主从关系在两个维度上体现这种形状。第一个是深度。

你可以把主从关系叠加到三个自定义从级的深度,这就到底了(Considerations for Object Relationships (opens in a new tab),英文)。在课上,我会画一个客户和它的订单(第一级),订单和它的项目(第二级),项目和它们的明细(第三级),然后我试着加第四级,Salesforce 不让我加。在 Steve Trucking 上它可能是这样的:**配送(Delivery)是主记录;它的配送项目(Delivery Item)是第一级;一个项目的安装任务(Install Task)是第二级;每个任务的任务步骤(Task Step)**是第三级。第四级,比如步骤备注(Step Note),就只能是查找关系了。

三级主从关系 Steve Trucking 上叠加的三层主从关系,用粗线表示。配送是主记录。配送项目是第 1 级从记录,安装任务是第 2 级,任务步骤是第 3 级。步骤备注用一条细的查找关系线挂在任务步骤下,因为第四级只能是查找关系。底部:所有权、共享和删除从配送一路向下传到每一级。 第 1 级 第 2 级 第 3 级 第四级只能是查找关系 配送 Delivery__c · 主记录 配送项目 Delivery_Item__c 安装任务 Install_Task__c 任务步骤 Task_Step__c 步骤备注 Step_Note__c 所有权、共享和删除从配送一路向下传到每一级。 恰好一个 零个或一个 一个或多个 零个或多个 主从 查找 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

叠加时有几条规则:

  • **标准对象不能作为自定义对象的从对象。**自定义对象可以是客户的从对象;客户不能是你的自定义对象的从对象。
  • **一个对象在多级叠加中只能出现一次。**Salesforce 不希望你绕回到自己身上:一个子从对象不能同时是它自己的主对象的主对象。
  • **主记录不能直接汇总最底层。**如果配送想要任务步骤的数量,就由安装任务汇总它的步骤,配送项目汇总这些汇总值,配送再汇总项目:一次一级。
  • **所有权和共享来自顶部。**每一级都继承主记录的所有人和访问权限。
  • 删除主记录会删除它下面的一切,恢复它会把一切都带回来。

三层通常已经足够。如果你的设计总想要第四层,就问一问:每一层是否真的终生属于它上面的那一层。还要记住,哪一个连接来断开这条链,是由你选择的。第四级不一定非得是那个查找关系:也许一个安装任务并不真正属于它的配送项目,它是单独排期、单独分派的,那么这个连接就变成查找关系,而安装任务开始一条新链,下面是任务步骤和步骤备注。挑出子记录真正有自己生命的那个连接,把它做成查找关系。

用查找关系断开链条 与三级叠加相同的五个 Steve Trucking 对象,中间有一个查找关系。配送是主记录,配送项目是它的从记录,用粗的主从关系线连接。配送项目用一条细的查找关系线链接到安装任务,断开了链条。安装任务是一个新的主记录:任务步骤是它的从记录,步骤备注是任务步骤的从记录,都用粗的主从关系线连接。底部:安装任务现在有了自己的所有人和共享,删除一次配送不再会删除它的安装任务。 第 1 级 查找关系断开链条 第 1 级 第 2 级 配送 Delivery__c · 主记录 配送项目 Delivery_Item__c 安装任务 Install_Task__c · 新的主记录 任务步骤 Task_Step__c 步骤备注 Step_Note__c 安装任务现在有了自己的所有人和共享,删除一次配送不再会删除它的安装任务。 恰好一个 零个或一个 一个或多个 零个或多个 主从 查找 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

现在安装任务有了自己的所有人和自己的共享,删除一次配送不会把它的安装任务一起带走,而且安装任务下面还可以再有两级。这就是取舍:链条更短了,而安装任务的生命是它自己的。

宽两个父对象:Salesforce 联接对象

Section titled “宽两个父对象:Salesforce 联接对象 ”

首先说说用词,因为“联接对象”有两个意思,而这两个我都教。

在第 2 章里,我们找到了逻辑联接对象:两个关键概念的交汇处变成了一个独立的东西,有它自己的属性。一只狗会很多技能,一个技能被很多只狗掌握,而狗狗技能(Dog Trick)位于两者之间,保存着熟练程度和日期。这就是一个逻辑联接对象,存在于逻辑模型中,远在任何人决定用查找关系还是主从关系之前。

Salesforce 对这个词的用法更窄。当 Salesforce 说联接对象时,它指的是一个带有两个主从关系的自定义对象(Create a Many-to-Many Object Relationship (opens in a new tab),英文)。我把它叫作 Salesforce 联接对象。每个 Salesforce 联接对象都是逻辑联接对象,但并不是每个逻辑联接对象都会变成 Salesforce 联接对象:用一个主从关系加一个查找关系,或者用两个查找关系来构建它,它仍然是逻辑联接对象,只是不是 Salesforce 那一种。(Salesforce 自己也不完全一致:它的对象参考把一些基于查找关系的标准对象也叫作“联接对象”。考试时,按窄的定义来理解。)

考点。 Salesforce 联接对象是一个带有两个主从关系的自定义对象,Salesforce 说“联接对象”时指的就是它。逻辑联接对象是更宽泛的建模概念:任何在多对多关系中连接另外两个对象、并带有自己属性的对象,不管它是怎么构建的。

第二个维度是宽度。一条子记录能有两个主从关系父对象吗?能。三个呢?不能。每个自定义对象最多可以有两个主从关系(Considerations for Object Relationships (opens in a new tab),英文)。两个主从关系父对象,就是你为多对多关系构建 Salesforce 联接对象的方式。配送小组和卡车在配送小组分配(Crew Assignment)中相遇,而正如我们在第 2 章发现的,它不是管道:它有日期、路线,也许还有状态。联接对象正是关系变成业务概念的地方。Salesforce 关于构建联接对象的建议:给它起一个能说明它是什么的名字,把它的记录名称设为自动编号,并把每个主对象的相关列表改名为另一个主对象的名字,这样配送小组页面显示“卡车”,卡车页面显示“配送小组”(Create a Many-to-Many Object Relationship (opens in a new tab),英文)。

复杂之处在这里。两个父对象中有一个是主要父对象,就是你先创建的那个主从关系字段所属的父对象。界面上没有任何视觉提示告诉你是哪一个,所以这是一道经典考题,也是一个经典的意外。

主要父对象决定什么?三件事(Create a Many-to-Many Object Relationship (opens in a new tab),英文):

  • **外观。**联接对象的页面使用主要主对象的颜色和图标。
  • **所有人。**联接记录继承主要主记录的所有人。你永远看不到它,因为从对象没有所有人字段;只有当你以后删除了两个主从关系时,它才有意义。
  • 部门(Division),如果你的 org 使用部门的话,也是同样的方式。

另一个就是次要父对象。它意味着什么?

  • **它是主要父对象的接班人。**删除主要的主从关系字段,或者把它转换为查找关系,次要父对象就会变成主要父对象。
  • **它决定报表的范围。**Salesforce 给你两种报表类型:“Primary master with junction object and secondary master”(主要主对象、联接对象和次要主对象)和“Secondary master with junction object and primary master”(次要主对象、联接对象和主要主对象)。排在前面的主对象决定报表能显示哪些记录。
  • 在访问权限上它同样重要。我是这样说的:所有权来自主要父对象,访问权限来自两者,最严格的那个说了算。联接对象和任何从对象一样,没有自己的共享表。Salesforce 会通过每个主从关系字段上的共享设置,检查用户对两条主记录的访问权限。如果两边都设为读/写,用户就需要对配送小组和卡车都有读/写权限,才能编辑这条配送小组分配(Considerations for Object Relationships (opens in a new tab),英文)。
  • **在删除上它同样起作用。**删除任何一个主记录,配送小组或卡车,它的联接记录都会进入回收站。两个都删除,它们就永久消失了。
联接对象的主要和次要父对象 一个联接对象位于它的两个主从关系父对象下方,用粗线表示。配送小组位于左上方,是主要父对象,因为它的主从关系字段先创建;它给配送小组分配带来所有人和外观。卡车位于右上方,是次要父对象;它决定第二种报表类型的范围,如果第一个关系被移除,它就会成为主要父对象。配送小组分配位于两者之间的下方。底部:访问权限需要两个父对象都满足,最严格的那个说了算;删除任一父对象,分配记录也会随之删除。 所有人和外观 报表范围 配送小组 Crew__c · 主要:先创建 卡车 Truck__c · 次要 配送小组分配 Crew_Assignment__c 访问权限需要两个父对象都满足:最严格的那个说了算。删除任一父对象,分配记录也会随之删除。 恰好一个 零个或一个 一个或多个 零个或多个 主从 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

还有几条规则:两个主对象都可以有来自联接对象的汇总字段,联接对象的公式和验证规则可以引用两个主对象上的字段。但联接对象不能作为其他任何东西的主对象,所以它不能位于叠加链条的中间;而且两个主对象不能是同一个对象,一个对象与它自己的多对多关系需要另一种设计。另外,如果一个父记录有超过 200 条联接记录,而联接对象又汇总到另一个父对象上,那么在你删掉足够多的联接记录之前,Salesforce 不会让你删除那个父记录。

那么,哪个父对象应该是主要父对象?先创建承载含义的那一个。对于配送小组分配,干活的是配送小组,所以我会先创建配送小组。然后把它写下来,因为界面上没有任何东西会告诉下一个人。

说了这么多,我最后用到的 Salesforce 联接对象其实并不多。通常一个对象只有一个主从关系父对象,而且是同一类数据:客户数据挂在客户数据下,交易数据挂在交易数据下(记得第 2 章的数据种类:客户数据、交易数据、参考数据)。当它的另一个父对象是另一类数据,通常是一个参考对象时,那个关系就是查找关系。配送项目属于它的配送:主从关系,两者都是交易数据。它用查找关系指向它所运送的产品,因为产品是有自己生命的参考数据,没人希望删除一个产品会把多年的配送历史一起带走。配送项目仍然是配送和其上产品之间的逻辑联接对象;它只是不是 Salesforce 联接对象。

一个对象可以与自己关联:一个上级客户,一个经理,一个向区域枢纽汇报的配送中心。Salesforce 内置了两个你几乎在每个 org 里都会遇到的自关系:客户到客户,即构建客户层级的上级客户(Parent Account)字段;以及个案到个案,即模块 2 里用来归组相关个案的上级个案(Parent Case)。自关系必须是查找关系,而且一条记录不能指向它自己,尽管两条记录可以互相指向(Considerations for Object Relationships (opens in a new tab),英文)。在 User 对象上,它是一种独立的特殊类型,即层级关系,用于一个用户的经理这类场景。机制很简单;命名才重要。一个从配送中心指向配送中心、叫作 Distribution_Center__c 的查找关系对谁都没有帮助。上级?备用?溢出?按角色给关系命名,而不是按它指向的对象命名。

自关系 三个对象,每个都有一个指向自己的查找关系。客户通过它的 ParentId 字段指向上级客户,由此构建客户层级。个案通过它的 ParentId 字段指向上级个案。配送中心是一个自定义对象,通过名为 Regional_Hub__c 的字段指向它的区域枢纽。底部:永远是查找关系,永远不指向同一条记录,并按它扮演的角色命名。 上级客户 上级个案 区域枢纽 客户 Account · ParentId 个案 Case · ParentId 配送中心 Distribution_Center__c · Regional_Hub__c 永远是查找关系,永远不指向同一条记录,并按它扮演的角色命名。 恰好一个 零个或一个 一个或多个 零个或多个 查找 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

层级关系:User 对象专属的关系

Section titled “层级关系:User 对象专属的关系 ”

这个很小,而它正是那种鲜为人知、独一无二、我预计考试会拿来出题的事实。层级关系是一种只存在于 User 对象上的特殊查找关系。它把一个用户连接到另一个用户,而 Salesforce 不允许这条链绕回来:一个用户不能直接或间接地指向自己(Object Relationships Overview (opens in a new tab),英文)。如果 Maria 的经理是 Dev,那么 Dev 的经理就不能是 Maria,也不能是任何向上汇报到 Maria 的人。

这就是它和上面那些自关系的区别。在客户或自定义对象上,两条记录可以互相指向。在 User 上,层级必须保持为层级。而且这是关联 User 与 User 的唯一方式:系统不提供从 User 指向自己的普通查找关系。

你已经有一个了。User 上的标准 Manager(经理)字段就是一个层级关系,审批可以使用它:把这个请求发给提交人的经理。你也可以创建自己的层级关系,比如 Escalation Contact(升级联系人)或 Backup Approver(备用审批人),审批也能以同样的方式沿着它流转(ApprovalProcess, Metadata API (opens in a new tab),英文)。不要把它和角色层级混为一谈,后者决定记录访问权限。层级字段只是用户之间的一个关系;它不共享任何一条记录。

考点。 层级关系只存在于 User 对象上,比如标准的 Manager 字段,而且一个用户不能直接或间接地指向自己。它不是角色层级。

每个对象最多可以有两个主从关系。查找关系字段和主从关系字段合在一起算作关系字段:每个对象默认 40 个,申请后最多 50 个。汇总字段:每个对象默认 25 个,最多 40 个(Increase the Maximum Relationships Allowed per Object (opens in a new tab);Increase the Maximum Limit of Roll-Up Summary Fields (opens in a new tab),英文)。Salesforce 还补充说,提高任一项限制都可能影响性能。

你不需要按这些数字来设计;你需要在接近它们时有所察觉。一个挂着一个又一个关系的对象,正在告诉你一些事情。也许它确实就是那么核心。也许是模型错了。(第 18 章的余量规则也适用:远离限制,并以第三年的数据来衡量。)

然后是子记录太多的父记录。Salesforce 自己的建议是,一个主从关系父记录下的子记录不要超过大约 10,000 条(Considerations for Object Relationships (opens in a new tab),英文),而它关于大规模记录访问的指南对一般的父子关系也是这么说的(Parent-Child Data Skew (opens in a new tab),英文)。第 10,001 条记录不会让 org 崩溃。但过了这个点,保存一条子记录可能会锁住父记录,而父记录上的共享变更会波及每一条子记录。

所以当我看到一个父记录、数量巨大的子记录时,我会问:为什么它们都在同一个父记录下?谁拥有它们?它们如何被查询?哪些自动化会碰到父记录?把所有配送都放在一个“Steve Trucking”客户下面,几天就会超过 10,000 条。一个无害的一对多关系变成了倾斜问题。下一个模块会预览数据倾斜,第 2 部分会深入讨论。

这是我希望你从本模块养成的习惯。不要从“我需要一个汇总字段”、“我需要它是必需的”、“我想要继承的共享”开始。这些是实现层面的线索。要从业务开始:子记录有自己的生命吗?谁拥有它?它能单独存在吗?能移动吗?父记录没了会怎样?关系本身承载数据吗?每一边能有多大?然后选择能实现这些答案的关系。

查找关系、主从关系还是联接对象? 一个矩阵,用三种关系模式对照六个业务问题。查找关系:子记录能单独存在,有自己的所有人,可以更换父记录,并能应对每个父记录超过 10,000 条子记录的情况,不过仍要注意数据倾斜;默认情况下它不会随父记录而去(字段被清空,或删除被阻止),并且每个字段只能有一个父记录。它意味着“相连”。主从关系:必须有父记录,子记录继承父记录的所有人,随父记录级联删除,只有在允许重设父级时才能更换父记录,超过 10,000 条子记录会带来父记录锁定和重新计算。它意味着“属于”。Salesforce 联接对象:需要两个父记录,所有人来自主要父对象,任一父记录被删除时它也会被删除,而且它是唯一能实现两边都是多个的模式。它意味着“一个业务概念”。底部:先回答业务问题;关系类型随之而来。 能单独存在 有自己的所有人 随父记录而去 可更换父记录 两边都是多个 每个父记录 10,000+ 它意味着 查找关系 配送 → 路线 默认清空或阻止 每个字段一个父记录 仍要注意倾斜 相连 主从关系 配送 → 配送项目 必须有父记录 继承父记录的 级联删除 仅当允许重设父级 每个字段一个主记录 父记录锁定、重算 属于 Salesforce 联接对象 配送小组 + 卡车 → 配送小组分配 需要两个父记录 来自主要父对象 任一父记录删除它 仅当允许重设父级 这正是它的用途 同样的 10,000 指南 一个业务概念 先回答业务问题,关系类型随之而来;不要从你想要的功能出发。 © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

**考试之外:AI 改变了什么。**AI 可以在几秒内提出关系。它无法告诉你业务是否会删除父记录。一旦几百万条记录和几十个自动化依赖于某个错误的关系,画图更快并不会让逆转它变得更便宜。

  • 子记录是有自己的生命周期,还是真正属于父记录,以及所有权和访问权限应该是它自己的还是继承的。
  • 子记录能否在没有父记录的情况下存在,能否在父记录之间移动,能否在父记录被删除后继续存在。
  • 每个查找关系在其父记录被删除时做什么:清空、阻止,还是级联删除。
  • 一个多对多关系是管道还是业务概念,逻辑联接对象是否要变成 Salesforce 联接对象,哪个父对象是主要父对象,以及两个父对象对访问权限意味着什么。
  • 模型是否符合平台的形状:深三个自定义从级,宽两个主从关系父对象,以及关系和汇总字段的限制。
  • 预期的子记录数量和分布是否会带来倾斜风险,而这种风险是否应该改变设计。

关系是一组关于两份数据如何一起表现的承诺。要有意识地做出这些承诺。