如何设计数据库模式以支持使用类别标记?

Pyr*_*cal 15 sql tags schema foreign-keys entity-attribute-value

我正在尝试像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
Run Code Online (Sandbox Code Playgroud)

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

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

我目前的设计是这样的:

vehicles
  int vid
  String vin

vehicleTags
  int vid
  int tid

tags
  int tid
  String tag
  int cid

categories
  int cid
  String category
Run Code Online (Sandbox Code Playgroud)

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

我可以在vehicleTags中为复合主键添加外键约束吗?IE浏览器.我是否可以添加一个约束,使得复合主键(vid,tid)只能在vehicleTags中没有行时才添加到vehicleTags,这样对于同一个vid,还没有一个tid在同样的cid?

我的猜测是否定的.我认为这个问题的解决方案是向vehicleTags添加一个cid列,并创建新的复合主键(vid,cid).它看起来像:

vehicleTags
  int vid
  int cid
  int tid
Run Code Online (Sandbox Code Playgroud)

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

我的架构应该是什么?

在我之前的问题中,Tom在我的数据库模式中发现了这个问题,你如何做多对多表外连接?

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

Bil*_*win 15

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

更易识别的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)
);
Run Code Online (Sandbox Code Playgroud)

有些人强制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"
);
Run Code Online (Sandbox Code Playgroud)

如果你打算使用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)
);
Run Code Online (Sandbox Code Playgroud)

但请记住,您正在将数据与元数据混合在一起.您将无法将某些约束应用于数据模型.

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

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

为这些流体非关系数据模型设计的另一种技术是语义数据库,它将数据存储在RDF中并使用SPARQL进行查询.一个免费的解决方案是芝麻.