Alim
Alim

Reputation: 337

Foreign key on a Foreign key - SQL Server

I'm creating a database for a project in which I need to declare a foreign key on another foreign key for the sake of a checking constraint.

I have a Person table and a Groups table, both of these contain a DepartmentID. Whenever I insert a new task in the Task table, I want to check that the Groups.DepartmentID matches the Person.DepartmentID.

The idea is that a task is linked to a person and has 2 types, a grouptype which defines if its database work, financial work etc and a tasktype which defines if its maintenance, training etc. When a person tries to add a task with a groupType that is not for his/her department it should fail.

I tried adding these attributes to the Task table as a foreign key, however declaring a foreign key on a non-unique or non-primary key isn't accepted in Microsoft SQL Server (the DepartmentID in the Person and Group tables cannot be unique!).

Anyone knows how to fix this?

CREATE TABLE Department 
(
    ID int PRIMARY KEY IDENTITY,
    Name varchar(50),
    UNIQUE ("Name")
)

CREATE TABLE Groups
(
    ID int IDENTITY,
    GroupType varchar(50) PRIMARY KEY,
    Description varchar(255) DEFAULT ('-'),
    DepartmentID int 
        FOREIGN KEY (DepartmentID) REFERENCES Department(ID),
)

CREATE TABLE Person 
(
    ID int PRIMARY KEY IDENTITY,
    Name varchar(50),
    DepartmentID int 
         FOREIGN KEY (DepartmentID) REFERENCES Department(ID)
)

CREATE TABLE TaskType
(
    ID int IDENTITY,
    TaskType varchar(50) PRIMARY KEY,
    Description varchar(255) DEFAULT ('-'), 
)

CREATE TABLE Task
(
    ID int IDENTITY,
    TimeFrame decimal(4,2),
    Yearcount int,
    GroupType varchar(50),
    TaskType varchar(50), 
    WeekNr int,
    ExceptionDetail varchar(255) DEFAULT ('-'),
    PersonID int
)

These are the FK attributes in the task table that are not accepted:

GDID int FOREIGN KEY REFERENCES Groups(DepartmentID),
PDID int FOREIGN KEY REFERENCES Person(DepartmentID),
CHECK (GDID = PDID),    

UNIQUE ("TaskType", "GroupType", "WeekNr", "Yearcount"),
FOREIGN KEY (TaskType) REFERENCES TaskType(TaskType),
FOREIGN KEY (PersonID) REFERENCES Person(ID),
FOREIGN KEY (GroupType) REFERENCES Groups(GroupType)

Upvotes: 1

Views: 713

Answers (1)

Damien_The_Unbeliever
Damien_The_Unbeliever

Reputation: 239636

Add wider "super keys" to these tables that include the primary key and additional columns1, then declare the foreign keys using them. Whether you also remove the superfluous smaller foreign keys is a matter of taste:

CREATE TABLE Groups(
ID int IDENTITY,
GroupType varchar(50) PRIMARY KEY,
Description varchar(255) DEFAULT ('-'),
DepartmentID int FOREIGN KEY (DepartmentID) REFERENCES Department(ID),
constraint Group_Dep_XRef UNIQUE (GroupType,DepartmentID)
)

CREATE TABLE Person(
ID int PRIMARY KEY IDENTITY,
Name varchar(50),
DepartmentID int FOREIGN KEY (DepartmentID) REFERENCES Department(ID),
constraint Person_Dept_XRef UNIQUE (ID,DepartmentID)
)

CREATE TABLE Task(
ID int IDENTITY,
TimeFrame decimal(4,2),
Yearcount int,
GroupType varchar(50),
TaskType varchar(50), 
WeekNr int,
ExceptionDetail varchar(255) DEFAULT ('-'),
PersonID int,
DepartmentID int,
constraint FK_Group_Dept_XRef FOREIGN KEY (GroupType,DepartmentID)
        references Group (GroupType,DepartmentID),
constraint FK_Person_Dept_XRef FOREIGN KEY (PersonID,DepartmentID)
        references Person (ID,DepartmentID),
UNIQUE ("TaskType", "GroupType", "WeekNr", "Yearcount"),
FOREIGN KEY (TaskType) REFERENCES TaskType(TaskType),
FOREIGN KEY (PersonID) REFERENCES Person(ID), --Redundant now
FOREIGN KEY (GroupType) REFERENCES Groups(GroupType) --Also redundant
)

(I also consolidated GDID and PDID into DepartmentID - if they're always meant to be equal, why store that twice and then have to have another constraint to assert their equality?)


1If a primary key (or unique key) is sufficient to uniquely identify each row then any wider key which includes the key columns and additional columns must also be sufficient to uniquely identify each row.

Upvotes: 1

Related Questions