将4列作为主键是否合适?

时间:2016-12-27 12:14:41

标签: sql sql-server tsql

我的数据库表格Students包含PK Student_ID,课程包含PK Course_ID

两个表格用于保存每个课程的反馈结果,表Questions我保存在PK question_ID的反馈问题和表格反馈中。

我想知道我是否可以使用PK course_ID

的反馈表(student_Idquestion_IDfeedback_ID)中的3个外键

我认为为每个问题或学生或课程提供结果很有用,但我不知道是否可以使用4列作为主键,不管是否好。

2 个答案:

答案 0 :(得分:3)

如果Feedback_Id唯一标识记录,那么将其作为主键应该可以正常工作。在PK中包含多个列可能会在将来造成麻烦。

让我们说你想坚持其他反馈细节(比如评论)。您想要定义一个名为FeedbackComment的表,该表应将Feedback作为父级。 FK只能转到一个或多个定义了UNIQUE约束的列。通常,PK是PK的目标。

当然,您可以在子表中定义PK的所有列(feedback_id,course_id等),但这会使连接更复杂。

此外,如果您在应用程序的服务层中使用某种ORM(即实体框架),则使用单个整数主键可能很有用(例如,具有基于整数标识符检索实体的泛型方法)。

正如戈登所说,复合主键没有任何问题,但考虑一下你对表的处理方式,而不是在必须进行应用程序扩展时使你的生活复杂化。

答案 1 :(得分:3)

由于大多数人认为主键是群集密钥,因此我将您的问题解释为“将4列作为群集密钥是好还是坏”。

您正在考虑的情况与The Clustered Index DebateSurrogate Key vs. Natural Key等辩论有关。

在这种情况下,我想考虑12字节宽复合群集密钥与4字节宽群集密钥的影响(如果您要使用bigint,则为两倍)。我的决策树看起来像这样:

  1. 我们会使用Hekaton(内存中OLTP)吗?

    • 是=>复合键。 Run away.

    • 否=>好的电话,继续......

  2. 此表有多少行?

    • 数千万,甚至更多! =>代理键(可能)。

      • 如果每行的数据长度是可变的而不是狭窄的=>代理关键。

      • 如果行的数据长度固定且缩小,则会导致页面使用最佳=>继续...

    • 小于那个=>继续...

  3. 如何查询反馈表?

    • course_id, student_Id, question_id =>的各种组合,并非总是如此。代理关键。

      • 在这种情况下,您可能希望能够为查询提供多个支持索引course_id, student_Id, question_id的组合。 聚簇键包含在所有非聚簇索引中,并且它越大,每个索引条目将需要的空间/页面越多。 =>代理钥匙。
    • 几乎总是由所有三个course_id, student_Id, question_id或几乎总是由course_idcourse_id, student_Id; 但不是student_id没有course_id而不是question_id没有course_id, student_id(此表上只有几个非聚集索引)=>继续...

  4. 其他任何表都会引用此表吗?

    • 是的:例如课程讲师将能够就反馈问题的回答发表评论或评论。 =>代理关键。

    • 有点......审计/历史记录表将跟踪此表中行的插入/更新/删除。

      • 代理密钥可以使更改跟踪不那么复杂以进行审查,并且审计表的聚类密钥很可能是代理密钥& datetime或代理键vs复合键& datetime或代理键。
    • 否=>复合键是一个合理的选择

  5.  

    即使我第一次运行上面的决策树导致我使用复合键,我可能会使用代理键开始我的设计,因为它更容易摆脱它(因为它没有被使用)返回并添加并实现其使用。

    为了澄清一下,我遇到了一些案例,我确实发现复合键是一个更好的解决方案并且重构了设计以放弃代理键。我不想留下代理键始终是更好的解决方案的印象,即使它是许多设计师(包括我自己)的常见默认设置。

    我会从这样的事情开始:

    create table feedback (
        feedback_id    int       not null identity(1,1)
      , course_id      int       not null
      , student_id     int       not null
      , question_id    int       not null
      , response_added datetime  not null
          constraint df_feedback_response_added_gd default getdate()
      , response       nvarchar(max) null
      , constraint pk_feedback primary key clustered (feedback_id)
      , constraint fk_feedback_course   foreign key (course_id)   
          references course(course_id)
      , constraint fk_feedback_students foreign key (student_id)  
          references student(student_id)
      , constraint fk_feedback_question foreign key (question_id) 
          references question(question_id)
      , constraint uq_feedback_course_student_question 
          unique (course_id, student_Id, question_id)
       /* or create a unique index to use include() instead */
      );
    
      /* unique index that includes response */
      create unique nonclustered index ux_feedback_course_student_question_covering
          on feedback (course_id, student_Id, question_id) 
            include (response_added, response);
    

    参考: