【问题标题】:Defining multiple foreign keys in one table to many tables在一个表中定义多个外键到多个表
【发布时间】:2011-05-02 08:03:26
【问题描述】:

我有 3 个模型:

发帖

  • 身份证
  • 标题
  • 身体

照片

  • 身份证
  • 文件路径

评论

  • 身份证
  • post_id
  • 身体

和数据库中的相应表。现在,如果我只想为我的帖子添加 cmets,我可以简单地添加以下外键:ALTER TABLE comment ADD FOREIGN KEY (post_id) REFERENCES post (id)。但我想为其他模型(照片、个人资料、视频等)提供 cmets,并将所有 cmets 保存在 一个 表中。在这种情况下,我该如何定义外键(ORM 肯定需要 FK)?

【问题讨论】:

    标签: database-design foreign-keys


    【解决方案1】:

    你可以这样做:

     post:
      * post_id (PK)
      * title
      * body
    
     photo:
      * photo_id (PK)
      * filepath
    
     comment:
      * comment_id (PK)
      * body
    
     comment_to_post
      * comment_id (PK) -> FK to comment.comment_id
      * post_id (PK) -> FK to post.post_id
    
     comment_to_photo
      * comment_id (PK) -> FK to comment.comment_id
      * photo_id (PK) -> FK to photo.photo_id
    

    仍然有可能拥有属于两个不同项目的评论。如果您认为这是一个问题,我可以尝试改进设计。

    【讨论】:

    • 不,对多个实体发表评论的可能性不是问题。所以我需要 N 个实体的 N 个表? CASCADE 规则在这种情况下是否有效?
    • 就是这样,N张桌子就是这个设计的想法。如果 DMBS 支持它们,级联规则当然应该有效。
    • 感谢您的解释。我可能会选择你的方法。改进设计是什么意思?
    • 我的意思是:想办法让评论 #123 不属于帖子 #456 照片 #789
    • 1.这才是正确的方法。逻辑层的多:多关系被实现为物理层的关联。许多表是规范化数据库的性质;连接不会“花费”任何东西。
    【解决方案2】:

    找一些共同的东西来发帖、个人资料等——我用Entity 是因为缺少更好的词,然后是子类型。

    • 在此模型中,一个实体可以有多个 cmets,一条评论仅属于一个实体。

    【讨论】:

    • 这是一个很好的通用设计技巧。 IF 四个实体有共同的列,可以归一化为超类型,是一种进步; 如果不是,这是一个障碍(例如,没有共同的评论),需要额外的定义(例如,不允许个人资料有评论)。即,您正在解决未发布的问题。
    • 我有许多可注释的实体,它们没有共同的属性。所以,我的超表实体将只有 EntityId 列,我认为这不是好的设计。这就是为什么 Alvaro 的方法与我更相关。
    • 这行得通,但我们如何限制帖子的 cmets 将仅附加特定帖子。并且不能意外附加到任何其他实体。
    【解决方案3】:

    如果您想知道单个列是否可以有多个外键,那么答案是否定的。

    如果你愿意,你可以有单独的外键。所以你可以像这样修改你的评论表 -

     comment:
      * comment_id (PK)
      * PostID (FK to Post.PostID)
      * PhotoID (FK to <Photo>.PhotoID)
      * ProfileID (FK to <Profile>.ProfileID)
      * Body
    

    而且,您必须确保在 Comment 表中的 PostID、PhotoID 和 ProfileID 列中允许为空,并且还可能将默认值设置为空。

    这是实现这一点的 DDL -

    Create table Photo
    (
    PhotoID int,
    PhotoDesc varchar(10),
    Primary key (PhotoID)
    )
    
    Create table Post
    (
    PostID int,
    PostDesc varchar(10),
    Primary key (PostID)
    )
    
    Create table Profiles
    (
    ProfileId int,
    ProfileDesc varchar(10),
    Primary key (ProfileId)
    )
    
    Create table Comment  
    (
    CommentID int,
    PhotoID int,
    PostID int,
    ProfileId int,
    body varchar(10),
    Primary key (CommentID),
    Foreign key (PhotoID) references Photo(PhotoID),
    Foreign key (PostID) references Post(PostID),
    Foreign key (ProfileId) references Profiles(ProfileId)
    )
    
    insert into Photo values (1,'Photo1')
    insert into Photo values (2,'Photo2')
    insert into Photo values (3,'Photo3')
    
    insert into Post values (11,'Post1')
    insert into Post values (12,'Post2')
    insert into Post values (13,'Post3')
    
    insert into Profiles values (111,'Profiles1')
    insert into Profiles values (112,'Profiles2')
    insert into Profiles values (113,'Profiles3')
    
    insert into Comment (CommentID,PhotoID,body) values (21,1,'comment1')
    insert into Comment (CommentID,PhotoID,body) values (22,3,'comment2')
    insert into Comment (CommentID,PostID,body) values (23,11,'comment3')
    insert into Comment (CommentID,PostID,body) values (24,12,'comment4')
    insert into Comment (CommentID,ProfileId,body) values (25,112,'comment5')
    insert into Comment (CommentID,ProfileId,body) values (26,113,'comment6')
    
    -- to select comments seperately for Photos, profiles and posts
    select * from Comment where PhotoID is not null
    select * from Comment where ProfileId is not null
    select * from Comment where PostID is not null
    

    【讨论】:

    • 1.这没有标准化。 2.即使你把不规范的问题放在一边,它也不起作用。当一个 FK 为真时,其他的都是假的。在索引字段上允许 Null 会降低性能。当然,也有没有此类问题的规范化解决方案。
    • 是的,它没有标准化。而且,是的,它会起作用。是的,就性能而言,也许不是最好的。但是我们在一些限制下工作,例如 - 1. galymzhan,在他的问题中说他希望所有 cmets 在一张桌子上。 2.外键是约束,不是索引。外键字段上没有创建隐式索引。
    • 不,它不会在任何ANSI SQL数据库中工作,请在发布前尝试。你不能有一个空的 FK。我和其他许多人一样,给了 OP 一个评论表;这不是问题。您的答案未标准化,因此存在严重的限制和性能问题;这的问题。如果您想继续,请发布一个新问题。
    • 我用的是SQL server 2008。我试过了。检查stackoverflow.com/questions/4057540/… 我再次同意,它没有标准化,这会对性能产生影响。
    • 你没试过,还有很多不明白的地方。无法解决commnets中的差异;它正在劫持这个线程。如前所述:**如果您想继续这样做,请发布一个新问题**并发布您的 DDL。您似乎错过了您的回复中未提供 OP 要求的事实;你违反了他已经实施/要求的规则。
    【解决方案4】:

    在这种情况下,您可以添加一个包含“照片”、“个人资料”的 ENUM 字段...这将是外键的第二部分

    【讨论】:

    • 你能解释一下吗? Post 表是什么样子的?
    【解决方案5】:

    由于照片 cmets 与 post cmets 不同,我会将它们存储在单独的相关表中。所以我会有:

    帖子:

    • PostId
    • 标题
    • 身体

    发表评论:

    • 评论编号
    • post_id 正文

    照片:

    • 照片编号
    • 文件路径

    照片评论:

    • 评论编号
    • photo_id
    • 身体

    使用 id 作为 PK 的名称是一种不好的做法,它会使报告变得更加困难,并且更有可能在复杂查询中无意中加入错误的表。如果您使用 tablenameID 并始终为 Fks 使用相同的名称,那么也更容易看到关系。

    【讨论】:

    • 同意可怜的列命名。但是提交显然与请求相反,即规范化评论。
    猜你喜欢
    • 1970-01-01
    • 2015-04-18
    • 1970-01-01
    • 1970-01-01
    • 2013-10-27
    • 2011-11-19
    • 2013-10-30
    • 1970-01-01
    相关资源
    最近更新 更多