如何使用两个或更多父 table 使用的子 table 构建数据模型
How to build data modeling with a child table being used by two or more parent tables
在一个简单的示例中,我有一个买家 table 和一个供应商 table。如何使用两个父 table 为 Phone table 建模。
更新
这是一种合适的方法吗?
注:使用结点table时,岂不是很困难,比如在数据库中查询一个phone号就知道了它属于供应商还是买方?
更新
经过考虑,我认为在这种情况下最好的解决方案是创建一个基础 table 并将其称为 Entities,因此其他具有 telephone 的实体必须有一个记录在此 table。尽管示例中的买方和供应商 table 具有相同的字段(只是为了方便起见),但在实际情况中它们是两个具有非常不同的字段的 table。
这是个大问题。
在讨论期间,我会保留您的后续评论,这基本上是一个方便的问题:给定一个 phone 号码,如果能够轻松知道就太好了who/what它属于。
假设我们按照您的原始图表保持依赖关系的方向,其中 phone 数字“指向”其买家或供应商。那看起来像什么?
create table PhoneNumbers
(
phoneNumber varchar(16) primary key,
buyerId int foreign key references Buyers,
supplierId int foreign key references suppliers
);
这里的明显限制是,如果你添加更多种类的“有phone个数字的东西”,你必须向phone个数字中添加越来越多的列table .也不清楚单个 phone 号码是否应该能够同时用于买方和供应商。只能有一个关联的 ID 不为空吗?如果添加了更多相关内容,这将成为一个烦人的检查约束。
但是,话虽如此,解决方案相当简单。如果你绝对肯定地知道你只有两种相关的类型,你就可以“逃脱”。这很糟糕,但我们承认它很糟糕,但还是做了。
但是这样方便吗?查询是什么样的?
select relatedThingType = case
when b.buyerId is not null then 'buyer'
when s.supplierId is not null then 'supplier'
else '????'
end
from PhoneNumbers p
left join Buyers b on b.buyerId = p.buyerId
left join Suppliers s on s.supplierId = p.supplierId
where p.phoneNumber = @phoneNumber
不太方便。我们在这里反对模式,防止两者都为空的情况,根据我们的案例陈述的顺序任意决定如果两个外键都被填充,我们将选择买家而不是供应商,等等。
好的,如果我们按照建议并根据您更新的图表将其颠倒过来呢?
create table PhoneNumbers
(
phoneNumber varchar(16) primary key
);
create table Buyers
(
buyerId int primary key,
buyerName varchar(32),
phoneNumber varchar(16) foreign key references phoneNumbers
);
create table Suppliers
(
supplierId int primary key,
supplierName varchar(32),
phoneNumber varchar(16) foreign key references phoneNumbers
);
有一件事很明显:给定一个 phone 号码,我们实际上不必加入 PhoneNumbers
table 来找出谁拥有它 - 只要我们不使用代理 ID,而是使用 phone 数字本身作为键。
事实上,tables 具有单个列(因此必然是键)永远不会在查询中发挥作用。它们的唯一功能是强制参照完整性(并使级联操作可用)。
OK,查询时间
select relatedThingType = 'buyer'
from Buyers
where phoneNumber = @phoneNumber
union all
select 'supplier'
from Suppliers
where phoneNumber = @phoneNumber;
本质上,join
变成了 union
。这样方便吗?我认为,如果有一些“查询结构的总体大小”的度量标准,那将是大致相同的。但是我们不再像以前那样与图式作斗争了。我们也“自然地”允许买家和供应商都拥有 phone 号码,如果发生这种情况,两者都会被退回。
所以这些模型在可用基数方面存在功能差异,这也意味着查询语义存在差异。到目前为止,选择并不是真正基于“什么更方便”,而是基于“您的模型需要执行的基数是多少”?
但是...这个版本稍微更方便。添加新的 relatedThingType 不需要我们更改任何架构。我们只是将另一个 union all
添加到查询中。
好的,大揭示时间。我一直在故意使用这个尴尬的术语 relatedThingType
。显然,我们对这是什么没有一个清晰明确的概念。我们如何解决这个问题?
问题是,您实际上是在说“买家和供应商都有一个共同的方面:他们都是可以拥有 phone 数字的东西。但他们在其他方面是不同的”。
如果您进行任何非 sql 编程,那闻起来很像继承关系。当您查看这两个 table 的模式时,会得到更多提示。他们都有名字。所以他们有一个共同的属性。在 OOAD 中,这将是基于 class.
的字段
所以解决这个问题的一种方法是明确承认基地 class。买家和供应商都是方,一个方可以有phone个号码。
create table Parties
(
partyType varchar(16) check (partyType in ('buyer', 'supplier')),
partyId int primary key,
phoneNumber varchar(16),
partyName varchar(32),
constraint pk_p unique (partyType, partyId)
);
create table Buyers
(
partyId int primary key,
partyType varchar(16) default 'buyer' check (partyType = 'buyer'),
-- other buyer specific columns
constraint fk_b_p foreign key (partyType, partyId) references Parties(partyType, partyId)
);
create table Suppliers
(
partyId int primary key,
partyType varchar(16) default 'supplier' check (partyType = 'supplier'),
-- other supplier specific columns
foreign key (partyType, partyId) references Parties(partyType, partyId)
);
看起来不太方便。但是等等,那是 架构 。我们正在谈论的查询呢?
select partyType, partyName, phoneNumber
from Parties
where phoneNumber = @phoneNumber
很方便。
当然,您可以通过视图获得相同的结果,其定义与我之前编写的 union all
查询基本相同。您是否真的想要“实例化基础 class”只是一个选择。我们将在一秒钟内了解如何做出选择...
但是模式中那些奇怪的默认和检查约束是怎么回事?他们正在做的是强制执行这样的规则,即一方只能是 供应商, 或 买家,但不能两者兼而有之。我们永远不会让买家和供应商不小心共享 partyId
。这是一种“排他性子类型”关系。如果需要强制执行此操作,则需要实例化基础 class。一个视图不会削减它。
我们可以走得更远,因为上一段暗示了另一种可能的情况:包含子类型 关系的情况。一方可以同时是买家,和供应商。为此,您将进入“基于角色的建模”模式,其中 Party
有一个或多个 Roles
,由 PartyRoles
连接点 table 确定。我通常称其为“CounterParties”table,因为每一行代表某一方在我们的观点方面发挥某种作用——即,它们是某种特定类型的交易对手,由角色定义。
好的,它的架构是什么?好吧……我真的不打算做这个。我会把它留作练习。
嗯,这一切都很好,很学术,但你还没有给我答案!
没错。但这里的想法不是给出“唯一真实的答案”。希望这一切表明 you 的正确答案恰恰取决于您的模型需要建模的内容。这取决于您的域。买家也可以是供应商吗?派对可以有多个 phone 号码吗?一个phone号可以关联多个买家吗?这些问题的答案对您的模式必须做什么和不能做什么提出了严格的要求。您构建的任何模式都必须遵守这些约束。我提供的各种选项都遵循不同的域约束集,它们并不完全等同。
那么,你问题的答案是……你会恨我的……
“视情况而定”。
请原谅 DDL 中的任何语法错误。它只是为了说明概念
在一个简单的示例中,我有一个买家 table 和一个供应商 table。如何使用两个父 table 为 Phone table 建模。
更新 这是一种合适的方法吗?
注:使用结点table时,岂不是很困难,比如在数据库中查询一个phone号就知道了它属于供应商还是买方?
更新
经过考虑,我认为在这种情况下最好的解决方案是创建一个基础 table 并将其称为 Entities,因此其他具有 telephone 的实体必须有一个记录在此 table。尽管示例中的买方和供应商 table 具有相同的字段(只是为了方便起见),但在实际情况中它们是两个具有非常不同的字段的 table。
这是个大问题。
在讨论期间,我会保留您的后续评论,这基本上是一个方便的问题:给定一个 phone 号码,如果能够轻松知道就太好了who/what它属于。
假设我们按照您的原始图表保持依赖关系的方向,其中 phone 数字“指向”其买家或供应商。那看起来像什么?
create table PhoneNumbers
(
phoneNumber varchar(16) primary key,
buyerId int foreign key references Buyers,
supplierId int foreign key references suppliers
);
这里的明显限制是,如果你添加更多种类的“有phone个数字的东西”,你必须向phone个数字中添加越来越多的列table .也不清楚单个 phone 号码是否应该能够同时用于买方和供应商。只能有一个关联的 ID 不为空吗?如果添加了更多相关内容,这将成为一个烦人的检查约束。
但是,话虽如此,解决方案相当简单。如果你绝对肯定地知道你只有两种相关的类型,你就可以“逃脱”。这很糟糕,但我们承认它很糟糕,但还是做了。
但是这样方便吗?查询是什么样的?
select relatedThingType = case
when b.buyerId is not null then 'buyer'
when s.supplierId is not null then 'supplier'
else '????'
end
from PhoneNumbers p
left join Buyers b on b.buyerId = p.buyerId
left join Suppliers s on s.supplierId = p.supplierId
where p.phoneNumber = @phoneNumber
不太方便。我们在这里反对模式,防止两者都为空的情况,根据我们的案例陈述的顺序任意决定如果两个外键都被填充,我们将选择买家而不是供应商,等等。
好的,如果我们按照建议并根据您更新的图表将其颠倒过来呢?
create table PhoneNumbers
(
phoneNumber varchar(16) primary key
);
create table Buyers
(
buyerId int primary key,
buyerName varchar(32),
phoneNumber varchar(16) foreign key references phoneNumbers
);
create table Suppliers
(
supplierId int primary key,
supplierName varchar(32),
phoneNumber varchar(16) foreign key references phoneNumbers
);
有一件事很明显:给定一个 phone 号码,我们实际上不必加入 PhoneNumbers
table 来找出谁拥有它 - 只要我们不使用代理 ID,而是使用 phone 数字本身作为键。
事实上,tables 具有单个列(因此必然是键)永远不会在查询中发挥作用。它们的唯一功能是强制参照完整性(并使级联操作可用)。
OK,查询时间
select relatedThingType = 'buyer'
from Buyers
where phoneNumber = @phoneNumber
union all
select 'supplier'
from Suppliers
where phoneNumber = @phoneNumber;
本质上,join
变成了 union
。这样方便吗?我认为,如果有一些“查询结构的总体大小”的度量标准,那将是大致相同的。但是我们不再像以前那样与图式作斗争了。我们也“自然地”允许买家和供应商都拥有 phone 号码,如果发生这种情况,两者都会被退回。
所以这些模型在可用基数方面存在功能差异,这也意味着查询语义存在差异。到目前为止,选择并不是真正基于“什么更方便”,而是基于“您的模型需要执行的基数是多少”?
但是...这个版本稍微更方便。添加新的 relatedThingType 不需要我们更改任何架构。我们只是将另一个 union all
添加到查询中。
好的,大揭示时间。我一直在故意使用这个尴尬的术语 relatedThingType
。显然,我们对这是什么没有一个清晰明确的概念。我们如何解决这个问题?
问题是,您实际上是在说“买家和供应商都有一个共同的方面:他们都是可以拥有 phone 数字的东西。但他们在其他方面是不同的”。
如果您进行任何非 sql 编程,那闻起来很像继承关系。当您查看这两个 table 的模式时,会得到更多提示。他们都有名字。所以他们有一个共同的属性。在 OOAD 中,这将是基于 class.
的字段所以解决这个问题的一种方法是明确承认基地 class。买家和供应商都是方,一个方可以有phone个号码。
create table Parties
(
partyType varchar(16) check (partyType in ('buyer', 'supplier')),
partyId int primary key,
phoneNumber varchar(16),
partyName varchar(32),
constraint pk_p unique (partyType, partyId)
);
create table Buyers
(
partyId int primary key,
partyType varchar(16) default 'buyer' check (partyType = 'buyer'),
-- other buyer specific columns
constraint fk_b_p foreign key (partyType, partyId) references Parties(partyType, partyId)
);
create table Suppliers
(
partyId int primary key,
partyType varchar(16) default 'supplier' check (partyType = 'supplier'),
-- other supplier specific columns
foreign key (partyType, partyId) references Parties(partyType, partyId)
);
看起来不太方便。但是等等,那是 架构 。我们正在谈论的查询呢?
select partyType, partyName, phoneNumber
from Parties
where phoneNumber = @phoneNumber
很方便。
当然,您可以通过视图获得相同的结果,其定义与我之前编写的 union all
查询基本相同。您是否真的想要“实例化基础 class”只是一个选择。我们将在一秒钟内了解如何做出选择...
但是模式中那些奇怪的默认和检查约束是怎么回事?他们正在做的是强制执行这样的规则,即一方只能是 供应商, 或 买家,但不能两者兼而有之。我们永远不会让买家和供应商不小心共享 partyId
。这是一种“排他性子类型”关系。如果需要强制执行此操作,则需要实例化基础 class。一个视图不会削减它。
我们可以走得更远,因为上一段暗示了另一种可能的情况:包含子类型 关系的情况。一方可以同时是买家,和供应商。为此,您将进入“基于角色的建模”模式,其中 Party
有一个或多个 Roles
,由 PartyRoles
连接点 table 确定。我通常称其为“CounterParties”table,因为每一行代表某一方在我们的观点方面发挥某种作用——即,它们是某种特定类型的交易对手,由角色定义。
好的,它的架构是什么?好吧……我真的不打算做这个。我会把它留作练习。
嗯,这一切都很好,很学术,但你还没有给我答案!
没错。但这里的想法不是给出“唯一真实的答案”。希望这一切表明 you 的正确答案恰恰取决于您的模型需要建模的内容。这取决于您的域。买家也可以是供应商吗?派对可以有多个 phone 号码吗?一个phone号可以关联多个买家吗?这些问题的答案对您的模式必须做什么和不能做什么提出了严格的要求。您构建的任何模式都必须遵守这些约束。我提供的各种选项都遵循不同的域约束集,它们并不完全等同。
那么,你问题的答案是……你会恨我的……
“视情况而定”。
请原谅 DDL 中的任何语法错误。它只是为了说明概念