16

我正在尝试类似Database Design for Tagging之类的东西,除了我的每个标签都被分组到类别中。

例如,假设我有一个关于车辆的数据库。假设我们实际上对车辆知之甚少,因此我们无法指定所有车辆将具有的列。因此,我们将用信息“标记”车辆。

1. manufacture: Mercedes
   model: SLK32 AMG
   convertible: hardtop

2. manufacture: Ford
   model: GT90
   production phase: prototype

3. manufacture: Mazda
   model: MX-5
   convertible: softtop

现在您可以看到所有汽车都标有其制造商和型号,但其他类别并不完全匹配。请注意,一辆车只能有一个类别。IE。一辆车只能有一个制造商。

我想设计一个数据库来支持搜索所有梅赛德斯,或者能够列出所有制造商。

我目前的设计是这样的:

vehicles
  int vid
  String vin

vehicleTags
  int vid
  int tid

tags
  int tid
  String tag
  int cid

categories
  int cid
  String category

我有所有正确的主键和外键,除了我无法处理每辆车只能有一个制造商的情况。或者我可以吗?

我可以在vehicleTags 中的复合主键中添加外键约束吗?IE。我可以添加一个约束,使得复合主键 (vid, tid) 只能添加到 vehicleTags 中,前提是在 vehicleTags 中还没有一行,这样对于同一个 vid,在 with 中还没有 tid同一个cid?

我的猜测是否定的。我认为解决这个问题的方法是在vehicleTags中添加一个cid列,并制作新的复合主键(vid,cid)。它看起来像:

vehicleTags
  int vid
  int cid
  int tid

这将防止汽车有两个制造商,但现在我已经复制了 tid 在 cid 中的信息。

我的架构应该是什么?

汤姆在我之前的问题中注意到我的数据库模式中的这个问题,你如何做多对多表外连接?

编辑
我知道在示例中制造确实应该是车辆表中的一列,但假设你不能这样做。这个例子只是一个例子。

4

6 回答 6

18

这是实体-属性-值设计的另一个变体。

一个更容易识别的 EAV 表如下所示:

CREATE TABLE vehicleEAV (
  vid        INTEGER,
  attr_name  VARCHAR(20),
  attr_value VARCHAR(100),
  PRIMARY KEY (vid, attr_name),
  FOREIGN KEY (vid) REFERENCES vehicles (vid)
);

有些人强制attr_name引用预定义属性名称的查找表,以限制混乱。

您所做的只是将 EAV 表分布在三个表上,但没有改进元数据的顺序:

CREATE TABLE vehicleTag (
  vid         INTEGER,
  cid         INTEGER,
  tid         INTEGER,
  PRIMARY KEY (vid, cid),
  FOREIGN KEY (vid) REFERENCES vehicles(vid),
  FOREIGN KEY (cid) REFERENCES categories(cid),
  FOREIGN KEY (tid) REFERENCES tags(tid)
);

CREATE TABLE categories (
  cid        INTEGER PRIMARY KEY,
  category   VARCHAR(20) -- "attr_name"
);

CREATE TABLE tags (
  tid        INTEGER PRIMARY KEY,
  tag        VARCHAR(100) -- "attr_value"
);

如果你打算使用 EAV 设计,你只需要vehicleTagscategories表。

CREATE TABLE vehicleTag (
  vid         INTEGER,
  cid         INTEGER,     -- reference to "attr_name" lookup table
  tag         VARCHAR(100, -- "attr_value"
  PRIMARY KEY (vid, cid),
  FOREIGN KEY (vid) REFERENCES vehicles(vid),
  FOREIGN KEY (cid) REFERENCES categories(cid)
);

但请记住,您将数据与元数据混合在一起。您失去了对数据模型应用某些约束的能力。

  • 您如何使其中一个类别成为强制性的(常规列使用NOT NULL约束)?
  • 您如何使用 SQL 数据类型来验证您的某些标记值?你不能,因为你对每个标签值都使用了一个长字符串。这个字符串是否足够长,可以满足您将来需要的每个标签?你说不出来。
  • 如何将一些标签限制为一组允许的值(常规表使用查找表的外键)?这是您的“软顶”与“软顶”示例。但是您不能对tag列进行约束,因为该约束将应用于其他类别的所有其他标记值。您还可以有效地将发动机尺寸和油漆颜色限制为“软顶”。

SQL 数据库不适用于此模型。做对是非常困难的,查询它变得非常复杂。如果您继续使用 SQL,您最好按照常规方式对表进行建模,每个属性一列。如果您需要“子类型”,则为每个子类型定义一个从属表(Class-Table Inheritance),或者使用Single-Table Inheritance。如果每个实体的属性有无限变化,请使用序列化 LOB

为这些流动的非关系数据模型设计的另一种技术是语义数据库,将数据存储在RDF中并使用SPARQL进行查询。一种免费的解决方案是RDF4J(以前称为 Sesame)。

于 2008-12-17T00:33:16.530 回答
3

我需要解决这个确切的问题(相同的通用领域和所有东西——汽车零件)。我发现该问题的最佳解决方案是使用 Lucene/Xapian/Ferret/Sphinx 或您喜欢的任何全文索引器。比 SQL 所能提供的性能要好得多。

于 2012-10-27T11:43:04.120 回答
1

您描述的不是标签,标签只是值,它们没有关联的键。标签通常实现为字符串列,值是分隔的值列表。

例如 #1,标签字段将包含一个值,例如:

“制造_梅赛德斯,型号_SLK32 AMG,敞篷车_硬顶”

然后,用户通常能够通过一个或多个标签的存在轻松过滤条目。从数据库的角度来看,它本质上是无模式数据。标签有缺点,但它们也避免了使用 EAV 模型带来的极端复杂性。如果你真的需要一个 EAV 模型,也可能值得考虑一个包含 JSON 数据的属性字段。查询起来更痛苦,但仍然没有跨多个表查询 EAV 可怕。

于 2021-01-11T18:53:56.357 回答
0

我认为您的解决方案是简单地将制造商列添加到您的车辆表中。这是您知道所有车辆都将具有的属性(即汽车不会自行出现),并且通过将其作为车辆表中的一列,您可以解决每辆车只有一个制造商的问题。这种方法适用于您知道将由所有车辆共享的任何属性。然后,您可以为不通用的其他属性实现标记系统。

因此,从您的示例中,车辆表将类似于:

车辆
  视频
  葡萄酒
  制作
  模型
于 2008-12-16T23:31:59.870 回答
0

一种方法是稍微重新考虑您的架构,将标签键标准化为远离值:

vehicles
  int vid
  string vin

tags
  int tid
  int cid
  string key

categories
  int cid
  string category

vehicleTags
  int vid
  int tid
  string value

现在你所需要的只是对vehicleTags(vid, tid).

或者,除了简单的外键之外,还有其他方法可以创建约束:根据您的数据库,您是否可以编写自定义约束或插入/更新触发器来强制车辆标签的唯一性?

于 2008-12-16T23:51:52.107 回答
0

我需要解决这个确切的问题(相同的通用领域和所有东西——汽车零件)。我发现该问题的最佳解决方案是使用 Lucene/Xapian/Ferret/Sphinx 或您喜欢的任何全文索引器。比 SQL 所能提供的性能要好得多。

这些天来,我几乎从来没有最终构建一个不涉及全文索引器的数据库支持的 Web 应用程序。这个问题和搜索的一般问题经常出现,以至于无法从您的工具箱中省略索引器。

于 2009-04-24T16:46:39.830 回答