6

我目前正在设计一个用于存储食谱的数据库模式。在这个数据库中,我希望能够标记不同类型的实体(配料、配方发布者、配方等)。所以一个标签有多个 n:m 关系。如果我使用“三表设计”,这将导致我拥有的每种实体类型(配方、成分、发行人)的表(交叉表)。换句话说,每次我介绍一个实体时,我都必须为其添加一个交叉表。

我正在考虑创建一个具有唯一 id 的表,所有实体都引用该表,以及标签表和“唯一 id”表之间的:m 关系。这样,“唯一 id”表和标签表之间只有一个交叉表。

以防有些人会认为这个问题已经被问过了。我已经阅读了Database Design for Tagging。那里提到了三张桌子的设计。

4

6 回答 6

2

我认为为所有标记分配使用一个表没有任何问题(与多个表相反 - 每个可标记实体一个表)。

然而,你设计中的一个重要细节对我来说仍然是模棱两可的:如果你打算有一些类似的东西

- - - - - - - - - -
Tag
    ID           // PK
    Name
    ...

- - - - - - - - - -
Taggable
    ID           // PK
    ...

- - - - - - - - - -
TagAssignment
    Tag_ID       // FK -> Tag.ID
    Taggable_ID  // FK -> Taggable.ID
    ...

- - - - - - - - - -
EntityOne
    Taggable_ID  // FK -> Taggable.ID
    ...

- - - - - - - - - -
EntityTwo
    Taggable_ID  // FK -> Taggable.ID
    ...

那么您的实体类是否将拥有自己的主键,或者您将使用EntityOne.TaggableIDand作为andEntityTwo.TaggableID的事实上的主键?EntityOneEntityTwo

在大多数情况下,我会谨慎并让实体拥有自己的 ID:

- - - - - - - - - -
EntityOne
    ID           // PK
    Taggable_ID  // FK -> Taggable.ID (Nullable)
    ...

- - - - - - - - - -
EntityTwo
    ID           // PK
    Taggable_ID  // FK -> Taggable.ID (Nullable)
    ...

这不会要求每个实体都有相应的实例,Taggable因此也不会要求与实体相关的每段代码也都知道标签。但是,如果标记将在系统中真正无处不在,并且如果您确定不需要任何其他实体的“共同祖先”(即,除了Taggable),那么您可能会在没有“内在”的情况下逃脱实体的 ID。

注意:我从来没有尝试过实现这样的东西,所以我所有的建议都是纯理论的。所以如果我没有看到一些明显的缺陷,请不要拍我。:-)


回应比尔·卡尔文的评论:

你是对的:上面描述的设计并没有阻止多个实体引用同一个Taggable. 但:

  1. 就像我说的,一切都取决于要求。如果我们确定它将Taggable成为实体的唯一“共同祖先”,那么可以将Taggable_IDFK 用作实体的 PK。但是,例如,如果某些恰好是“可标记”的实体也必须是“可观察的”(想想通知、通知时间表等)或“任何可以的”:-) 怎么办?我们可以通过将任何实体与任何实体紧密联系来切断所有这些“能力”Taggable吗?

  2. 如果你真的想对一个可标记的实体约束进行数据库级别的强制...... AFAIK,至少有一种常见的方法可以做到这一点而不使 FK 充当 PK:通过引入可标记的“类型”(其中无论如何可能对其他一些功能有用)。

类似这样的东西会让我们吃蛋糕并吃掉它:

- - - - - - - - - -
Taggable
    ID           // PK
    Type        
    ... 
    - - - - - - - -
    Constraint: (ID, Type) is unique


- - - - - - - - - -
EntityOne
    ID
    Taggable_ID   
    Taggable_Type // Constraint: always = 'EntityOne'
    ...
    - - - - - - - -
    FK: (Taggable_ID, Taggable_Type) -> (Taggable.ID, Taggable.Type)

当然,所有这些都比将实体绑定到可标记对象要复杂得多。但我只是想讨论在我的拙见中,除了原始问题提供的狭隘图片之外,还应该考虑什么。

于 2008-11-19T17:53:12.383 回答
2

我会说这取决于你想如何使用标签。

如果您一次只搜索一种类型的实体,我想您可以为要标记的每种实体类型创建一个额外的交集表。换句话说,说“给我看带有'yummy'标签的成分”是很正常的,但不清楚这意味着“给我看带有'yummy'标签的成分和食谱发布者。”在这个在这种情况下,每个实体有一个单独的交集表就可以了。

但是,如果您确实需要使用给定标签搜索所有类型的所有实体,那么使用单个“ID”表会更容易。使用您定义为主键和外键的列使所有实体表指向它:

CREATE TABLE Recipes (
  recipe_id INT NOT NULL PRIMARY KEY, -- not auto-generated
  FOREIGN KEY (recipe_id) REFERENCES Taggables(id)
);

这个计划的唯一弱点是你不能阻止两个中的一行RecipesIngredients指向同一行Taggables

INSERT INTO Taggables (id) VALUES (327);
INSERT INTO Recipes (recipe_id, name) VALUES (327, 'Hollandaise sauce');
INSERT INTO Ingredients (ingr_id, name) VALUES (327, 'eggs');

您是否希望与鸡蛋相关的每个标签也适用于荷兰酱?

我只是指出单表设计的这一方面。考虑到其他要求,它可能仍然是为您的标记建模的最佳方式。但是您应该注意从属表中 id 冲突的可能性。

于 2008-11-19T18:13:43.497 回答
1

我认为你在正确的轨道上。你描述得很好,你有几个不同的实体。您可以创建一个名为实体的表,其中包含所有常见属性(如果有的话)。所以例如

实体

  • 实体 ID
  • 姓名

成分

  • 实体 ID
  • 数量

配方发行者

  • 实体 ID
  • 一些其他信息

现在您可以有一个表来标记实体。

于 2008-11-19T15:22:31.003 回答
0

我手上也有类似的“问题”。我正在开发一个小型产品数据库,其中涉及标签并为标签赋予一个值(例如标签名:颜色,值:绿色)。

两个主要表格是项目 (I) 和文章 (A)。项目是实际的物理项目,并且文章是从项目派生的。文章是可以在网站上展示的东西,物品是要存放在仓库中的东西。这种关系的一个小例子可能是汽车零件。具有已知尺寸和其他数据的散热器实际上可以适合许多不同的型号和制造,这就是为什么用于表示散热器的项目涉及到多个表明散热器可以适合的物品的原因。另一方面,我们可能为一个型号提供两种不同的散热器,一种是全新的版本,另一种是刚刚再制造的。在这种情况下,有两个项目与同一篇文章有​​关。

因此,我和 A 具有 N:M 关系。

物品和物品具有某些属性。例如,散热器项目可能具有条件、材料、重量、高度、宽度和厚度等数据。这篇文章还包含一些基本信息,如品牌、型号、年份、发动机等,但可能还需要一些特殊数据,如底盘型号、变速箱类型,或其他一些信息,如同一型号上使用的两种不同的配件类型。因为两个项目可以链接到一篇文章,这意味着我不能只标记文章。用两个条件值标记一篇文章是愚蠢的,另一方面,用一个模型的多个实例、品牌或一些特殊要求标记一个项目也不是一个好主意。_有两种类型的属性,第一种表示某物是什么样的,第二种表示它适合什么。

标签不必有值,它们可以简单地充当分配给实体的常规标签。

散热器只是简单产品的一个例子。我们不妨将一些计算机零件或服装放入我们的数据库中。这意味着我需要能够在两个不同的实体 I 和 A 上放置不同的“标签”。

我需要能够在网上商店中搜索文章。假设我正在使用基于树的导航,其中我有一个名为“二手日产散热器”的类别。搜索将涉及搜索文章和项目,文章具有标签 Model:Nissan,并且项目具有标签 Condition:Used。当然,当用户查看文章时,他确实会看到与文章相关的所有项目。

我正在考虑的解决方案之一是三角形数据库设计,其中有一个通用表,称为所有属性和标签的标签。

我们有表项目 (I)、文章 (A) 和标签 (T) 它们以 N:M 关系连接:I2A 将项目连接到文章。T2I 将标签连接到项目,并且也可能存储标签或属性的值。T2A 将标签连接到文章,并且也可能存储标签的值。

在纸面上,这个解决这个问题的 6 表设计看起来相当不错,但是我对形成一个体面的查询感到头疼,我可以在其中选择匹配一组不同标签及其值的文章,例如:Condition=Remanufactured , 品牌=日产

我希望能够做的是类似于 www.summitracing.com 的东西。从“商店”下方的左侧选择部门,选择任何类别,您将看到他们如何设法为项目提供一些属性。它们具有适用于大多数应用的发动机尺寸,但在寻找轮辋时,它们还具有宽度属性。

对此的任何反馈将不胜感激,我开始尝试设计它。

于 2009-02-15T02:36:58.377 回答
0

为食谱、配料等正常制作表格。

那么你的标签表应该是这样的:Id, Type, Tag

我建议在代码中使用枚举来区分不同的“类型”(实体)。

于 2008-11-19T15:33:34.150 回答
0

这个怎么样?

类型(PK:类型,set_id[,TypeDesc])

属性(PK :( set_id,FK:类型),值)

PS:粗体/斜体真的很烂

于 2008-11-19T15:23:13.623 回答