几乎树的数据库模式

时间:2013-10-21 14:31:02

标签: database-design data-modeling database-normalization

我有以下结构:

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

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

架构目前是这样的:

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

问题在于可以有Bar's 属于不同Block的Foo的

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

3 个答案:

答案 0 :(得分: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)
  ) ;

答案 1 :(得分:2)

除了ypercube said(+1给他,BTW)之外,如果你愿意改变你的密钥结构,你可以用更少的索引开销来做到这一点:

enter image description here

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

除非需要额外的索引(在原始问题范围之外),否则此结构可以非常有效clustered

答案 2 :(得分: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)

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

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

<强> block_foo

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

<强> block_bar

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

通过这种方式,两个新表将强制执行for / bar-block关系的唯一性。 block_foo_bar将允许您处理n-m关系。为了确保你不会在block_foo和block_bar表中不允许的block_foo_bar关系中在block_foo_bar中创建两个约束:

  • block_foo_bar_FK1 = block - foo in block_foo table
  • block_foo_bar_FK2 = block - bar in block_bar table

正如您可能已经注意到我创建了0A和0B之类的虚拟ID,我之所以这样做是因为ID nullable永远不是一个好主意,其次是因为这样我可以在ID上强制执行PK。我知道这意味着这意味着在Foo / Bar表中为每个块创建一个虚拟记录而没有Foo / Bar(更多的ETL工作),但这通常会让我头疼。

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