【问题标题】:Database best practices数据库最佳实践
【发布时间】:2010-05-27 06:02:07
【问题描述】:

我有一个存储 cmets 的表,评论可以来自另一个用户,也可以来自另一个配置文件,它们是这个应用程序中的独立实体。

我最初的想法是该表将同时具有 user_id 和 profile_id 字段,因此如果用户提交评论,它会给出 user_id 将 profile_id 留空

这是对还是错,有更好的方法吗?

【问题讨论】:

    标签: database polymorphic-associations


    【解决方案1】:

    无论是最佳解决方案,恕我直言,不仅取决于表格,还取决于应用程序中其他地方的使用方式。

    假设 cmets 都与某个其他对象相关联,假设您从该对象中提取所有 cmets。在您提出的设计中,提取所有 cmets 只需要从一个表中进行选择,这是有效的。但这是提取 cmets 而不提取有关每个评论的发布者的信息。也许您不想显示它,或者它们已经缓存在内存中。

    但是,如果您必须在检索 cmets 时检索有关海报的信息怎么办?然后你必须加入两个不同的表,现在结果记录集被很多 NULL 值污染(对于配置文件评论,所有用户字段都将为 NULL)。必须解析此结果集的代码也可能变得更复杂。

    就个人而言,我可能会从完全规范化的版本开始,然后当我开始看到性能问题时再去规范化

    这个问题还有一个完全不同的可能解决方案,但这取决于它在域中是否有意义。如果应用程序中还有其他地方可以互换使用用户和海报怎么办?如果用户只是一种特殊的配置文件怎么办?然后我认为解决方案应该在用户/配置文件表中解决。例如(一些缩写的伪sql):

    create table AbstractProfile (ID primary key, type ) -- type can be 'user' or 'profile'
    create table User(ProfileID primary key references AbstractProfile , ...)
    create table Profile(ProfileID primary key references AbstractProfile , ...)
    

    然后,在您的应用程序中,用户或个人资料可以互换使用的任何地方,您都可以引用 LoginID。

    【讨论】:

    【解决方案2】:

    如果 cmets 对于多个对象是通用的,您可以为每个对象创建一个表:

    user_comments (user_id, comment_id)
    profile_comments (profile_id, comment_id)
    

    那么您的 cmets 表中不必有任何空列。它还将使将来添加新的评论源对象变得容易,而无需触及 cmets 表。

    【讨论】:

    • 我喜欢这个解决方案,但是按评论者或评论者类型对 cme​​ts 进行简单过滤需要加入。
    • 确实 - 但是连接是一个小缺点,并且始终可以为此创建视图或存储过程,因此您不必在每次需要时都考虑连接数据。
    • 是的,我同意这是最干净的解决方案。但也许在某些情况下(数百万 cmets)最好进行非规范化。
    【解决方案3】:

    另一种解决方法是始终非规范化(复制)评论中评论者的名称,并通过类型和 id 字段将引用存储回评论者。这样您就有了一个统一的 cmets 表,您可以在其中快速搜索、排序和修剪。缺点是评论与其所有者之间没有任何真正的 FK 关系。

    【讨论】:

    • 目前提到的方法中,我更喜欢这一种。是的,没有真正的外键关系;这总是一个缺点,但没有多少 Web 应用程序担心这一点!这是最可扩展的方式,无需创建许多不同的表来存储相似的信息。
    • 我不明白。你是说,存储评论者姓名的副本,但没有 FK?
    • @user270797:评论数据已经上桌了。评论者的数据(姓名、电子邮件等)应在评论表上复制(非规范化)以便于访问。这种解决方案在某些情况下确实有意义。
    【解决方案4】:

    过去我使用了一个集中式 cmets 表,并为它所引用的 fk_table 提供了一个字段。

    例如:

    comments(id,fk_id,fk_table,comment_text)
    

    这样您就可以使用 UNION 查询来连接来自多个来源的数据。

    SELECT c.comment_text FROM comment c JOIN user u ON u.id=c.fk_id WHERE c.fk_table="user"
    UNION ALL
    SELECT c.comment_text FROM comment c JOIN profile p ON p.id=c.fk_id WHERE c.fk_table="profile"
    

    这确保您可以在不创建冗余表的情况下扩展具有 cmets 的对象的数量。

    【讨论】:

      【解决方案5】:

      这是另一种方法,它允许您通过外键维护引用完整性、集中管理并使用标准数据库工具(如索引以及如果您确实需要分区等)提供最高性能:

      create table actor_master_table(
        type char(1) not null, /* e.g. 'u' or 'p' for user / profile */
        id varchar(20) not null, /* e.g. 'someuser' or 'someprofile' */
        primary key(type, id)
      );
      
      create table user(
        type char(1) not null,
        id varchar(20) not null,
        ...
        check (id = 'u'),
        foreign key (type, id) references actor_master_table(type, id)
      );
      
      create table profile(
        type char(1) not null,
        id varchar(20) not null,
        ...
        check (id = 'p'),
        foreign key (type, id) references actor_master_table(type, id)
      );
      
      create table comment(
        creator_type char(1) not null,
        creator_id varchar(20) not null,
        comment text not null,
        foreign key(creator_type, creator_id) references actor_master_table(type, id)
      );
      

      【讨论】:

        猜你喜欢
        • 2012-09-16
        • 1970-01-01
        • 2010-11-06
        • 2018-11-19
        • 1970-01-01
        • 1970-01-01
        • 2014-08-01
        • 2015-09-10
        • 2012-05-16
        相关资源
        最近更新 更多