类别和标签的数据结构

时间:2015-02-26 21:08:32

标签: mysql database database-design

我正在构建一个业务目录,用户可以在其中为其业务创建列表。目录的结构如下:

  • 有分类和标签
  • 一个类别可以有很多标签。标签只能属于一个类别
  • 列表已分配到类别和类别标签

目前的数据结构如下:

category               tag
============           ===========
id                     id
name                   name
                       category_id (FK)


listing                listing_category           listing_category_tag
============           ================           ====================
id                     id                         listing_category_id (FK)
name                   listing_id (FK)            tag_id (FK)
etc                    category_id (FK)

随着目录的建立,我将不可避免地想要添加新的类别和标签,现有的可能必须归档。这可能意味着需要更新一些列表以引用新创建的类别和标签。

此数据结构是否有效设置以处理此类情况?我想避免列表中的标签与当前类别分配不匹配的情况。

4 个答案:

答案 0 :(得分:1)

为了保持数据库级别的一致性,我建议在联结表中使用自然复合主键而不是代理标识符:

category               tag
============           ================
id     (PK)             name        (PK)(UC)             
name   (UC)             category_id (PK)(FK)


listing               listing_category           listing_category_tag
============          ================           ====================
id   (PK)             category_id(FK)(PK)        listing_category.PK (FK)
name (UC)             listing_id (FK)(PK)        tag.PK (FK)
etc                    

我们最终会得到这样的结论:

listing_category_tag
====================
listing_category_category_id (FK)
listing_category_listing_id(FK)
tag_name(FK)
tag_category_id(FK)

现在有检查约束,如

check_constraint_1 = {listing_category_category_id = tag_category_id}

会阻止不一致

答案 1 :(得分:1)

您提出的设计是合理的。我会在这里列出一些灰色区域,而不是挑战你的方法以激发思想:

  • listing_category引用类别。标签也引用类别。 listing_category_tag引用标记和列表类别,因此如果未维护引用完整性,则可以使用listing_category_tag引用与标记类别中的不同类别相关联的列表。我认为这表明设计不在Boyce-Codd Normal Form中,但可能是第3次正常?

  • 正如您所描述的那样,随着时间推移更改类别和标签的可能性,这可能会成为一堆参考依赖项。我知道这在理论上听起来很棒,但在实际操作中,当设计允许引用不一致时,小错误很容易复合。 (是的,约束,但在实际操作中,这种情况很容易摆脱困境。)

您是否考虑过更简单,更规范的非规范化方法?这变得越来越普遍,并且在一些非常大且成功的站点上被广泛使用。例如:

listing           listing_category_tag
==========        ====================
id                listing_id
name              category_name 
etc.              tag_name

在listing_category_tag上放置一个复合索引(category_name,tag_name)。如果您需要一个类别列表,只需select distinct(category_name),当您想要一个类别的唯一标记列表时,select distinct(tag_name) where category=?

您可能会惊讶地发现,即使是数百万个列表,它的表现也会有多好,以及实现和维护错误的程度会更简单,也更不容易出错。

我希望这有用!

答案 2 :(得分:0)

您的数据结构非常适合您尝试解决的挑战。

  

这可能意味着需要更新一些列表以引用新创建的类别和标签......

     

我希望避免列表中的标签与当前类别分配不匹配的情况。

这可以通过几种不同的方式实现。

  • 您可以从应用程序代码中的业务逻辑层处理引用完整性,从而使数据库不可知。
  • 您可以在外键上强制数据库中的引用完整性。

如果您选择第一种方法,则无需在您已经拥有的模型上进行任何操作。当您更新或插入新的列表/标签/类别时,应用程序代码必须确保它们有效。

如果选择第二种方法,则将listing_category_tag中的外键更改为复合外键,并指定适当的约束。

listing_category_tag
====================
listing_category_id (FK: listing_category)       
tag_id (FK: tag)
category_id (FK: tag)

The MySQL Documentation有很好的例子,说明如何使用约束。

答案 3 :(得分:0)

我认为您的设计在强制执行包含约束时,所有已分配的标记必须与列表具有相同的类别,这是非常复杂的。更简单,更自然的设计也强制执行包含约束,如下所示

category               tag
============           ===========
id                     category_id (FK)
name                   name
                       PK( category_id, name)

listing                listing_category           listing_tag
============           ================           ====================
id                     listing_id (FK)            listing_id (FK)
name                   category_id (FK)           category_id (FK)    
etc                                               tag_name (FK)