10

假设我有可能Bravo或 Charlie 的事物相关或无关的 Alpha 事物。

这些是一对一的关系:没有任何 Alpha 与一个以上的 Bravo 相关。没有 Bravo 与不止一个 Alpha 相关。

我有几个目标:

  • 一个易于学习和维护的系统。
  • 在我的数据库中强制执行数据完整性。
  • 与我的数据的真实逻辑组织相匹配的模式。
  • 我的编程中可以很好地映射到数据库表的类/对象(à la Linq to SQL)
  • 快速的读写操作
  • 有效利用空间(少数空字段)

我有三个想法……

PK = primary key  
FK = foreign key  
NU = nullable

一张包含许多 nullalbe 字段的表(平面文件)...

      Alphas
      --------
   PK AlphaId
      AlphaOne
      AlphaTwo
      AlphaThree
   NU BravoOne
   NU BravoTwo
   NU BravoThree
   NU CharlieOne
   NU CharlieTwo
   NU CharlieThree

许多具有零 nullalbe 字段的表...

      Alphas
      --------
   PK AlphaId
      AlphaOne
      AlphaTwo
      AlphaThree

      Bravos
      --------
FK PK AlphaId
      BravoOne
      BravoTwo
      BravoThree

      Charlies
      --------
FK PK AlphaId
      CharlieOne
      CharlieTwo
      CharlieThree

两者中最好的(或最坏的):许多表的许多空外键......

      Alphas
      --------
   PK AlphaId
      AlphaOne
      AlphaTwo
      AlphaThree
NU FK BravoId
NU FK CharlieId

      Bravos
      --------
   PK BravoId
      BravoOne
      BravoTwo
      BravoThree

      Charlies
      --------
   PK CharlieId
      CharlieOne
      CharlieTwo
      CharlieThree

如果 Alpha 必须是 Bravo 或 Charlie,但不能两者兼而有之怎么办?

如果不只是 Bravos 和 Charlies,Alphas 还可以是 Deltas、Echos、Foxtrots 或 Golfs 等中的任何一个……?


编辑:这是问题的一部分:哪个是我导航的最佳数据库模式?

4

9 回答 9

8

如果您希望每个 Alpha 仅与一个 Bravo 相关,我会投票支持使用组合 FK/PK 的可能性:

      Bravos
      --------
FK PK AlphaId
      BravoOne
      BravoTwo
      BravoThree

这样,只有一个 Bravo 可以指代您的 Alpha。

如果 Bravos 和 Charlies 必须互斥,最简单的方法可能是创建一个鉴别器字段:

      Alpha
      --------
   PK AlphaId
   PK AlphaType NOT NULL IN ("Bravo", "Charlie")
      AlphaOne
      AlphaTwo
      AlphaThree

      Bravos
      --------
FK PK AlphaId
FK PK AlphaType == "Bravo"
      BravoOne
      BravoTwo
      BravoThree

      Charlies
      --------
FK PK AlphaId
FK PK AlphaType == "Charlie"
      CharlieOne
      CharlieTwo
      CharlieThree

这样,AlphaType 字段会强制记录始终恰好属于一个子类型。

于 2008-09-11T17:58:12.453 回答
4

I'm assuming you will be using SQL Server 2000 / 2005. I have a standard pattern for 1-to-1 relationships which I use, which is not too dissimilar to your 2nd idea, but here are the differences:

  • Every entity must have its own primary key first, so your Bravo, Charlie, etc tables should define their own surrogate key, in addition to the foreign key column for the Alpha table. You are making your domain model quite inflexible by specifying that the primary key of one table must be exactly the same as the primary key of another table. The entities therefore become very tightly coupled, and one entity cannot exist without another, which is not a business rule that needs to be enforced within database design.

  • Add a foreign key constraint between the AlphaID columns in the Bravo and Charlie tables to the primary key column on the Alpha table. This gives you 1-to-many, and also allows you to specify whether the relationship is mandatory simply by setting the nullability of the FK column (something that isn't possible in your current design).

  • Add a unique key constraint to tables Bravo, Charlie, etc on the AlphaID column. This creates a 1-to-1 relationship, with the added benefit that the unique key also acts as an index which can help to speed up queries that retrieve rows based on the foreign key value.

The major benefit of this approach is that change is easier:

  • Want 1-to-many back? Drop the relevant unique key, or just change it to a normal index
  • Want Bravo to exist independently of Alpha? You've already got the surrogate key, all you do is set the AlphaID FK column to allow NULLs
于 2008-09-22T05:24:49.773 回答
3

就个人而言,我在您的第二个模型上取得了很多成功,在单个列上使用 PK/FK。

我从来没有遇到过要求所有 Alpha 都在 Bravo 或 Charlie 表中记录的情况。我一直处理 1 <-> 0..1,从来没有 1 <-> 1。

至于你的最后一个问题,那就是更多的表。

于 2008-09-11T17:41:57.967 回答
1

到目前为止,我有一个非常适合您的模型的示例:

我有来自 Alpha 的外键 alpha_id 的 Charlie 和 Bravo 表。就像你的第一个例子一样,除了 alpha 不是主键, bravo_id 和 charlie_id 是。

我在需要处理这些实体的每个表上都使用 alpha_id,因此,为了避免可能导致延迟研究 Bravo 和 Charlie 以查找哪个 Alpha 的 SQL,我创建了一个 AlphaType 表并在 Alpha 表上我有它的 id (alpha_type_id) 作为外键。这样,我可以以编程方式知道我正在处理的 AlphaType,而无需加入可能有无数记录的表。在 tSQL 中:

// For example sake lets think Id as a CHAR.
// and pardon me on any mistake, I dont have the exact code here,
// but you can get the idea

SELECT 
  (CASE alpha_type_id
    WHEN 'B' THEN '[Bravo].[Name]'
    WHEN 'C' THEN '[Charlie].[Name]'
    ELSE Null
  END)
FROM ...
于 2008-09-11T22:07:43.147 回答
1

另一种方法是使用 3 个表来存储 3 个实体,并使用一个单独的表来存储关系。

于 2008-09-11T17:40:57.983 回答
1

您可以有一个指定 Alpha 和相关 ID 的连接表。然后,您可以添加另一列,指定它是 Bravo、Charlie 还是其他的 ID。使列在 Alpha 上缓慢下降,但确实增加了连接查询的复杂性。

于 2008-09-11T17:42:13.240 回答
1

您提出了很多问题,如果没有更多地说明您要解决的确切问题,就很难选择您提出的任何解决方案。不仅要考虑我的澄清问题,还要考虑您将用来评估我的问题的标准,以表明解决问题所需的详细信息量:

  • 一个易于学习和维护的系统。

什么“系统”容易学习和维护?您的应用程序的源代码,或通过其最终用户界面的应用程序数据?

  • 在我的数据库中强制执行数据完整性。

“在我的数据库中强制执行”是什么意思?这是否意味着您不能以任何其他方式控制数据完整性,即项目只需要基于数据库的数据完整性规则?

  • 与我的数据的真实逻辑组织相匹配的模式。

您能否向我们提供您所指的真实世界的逻辑组织?不可能从您尝试存储的数据的三个示例中推断出它——即假设您的所有三个结构都完全错误。除非我们知道真实世界的规范,否则我们怎么知道呢?

  • 我的编程中可以很好地映射到数据库表的类/对象(à la Linq to SQL)

这个要求听起来像是你的手被迫使用 linq to SQL 创建它,是这样吗?

  • 快速的读写操作

什么是“快”?0.03 秒?3秒?30分钟?目前尚不清楚,因为您没有指定您所指的数据大小和操作类型。

  • 有效利用空间(少数空字段)

有效利用空间与空字段的数量无关。如果您的意思是规范化的数据库结构,那将再次取决于问题中未提供的实际规范和应用程序的其他设计元素。

于 2008-09-17T19:04:08.297 回答
0

除非我有充分的理由不这样做,否则我会选择选项 1。它可能不会像你想象的那样花费你那么多的空间,尤其是。如果您在 Bravo 中使用 varchars。不要忘记拆分它会花费你的外键、二级身份和所需的索引。

如果不太可能需要 Bravo (<%10) 并且您需要通过其中一个字段快速查询以便索引它,您可能会遇到麻烦的地方。

于 2008-09-11T17:48:23.257 回答
0

我会创建一个超类型/子类型关系。

   THINGS
   ------
PK ThingId  

   ALPHAS
   ------
FK ThingId (not null, identifying, exported from THINGS)
   AlphaCol1
   AlphaCol2
   AlphaCol3  

   BRAVOS
   ------
FK ThingId (not null, identifying, exported from THINGS)
   BravoCol1
   BravoCol2
   BravoCol3  

   CHARLIES
   --------
FK ThingId (not null, identifying, exported from THINGS)
   CharlieCol1
   CharlieCol2
   CharlieCol3

因此,例如,一个有 charlie 但没有 bravo 的 alpha:-

insert into things values (1);
insert into alphas values (1,'alpha col 1',5,'blue');
insert into charlies values (1,'charlie col 1',17,'Y');

请注意,您不能为 alpha 创建一个以上的 charlie,就好像您尝试创建一个 ThingId 为 1 的两个 charlie 一样,第二次插入将获得唯一的索引/约束违规。

于 2008-09-17T18:13:16.407 回答