是否可以在子表的复合外键中使用引用(通过外键)祖父母行中的字段?

时间:2012-06-29 14:33:26

标签: mysql composite-key

完整问题:

从通过外键链接到其祖父表中的行的子表(引用祖父项主键),是否可以使用子表的复合外键中引用的祖父表行中的字段?

我当前的数据库设计有一个主要的父表项目项目有两个子表, JobTitles 任务任务然后有一个子表子任务子任务然后有一个子表分配,这是一个将员工置于子任务的x-ref表。我遇到的问题是将 JobTitles 分配给分配; JobTitles 属于项目,因此任何指定的项目下的作业应该只能引用<分享项目的strong> JobTitles 。我最近询问this question关于将 JobTitle 的选择限制为与共享项目的人。但是,从那以后我发现复合外键是一个更清洁的解决方案。

简单的数据库布局:

  • 项目
    • JobTitles
    • 任务
      • 子任务
        • 分配

我已经找到了如何创建复合键here,但为此我需要使用Project的主键作为复合外键的一部分。

表:

  • 项目
    • ProjectName (PK)
    • ProjectID(唯一索引)
  • JobTitles
    • JOBTITLE
    • ProjectID(外键 - &gt; Projects.ProjectID)(复合PK:JobTitle-ProjectID)
    • JobTitleID(唯一索引)
  • 任务
    • TASKNAME
    • ProjectID(外键 - &gt; Projects.ProjectID)(复合PK:TaskName-ProjectID)
    • TaskID(唯一索引)
  • 子任务
    • SubtaskName
    • TaskID(外键 - &gt; Tasks.TaskID)(复合PK:子任务名称 - 任务ID)
    • 子任务ID(唯一索引)
  • 分配
    • EmployeeID(外键)
    • SubTaskID(外键 - &gt; Subtasks.SubtaskID)(复合PK:EmployeeID-SubtaskID)
    • JobTitleID(外键 - &gt; JobTitles.JobTitleID)
    • AssignmentID(唯一索引)

为了将JobTitle分配给Assignment,我想使用Assignment的ProjectID(来自其父Task)及其选择的JobTitleID来设置复合外键。唯一的问题是我不知道如何抓住两代之后的ProjectID用于密钥。通过将ProjectID包装在每个表中的复合键中,可以将ProjectID向下传递给每一代,但考虑到它们对性能的影响,复合键不会出现在willy nilly周围的东西(更不用说传递值了)下来似乎有点草率)。有没有办法到达ProjectID以便在密钥中使用而不通过其他表?

1 个答案:

答案 0 :(得分:2)

正如您所说,您可以通过每个表传递ProjectID。我不认为这很邋and,它使您能够创建复合主键,以便重用例如不同项目中的TaskID。一旦人们意识到这一点,就会发现在每个表格中使用ID列的常见做法有点多余。可以使用语义上有意义的数据作为键,这通常是我的偏好(确定它在空间方面成本更高,但索引对时间的影响相对较小):

CREATE TABLE Projects (
  ProjectName VARCHAR(20) NOT NULL PRIMARY KEY
);

CREATE TABLE JobTitles (
  ProjectName VARCHAR(20) NOT NULL,
  JobTitle    VARCHAR(20) NOT NULL,
  PRIMARY KEY (ProjectName, JobTitle),
  FOREIGN KEY (ProjectName) REFERENCES Projects (ProjectName)
);

CREATE TABLE Tasks (
  ProjectName VARCHAR(20) NOT NULL,
  TaskName    VARCHAR(20) NOT NULL,
  ParentTask  VARCHAR(20),
  PRIMARY KEY (ProjectName, TaskName),
  FOREIGN KEY (ProjectName) REFERENCES Projects (ProjectName),
  FOREIGN KEY (ProjectName, ParentTask) REFERENCES Tasks (ProjectName, TaskName)
);

CREATE TABLE Assignments (
  ProjectName VARCHAR(20)  NOT NULL,
  TaskName    VARCHAR(20)  NOT NULL,
  JobTitle    VARCHAR(20)  NOT NULL,
  Email       VARCHAR(255) NOT NULL,
  PRIMARY KEY (ProjectName, TaskName, JobTitle),
  FOREIGN KEY (ProjectName) REFERENCES Projects (ProjectName),
  FOREIGN KEY (ProjectName, TaskName) REFERENCES Tasks (ProjectName, TaskName),
  FOREIGN KEY (ProjectName, JobTitle) REFERENCES JobTitles (ProjectName, JobTitle),
  FOREIGN KEY (Email) REFERENCES Employees (Email)
);

由于MySQL不支持更强大的约束验证,我能想到的唯一其他选择是在BEFORE INSERT上定义引发错误以拒绝数据的BEFORE UPDATEAssignments触发器包含无效的JobTitle。但是,您还需要在JobTitles上创建触发器来处理此类引用记录更改或删除的情况;然后还有所有其他可能导致联系中断的表格。丑丑丑陋。

因此,我倾向于使用上面给出的第一种方法。