我目前正在设计一个用于存储食谱的数据库模式。在这个数据库中,我希望能够标记不同类型的实体(成分,配方发布者,食谱等)。所以标签有多个n:m关系。如果我使用“三表设计”,这将导致我拥有的每种实体类型(食谱,配料,发行人)的表(交叉表)。换句话说,每次我介绍一个实体时,我都要为它添加一个交叉表。
我正在考虑创建一个具有唯一ID的表,所有实体都引用该表,并且在tags表和“unique id”-table之间建立n:m关系。这样,“unique id”-table和tag表之间只有一个交叉表。
以防万一有人会认为这个问题已被提出。我已经阅读了Database Design for Tagging。并且提到了三种表格设计。
答案 0 :(得分: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.TaggableID
和EntityTwo.TaggableID
用作EntityOne
和EntityTwo
的事实上的主键?
在大多数情况下,我会谨慎,让实体拥有自己的ID:
- - - - - - - - - -
EntityOne
ID // PK
Taggable_ID // FK -> Taggable.ID (Nullable)
...
- - - - - - - - - -
EntityTwo
ID // PK
Taggable_ID // FK -> Taggable.ID (Nullable)
...
这不需要每个实体都有Taggable
的相应实例,因此这不需要与实体有关的每一段代码都知道标签。但是,如果标记在系统中确实无处不在,并且如果您确定不需要任何其他“共同祖先”用于实体(即Taggable
除外),那么您可能会得到没有实体的“内在”ID。
NB :我从来没有尝试过这样的事情,所以我的所有建议都纯粹是理论上的。所以如果我没有看到一些明显的缺陷,请不要开枪。 : - )
回应Bill Karwin的评论:
您是对的:上述设计不会阻止多个实体引用相同的Taggable
。但是:
就像我说的,一切都取决于要求。如果我们确定Taggable
将成为实体的唯一“共同祖先”,那么可以使用Taggable_ID
FK作为实体的PK。但是,例如,如果碰巧“可标记”的某些实体也必须“可观察”(思考通知,通知时间表等)或“无论如何”:-)?我们可以通过将任何实体与Taggable
联系起来来削减所有这些“能力”吗?
如果你真的想让DB-level强制执行one-taggable-one-entity约束... 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)
当然,所有这些都比将实体绑定到taggables更复杂。但我只是试图讨论除了原始问题提供的狭隘图片之外,还应该考虑什么。
答案 1 :(得分:2)
我想说这取决于你想如何使用标签。
我想你可以为你要标记的每个实体类型创建一个额外的交集表,如果你一次只搜索一种类型的实体。换句话说,通常会说“向我展示标签'美味'的成分”,但不清楚它的含义是什么,“向我展示标签'美味的两种成分和食谱发布者'。”在此例如,每个实体都有一个单独的交叉表是可以的。
但是,如果您确实需要使用给定标记搜索所有类型的所有实体,则使用单个“ID”表更容易。使所有实体表都指向它,并使用您定义为主键和外键的列:
CREATE TABLE Recipes (
recipe_id INT NOT NULL PRIMARY KEY, -- not auto-generated
FOREIGN KEY (recipe_id) REFERENCES Taggables(id)
);
此计划的唯一缺点是,您无法阻止Recipes
和Ingredients
中的某一行指向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的冲突可能性。
答案 2 :(得分:1)
我认为你走在正确的轨道上。你已经描述它非常好,你有几个不同的实体。您可以创建一个名为entities的表,其中包含所有常用属性(如果有的话)。例如,
实体
成分</ P>
RecipeIssuer
现在您可以拥有一个表来标记实体。
答案 3 :(得分:0)
这是怎么回事?
类型(PK:类型,set_id [,TypeDesc])
属性(PK:( set_id ,FK: 类型 ),值)
PS:Bold / Italics Realy Suck
答案 4 :(得分:0)
使表格成为正常的收件人,成分等。
然后您的标记表应如下所示:Id,Type,Tag
我建议在代码中使用枚举来区分不同的“类型”(实体)。
答案 5 :(得分:0)
我手上也有类似的“问题”。我正在开发一个小型产品数据库,它涉及标记并为标记赋值(标记名:颜色,值:绿色例如)。
两个主要表格是项目(I)和文章(A)。物品是实际的物理物品,物品是从物品中取出的。文章可以在网站上显示,而项目是存储在仓库中的项目。 这种关系的一个小例子可能是汽车零件。具有已知尺寸和其他数据的散热器实际上可以适合许多不同的模型和制造,这就是为什么用于重新呈现散热器的物品涉及多个物品,其指示散热器可以适合什么。 另一方面,我们可能为一个型号提供两种不同的散热器,一种是工厂新版本,另一种是再制造。在这种情况下,有两个项目与同一篇文章有关。
所以,我和A有N:M的关系。
物品和物品具有某些属性。例如,散热器项目可能具有条件,材料,重量,高度,宽度和厚度等数据。 文章还有一些基本的信息,如品牌,型号,年份,引擎等,但也可能需要一些特殊的数据,如底盘模型,传输类型,或其他类似于两种不同的配件类型,已在同一型号上使用。 因为两个项目可以链接到一篇文章,这意味着我不能只标记文章。使用两个条件值标记文章只是愚蠢的,另一方面用一个模型的多个实例标记一个项目,make或某些特殊要求也不是一个好主意。 _有两种类型的属性,第一种表示什么是什么,第二种表示它适合什么。
标签不必具有值,它们可以简单地充当分配给实体的传统标签。
散热器只是一个简单产品的例子。我们还可以在我们的数据库中放置一些电脑零件或衣服。这意味着我需要能够在两个不同的实体I和A上放置不同的“标签”。
我需要能够在网上商店中搜索文章。假设我使用的是基于树的导航,我有一个名为“二手日产散热器”的类别。 搜索将涉及搜索文章和项目,文章具有标签Model:Nissan,并且项目具有标签Condition:Used。 当用户查看文章时,他确实会看到与文章相关的所有项目。
我正在思考的解决方案之一是三角形数据库设计,其中包含一个名为所有属性和标签的标签的公用表。
我们有表格项目(I),文章(A)和标签(T) 他们与N:M关系联合起来: I2A将项目加入到文章中。 T2I将标记加入到项目中,并且可能也存储标记或属性的值。 T2A将标签加入到文章中,也可以为标签存储值。
在纸面上,这个解决这个问题的6桌设计看起来相当不错,但我在形成一个体面的查询时遇到了麻烦,我可以选择匹配一组不同标签及其值的文章,例如: 条件=再制造,Make = Nissan
我想要做的是像www.summitracing.com。从“商店”下方的左侧选择部门,选择任何类别,您将看到他们如何设法为项目提供一些属性。它们具有适用于大多数应用的引擎尺寸,但在寻找轮辋时,它们也具有宽度属性。
对此的任何反馈都将非常感激,我即将开始试图设计这个。