1

我有以下结构:

Block A
  Foo 1
    Bar 1
    Bar 2
  Foo 2
    Bar 1
    Bar 3
  Bar 4

Block B
  Foo 3
  • 每个 Foo 都完全属于 Block。
  • 每个 Bar 都完全属于 Block。
  • 一个 Bar 可以不属于同一个 Block的一个或多个 Foo 。

架构目前是这样的:

    Block
   1/   \1
  n/     \n
Foo-n---m-Bar

这样做的问题是可能存在属于不同 Block 的 Foo 的 Bar

是否存在既没有冗余也不允许不一致的模式?

4

3 回答 3

5

是的,有办法。将 的主键列包含Block到关联表中,并使用它来扩展外键约束:

CREATE TABLE Blocks
  ( BlockID INT 
  , PRIMARY KEY (BlockID)
  ) ;

CREATE TABLE Foos
  ( BlockID INT
  , FooID INT
  , PRIMARY KEY (FooID)
  , FOREIGN KEY (BlockID)
      REFERENCES Blocks (BlockID)
  , UNIQUE (BlockID, FooID)              -- the Unique constraints are needed for 
  ) ;

CREATE TABLE Bars
  ( BlockID INT
  , BarID INT
  , PRIMARY KEY (BarID)
  , FOREIGN KEY (BlockID)
      REFERENCES Blocks (BlockID)
  , UNIQUE (BlockID, BarID)              -- the composite FKs below
  ) ;

CREATE TABLE Foo_Bars                    -- the m:n association tabletable
  ( BlockID INT
  , FooID INT
  , BarID INT
  , PRIMARY KEY (FooID, BarID)
  , FOREIGN KEY (BlockID, FooID)         -- composite FK constraints to Foos
      REFERENCES Foos (BlockID, FooID)
  , FOREIGN KEY (BlockID, BarID)         -- and Bars
      REFERENCES Bars (BlockID, BarID)
  ) ;
于 2013-10-21T16:57:40.703 回答
2

除了ypercube 所说的(对他 +1,顺便说一句),如果您愿意更改键的结构,您可以用更少的索引开销来做到这一点:

在此处输入图像描述

由于 FOO_BAR.BLOCK_ID 引用两个“分支”一直到 BLOCK,那么如果 FOO 和 BAR 连接,它们也必须连接到同一个BLOCK。

除非需要额外的索引(在您原始问题的范围之外),否则可以非常有效地对这种结构进行聚类。

于 2013-10-22T12:01:49.730 回答
1

如果我理解正确,您会遇到这样的情况:

block_foo_bar

block   foo bar
A       1   1
A       1   2
A       2   1
A       2   3
A       0A  4
B       3   0B
  • 我将 ID 0 用于代表缺失元素的虚拟 Foos 和 Bars(如果您愿意,可以使用 NULL)

像这样的表处理 nm,但它不能强制执行 1-n 关系。

我的建议是创建两个额外的表:

block_foo

block(FK) foo(PK)
A         0A
A         1
A         2
B         0B
B         3

块栏

block(FK) bar(PK)
A         0A
A         1
A         2
A         3
A         4
B         0B

这样,两个新表将强制执行 for/bar-block 关系的唯一性。block_foo_bar 将允许您处理 nm 关系。为确保您不会在 block_foo_bar 中存在 block_foo 和 block_bar 表不允许的关系,请在 block_foo_bar 中创建两个约束:

  • block_foo_bar_FK1 = block - block_foo 表中的 foo
  • block_foo_bar_FK2 = 块 - block_bar 表中的条

您可能注意到我创建了像 0A 和 0B 这样的虚拟 ID,我这样做是因为可空的 ID 从来都不是一个好主意,其次是因为通过这种方式我可以在 ID 上强制执行 PK。我知道这意味着在 Foo/Bar 表中为每个没有 Foo/Bar 的块创建一个虚拟记录(ETL 需要做更多的工作),但这通常会让我以后头疼。

希望我的解释对你有意义。

于 2013-10-21T15:08:31.913 回答