数据模型,替代EAV

时间:2013-11-19 00:01:49

标签: mysql database-design doctrine-orm entity-attribute-value

我有一个产品数据库,我想在产品上附加文字,图片和视频。我还希望每个实体(文本,图像或视频)都有一个标签,以便进一步组织应用程序。

我想过使用这个模型:

内容:

content_id|content_product_id|content_type|content_tag_id|content_url|content_title|content_text

标签

tag_id|tag_name

这意味着使用Entity(content_product_id) - Attribute(content_tag_id) - Value(content_url或content_title | content_text)模型。

在阅读了很多内容之后,我明白使用这种建模模式(描述为数据库反模式,不可扩展并导致性能问题)是一个坏主意,您想知道替代方法吗?

我想使用Doctrine ORM,我想找到一种方便与该数据映射器兼容的方法

2 个答案:

答案 0 :(得分:4)

我会为任何类型的内容创建一个通用表格:

CREATE TABLE ProductContents(
  content_id INT AUTO_INCREMENT PRIMARY KEY,
  content_type INT NOT NULL
  -- other general attributes like when it was created, by whom, etc.
);

对于每个文字,图片或视频,请在此表格中插入一行。如果使用自动增量主键,则此表负责生成ID号。

对于标签,现在您只需在ProductContent和Tags之间建立多对多关系。这由交叉表表示。

CREATE TABLE Tags (
  tag_id INT AUTO_INCREMENT PRIMARY KEY,
  tag TEXT NOT NULL
);

CREATE TABLE ProductContentTagged (
  content_id INT, 
  tag_id INT, 
  PRIMARY KEY (content_id, tag_id),
  FOREIGN KEY (content_id) REFERENCES ProductContents(content_id),
  FOREIGN KEY (tag_id) REFERENCES Tags(tag_id),
);

然后,如果您有任何特定于每种类型内容的属性,请为每种类型创建辅助表,并与内容表保持一对一的关系。

CREATE TABLE ProductContentTexts (
  content_id INT PRIMARY KEY,
  content TEXT NOT NULL,
  FOREIGN KEY (content_id) REFERENCES ProductContents(content_id)
);

CREATE TABLE ProductContentImages (
  content_id INT PRIMARY KEY,
  image_path TEXT NOT NULL,
  FOREIGN KEY (content_id) REFERENCES ProductContents(content_id)
);

CREATE TABLE ProductContentVideos (
  content_id INT PRIMARY KEY,
  video_path TEXT NOT NULL,
  FOREIGN KEY (content_id) REFERENCES ProductContents(content_id)
);

请注意,这些辅助表具有自动增量列。他们不需要 - 他们将始终使用ProductContents表生成的值,并且您负责插入该值。

答案 1 :(得分:3)

Bill Karwin的回答非常好。

但是,既然你说:

  

我想使用Doctrine ORM,我想找到一种方便与该数据映射器兼容的方法

我会将他的答案与特定的ORM联系起来。

Bill描述的是继承。您有一个超级类“内容”,由包含所有共享数据的表表示。然后你有子类(文本,图像,视频),通过添加特定于内容类型的列来扩展该超类。

Doctrine2基本上会使用Bill在您使用class-table inheritance时建议的内容。正确配置实体后,它将创建一组与Bill描述的非常相似的表。

因此,使用Doctrine,您可以使用图像,文本和视频扩展内容实体。

就标记而言,您只需创建一个基本的Tag实体,而Content将与Tag具有ManyToMany关系。 Doctrine将为您创建中间表。