1

完整问题:

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

我当前的数据库设计是有一个主父表ProjectsProjects有两个子表JobTitlesTasksTasks然后有一个子表SubtasksSubtasks然后有一个子表Assignments,这是一个将员工放在Subtasks上的外部参照表。我遇到的问题是将JobTitles分配给AssignmentsJobTitles属于一个Project,因此任何给定Project下的Assignments应该只能引用共享一个Project的JobTitles。我最近问了这个关于将JobTitle选择限制为共享项目的人的问题。但是,从那时起,我发现复合外键是一种更清洁的解决方案。

简单的数据库布局:

  • 项目
    • 职称
    • 任务
      • 子任务
        • 作业

我在这里找到了如何创建复合键,但为此我需要使用项目的主键作为复合外键的一部分。

表:

  • 项目
    • 项目名称(PK)
    • ProjectID(唯一索引)
  • 职称
    • 职称
    • ProjectID(外键-> Projects.ProjectID)(复合PK:JobTitle-ProjectID)
    • JobTitleID(唯一索引)
  • 任务
    • 任务名称
    • ProjectID(外键-> Projects.ProjectID)(复合PK:TaskName-ProjectID)
    • TaskID(唯一索引)
  • 子任务
    • 子任务名称
    • TaskID(外键-> Tasks.TaskID) (复合PK: SubtaskName-TaskID)
    • SubtaskID(唯一索引)
  • 作业
    • EmployeeID(外键)
    • SubTaskID(外键-> Subtasks.SubtaskID)(复合PK:EmployeeID-SubtaskID)
    • JobTitleID(外键 -> JobTitles.JobTitleID)
    • 分配 ID(唯一索引)

为了将 JobTitle 分配给分配,我想使用分配的 ProjectID(来自其父任务)及其选定的 JobTitleID 设置复合外键。唯一的问题是我不知道如何获取两代之后的 ProjectID 用于密钥。可以通过将 ProjectID 包装在每个表中的复合键中来将其传递到每一代,但考虑到它们对性能的影响(更不用说传递值),复合键并不是随便扔的东西下来似乎有点草率)。有没有办法在不通过其他表的情况下获取 ProjectID 以在密钥中使用?

4

1 回答 1

2

正如您所说,您可以通过每个表向下传递 ProjectID。我不认为这很草率,它使您能够创建复合主键,从而可以在不同的项目中重复使用例如 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 INSERTBEFORE UPDATE触发Assignments引发错误以拒绝包含无效 JobTitle 的数据。但是,您还需要创建触发器JobTitles来处理此类引用记录发生更改或被删除的情况;然后还有可能导致链接中断的所有其他表。丑丑丑。

因此,我更喜欢上面给出的第一种方法。

于 2012-06-29T14:58:13.467 回答