数据库设计问题:歧视列与外键

时间:2009-04-16 16:54:06

标签: database-design

因此,假设我有一个用于管理客户的数据库。客户拥有人们可能期望的所有基本属性 - 但是,有一些特殊类型的客户称为分销商。经销商本身没有任何特殊的财产,除了它可以将产品分销到存储在市场表中的某些市场。

我可以想出两种方法来区分经销商和“常客”:

  1. 在客户表中创建一个名为“customer_type”的歧视列。此列将包含一个区分“常规”和“分销”客户的值。

    • 这种方法的好处在于它非常简单,如果需要,可以轻松添加新的客户类型。

    • 这种方法的缺点是,在这种情况下,连接市场和“经销商”的市场表只会将市场与“客户”联系起来。没有办法强制将市场链接到分销商类型的客户。

  2. 单独保留customers表,并创建一个基本上只有id列和customer表的外键列的分配器表。

    • 这种方法的好处是我的市场表现在可以链接到只包含分销商的分销商表。没有机会链接到非经销商的客户(如果客户“类型”从经销商更改为常规经销商可能会发生)。

    • 这种方法的缺点是它更加复杂,添加新的客户类型非常困难。

  3. 我还有什么遗漏,你对这件事有何看法?

7 个答案:

答案 0 :(得分:5)

我会为拥有自己的密钥/ ID的经销商提供一个单独的表格。如果所有经销商都是客户,则可以将外键输入客户表。

最终您可能想要向分销商添加属性。然后我会有一个单独的表格将市场与经销商联系起来(这可能会随着时间的推移而变化)。

通常只有少数几个属性的实体(例如分销商)最终会拥有更多。

答案 1 :(得分:2)

经销商有多少额外的财产?只有一两个? - >与鉴别者一起去

如果您拥有大量新物业:您的客户中有多少将成为经销商?只是几个 - >使用附加属性制作单独的分销商表。

但如果您的一半客户将成为经销商,那么将它们全部放在一个表中并且只有一个“客户类型”的描述符可能更容易。

马克

答案 2 :(得分:2)

我赞成你的选择(1)。正如您所说,如果分销商没有其他特殊字段,并且您将来可能会添加更多客户类型,这绝对是正确的方法。 (如果情况发生变化,则情况可能并非如此)

参考:

  

没有办法强制执行a   市场与客户有关   经销商类型。

...你可以写一个确保这个的约束。

或者甚至可能更好,让经销商成为view客户并将您的市场表链接到那里。

答案 3 :(得分:2)

在您的解决方案1中,您可以强制市场以这种方式仅引用分销商:

CREATE TABLE Customers (
  customer_id SERIAL PRIMARY KEY,
  customer_type CHAR(1) CHECK (customer_type IN ('C', 'D')),
  UNIQUE KEY (customer_id, customer_type)
);

CREATE TABLE Markets (
  market_id SERIAL PRIMARY KEY,
  customer_id INT NOT NULL,
  customer_type CHAR(1) CHECK (customer_type = 'D'),
  FOREIGN KEY (customer_id, customer_type) 
    REFERENCES Customers (customer_id, customer_type)
);

外键可以引用 主键或引用表中的唯一键。

但请注意,如果您的分销商特定属性与非分销商无关,则将其放入Customers表格会违反Third Normal Form。也就是说,这些分发器属性的相关性将取决于customer_type中不属于主键的值。在第三范式中,每个属性必须只依赖于主键。

出于这个原因,我会选择第二个解决方案,使Distributors成为子表,引用Customers。将特定于分发服务器的属性放入Distributors表。然后,Markets表格可以更简单地引用Distributors

答案 4 :(得分:2)

我建议你选择最简单的选项1。 (实际上)不可能在数据库级别强制执行每个业务规则。

考虑如果市场与公司而不是分销商相关联,并根据可能产生的情况做出决定,将会发生什么后果。以下是“如果...有多糟糕”我使用的决策矩阵:

  1. 我会被解雇吗?
  2. 晚上我会在家打电话吗?
  3. 第二天早上我需要修理吗?
  4. 我是否必须为下一个预定版本修复它?

答案 5 :(得分:1)

为什么不通过在customer中使用鉴别器列,然后编写视图来获得这两个优势:

 create view distributor as 
  select * from customer where is_distributor = 1;

事实上,您应该将FK客户转到customer_type表,并区别于:

create table customer_type (id int, name, bit is_distributor);

insert into customer_type values (1, 'Customer', 0);
insert into customer_type values (2, 'Distributor', 1);

alter table customer 
 add column customer_type_id int
 references customer_type(id)
;

create view distributor as 
  select a.* from customer a 
  join customer_type b 
  on (a.customer_type_id = b.id and b.is_distributor = 1)
;

答案 6 :(得分:1)

假设您要使用一些OR映射器,将从代码中隐藏此数据库详细信息。我会为每个子类映射决定一个表,因为......

  1. 它非常好地重新采样继承层次结构。
  2. 不需要人工鉴别器栏。
  3. 添加新子类只需要添加新表,而每个类层次结构使用表映射需要修改正在使用的表。
  4. 对于任何行都没有无用的空字段,如果每个类层次结构映射将新的子类引入表中,则会出现越来越多的无用值 - 这是表格规范化程度低的结果。
  5. 您可以轻松实现外键约束。
  6. 使用OR映射器和往返工​​程,即使是复杂的继承层次结构的创建也不会有任何痛苦。
  7. 如果您考虑使用Hibernate,请查看[N] Hibernate参考手册中的Inheritance Mapping