Full Question:
From a child table linked to a row in its grandparent table by a foreign key (references grandparent primary key), is it possible to use a field from the referenced grandparent row in the child table’s composite foreign key?
My current database design is has a main parent table, Projects. Projects has two child tables, JobTitles and Tasks. Tasks then has a child table Subtasks. Subtasks then has a child table Assignments, which is a x-ref table putting employees on Subtasks. The problem I’ve come up to is assigning JobTitles to Assignments; JobTitles belong to a Project, and as such the Assignments under any given Project should only be able to reference JobTitles that share a Project. I recently asked this question regarding limiting choice of JobTitle to those sharing a project. However, since then I’ve found a composite foreign key to be a much cleaner solution.
Simple database layout:
- Projects
- JobTitles
- Tasks
- Subtasks
- Assignments
- Subtasks
I’ve found how to create a composite key here, but for this I would need to use the Project’s primary key as part of the composite foreign key.
Tables:
- Projects
- ProjectName (PK)
- ProjectID (Unique Index)
- JobTitles
- JobTitle
- ProjectID (Foreign Key -> Projects.ProjectID) (Composite PK: JobTitle-ProjectID)
- JobTitleID (Unique Index)
- Tasks
- TaskName
- ProjectID (Foreign Key -> Projects.ProjectID) (Composite PK: TaskName-ProjectID)
- TaskID (Unique Index)
- Subtasks
- SubtaskName
- TaskID(Foreign Key -> Tasks.TaskID) (Composite PK: SubtaskName-TaskID)
- SubtaskID (Unique Index)
- Assignments
- EmployeeID (Foreign Key)
- SubTaskID (Foreign Key -> Subtasks.SubtaskID) (Composite PK: EmployeeID-SubtaskID)
- JobTitleID (Foreign Key -> JobTitles.JobTitleID)
- AssignmentID (Unique Index)
In order to assign a JobTitle to an Assignment, I want to set up a composite foreign key using an Assignment’s ProjectID (from its parent Task) and its selected JobTitleID. The only problem is that I have no idea how to grab the ProjectID, which is two generations away, for use in a key. It could be possible to pass the ProjectID down through each generation by wrapping it in a composite key within each table, but composite keys aren’t something something to throw around willy nilly considering the toll they take on performance (not to mention passing the value down seems a bit sloppy). Is there any way to get to the ProjectID for use in the key without passing it through the other tables?
As you say, you can pass the ProjectID down through each table. I don’t think that’s sloppy, and it enables you to create composite primary keys that enable reuse of e.g. TaskIDs across different projects. Once one realises that, one observes that the common practice of using
IDcolumns in every table is a little superfluous. One can instead use semantically meaningful data as keys, which is usually my preference (sure it’s more costly in terms of space, but the indexing results in relatively little impact on time):Since MySQL doesn’t support more powerful constraint validation, the only other option I can think of would be to define
BEFORE INSERTandBEFORE UPDATEtriggers onAssignmentsthat raise errors to reject data that contains an invalid JobTitle. However, then you also would need to create triggers onJobTitlesto handle situations where such referenced records change or are deleted; and then also on all the other tables that could give rise to a break in linkage. Ugly ugly ugly.Hence my preference is with the first approach given above.