试图避免“多态关联”并维护外键参照完整性

时间:2011-08-30 17:16:24

标签: mysql database-design comments photo polymorphic-associations

我正在建立一个类似于Yelp的网站(推荐引擎,虽然规模较小),因此系统中将有三个主要实体:用户,地方(包括商家)和活动。

现在我想知道的是如何为每种类型的实体存储照片,评论和“赞美”(类似于Facebook的“赞”)之类的信息,以及如何为每个对象存储它们(例如评论推荐,照片等)。现在,我这样做的方式就是每个人都有一张表。

  

照片(ID,类型 owner_id ,is_main等...)
  其中type表示:1 = user,2 = place,3 = event

     

评论(id, object_type,object_id ,user_id,内容等等...)
  其中object_type可以是一些不同的对象,如照片,推荐等

     

赞美( object_id object_type ,compliment_type,user_id)
  其中object_type可以是一些不同的对象,如照片,推荐等

     

活动(ID,来源, source_type source_id 等等。)//for "activity feed"
  其中source_type是用户,地点或事件

     

通知(ID,收件人,发件人,activity_type, object_type object_id 等...)
   where object_type& object_id将用于提供到通知对象的直接链接,例如用户赞美的照片

但是after reading在SO上有一些posts,我意识到我无法使用外键保持参照完整性,因为这需要1:1的关系和我的source_id / object_id字段可以与多个表中的ID相关联。所以我决定采用保持主实体的方法,然后将其分解为子集,即

  

User_Photo(photo_id,user_id)| Place_Photo(photo_id,place_id)|等...

     

Photo_Comment(comment_id,photo_id)| Recommendation_Comment(comment_id,rec_id)|等...

     

赞美(id,...)//would need to add a surrogate key to Compliment table now

     

Photo_Compliment(compliment_id,photo_id)| Comment_Compliment(compliment_id,comment_id)|等...

     

User_Activity(activity_id,user_id)| Place_Activity(activity_id,place_id)|等...

我在想我可以创建将每个子表连接到主表的视图,以获得我想要的结果。另外我认为它也适合Code Igniter中的对象模型。

我认为我唯一可以离开的表是通知表,因为有很多对象类型(论坛帖子,照片,推荐等等),这个表只会保留一周的通知,所以任何参考完整性问题应该不是问题(我认为)。

我是否以明智的方式解决这个问题?我可能忽略的任何性能,可靠性或其他问题?

我能看到的唯一“问题”是我最终会得到很多表格(因为现在我有大约72个,所以我想在添加之后我最终会得到一些不到90个表格额外的),就我所知,这不是问题。

非常感谢任何反馈。提前谢谢。

编辑:为了清楚起见,我并不担心我是否会以另外10张桌子结束。据我所知,表的数量并不是一个问题(一旦被使用)......除非你说20​​0左右:/

3 个答案:

答案 0 :(得分:6)

这个UoD(话语世界)的一些命题

  • 名为Bob的用户已登录。
  • 名为Bob的用户上传了第56号照片。
  • 有一个名叫伦敦的地方。
  • 第56号照片名为伦敦。
  • 名为Joe的用户在第56号照片上创建了评论“非常好”。

引入对象ID

  • 用户(UserID)已登录。
  • 用户(UserID)上传了Photo(PhotoID)。
  • 有Place(PlaceID)。
  • 照片(PhotoID)是Place(PlaceID)。
  • 用户(UserID)在Photo(PhotoID)上创建了评论(CommentID)。

事实类型

  • 用户已登录。
  • 用户上传了照片。
  • 存在。
  • 照片是Place。
  • 用户创建了对照片的评论。

现在提取谓词

Predicate               Predicate Arity
---------------------------------------------
... logged in            1 (Unary predicate)
... uploaded ...         2 (Binary)
... exists               1 (Unary) 
... is of ...            2 (Binary)
... created ... on ...   3 (Ternary)

看起来每个命题是这个UoD可以用最大三元谓词来表示, 所以我建议像

这样的东西

enter image description here

谓词角色 (Role_1_ID, Role_2_ID, Role_3_ID)是对象在谓词中扮演的角色。每个...从左到右替换谓词中的Role_ID。 请注意,只有Role_1_ID是必需的(至少是一元谓词),其他两个可能是NULL。

在这个简单的模型中,可以提出任何内容。 因此,您需要在应用程序层上实现约束。 例如,您必须确保可以在Comment上创建Place,但不能在Place上创建Place。 并非所有谓词都代表操作,例如... logged in是一个操作,而... is of ...则不是。 因此,您的活动Feed 会将所有Propositions列为Predicate.IsAction = True

答案 1 :(得分:3)

如果您稍微重新排列,可以简化您的评论和赞美。基本上你想要一个评论商店和另一个赞美商店。您的问题是,这不会让您使用声明性参照完整性(外键约束)。

解决这个问题的方法是确保能够吸引评论和赞美的对象都是一个超类型的逻辑子类型。从逻辑的角度来看,这意味着你有一个“THING_OF_INTEREST”实体(我在这里做出命名约定推荐!)并且每个吸引评论和赞美的具体事物都将是一个子 - THING_OF_INTEREST的类型。因此,您的评论表将具有“thing_of_interest_id”FK列,并且您的赞美表也类似。您仍将拥有子类型表,但它们将具有1:1 FK和THING_OF_INTEREST。换句话说,THING_OF_INTEREST可以为您提供单个主键域,而所有子类型表都包含特定于类型的属性。通过这种方式,您仍然可以使用声明性参照完整性来强制执行您的评论和称赞关系,而无需为不同类型的评论和赞美提供单独的表。

物理实施角度来看,最重要的是您感兴趣的各种事物都共享一个共同的主键域。这就是让你的评论表有一个FK值的东西,可以很容易地与任何感兴趣的东西联系起来。

根据你的评论和建议的方式,你可能(但可能不会)需要物理实现THING_OF_INTEREST - 它至少有两个属性,主键(通常是一个int)加上一个分区属性告诉你你是哪种子类型。

答案 2 :(得分:2)

如果您需要referential integrity (RI),没有比使用多对多联结表更好的方法了。没错,你最终在系统中有很多表,但这是你需要付出的代价。它还有一些其他好处,例如你可以免费获得某种分区:你可以通过它们的关系类型分区数据,每个数据都在自己的表中。这提供了RI,但它也不是100%安全,例如没有什么可以保证评论属于照片和单独的照片,你需要在需要时手动强制执行这种约束。

另一方面,使用像您已经做过的通用解决方案可以让您更快地开始实施,并且将来更容易扩展,但除非您手动编码,否则不会有RI(这是非常的比每种关系类型的替代M:M更复杂且更难处理。

仅提及另一种替代方法,类似于您现有的实现,您可以使用自定义M:M联结表来处理所有关系,无论其类型如何:object1_type, object1_id, object2_type, object2_id。简单但除了非常容易实现和扩展之外没有其他好处。我只推荐它,如果你不需要RI而你自己有很多桌子,都是相互关联的。