SQL Many-to-many relationship between 3 tables - sql

I have the following tables:
Users
- user_id (PK)
Projects
- project_id (PK)
Tasks
- taks_id (PK)
- project_id (FK)
The requirements are:
Each Task belongs to only one project.
Each Project has one or more users.
Each Task has 0 or more users (but the users must belong to the project to which the task belongs to).
I'm currently trying to do the above with 2 join tables:
UsersProjects
[user_id] INT NOT NULL,
[project_id] INT NOT NULL,
[accepted] BIT NULL,
FOREIGN KEY ([user_id]) REFERENCES [dbo].[_Users] ([user_id]),
FOREIGN KEY ([project_id]) REFERENCES [dbo].[_Projects] ([project_id]),
CONSTRAINT [PK_UsersProjects] PRIMARY KEY ([user_id], [project_id])
UsersTasks
[user_id] INT NOT NULL,
[task_id] INT NOT NULL,
[accepted] BIT NULL,
FOREIGN KEY ([user_id]) REFERENCES [dbo].[_Users] ([user_id]),
FOREIGN KEY ([task_id]) REFERENCES [dbo].[_Tasks] ([task_id]),
CONSTRAINT [PK_UsersTasks] PRIMARY KEY ([user_id], [task_id])
Am I on the right track for the many-to-many relationships and for the requirements?
How can I optimise it?
Thanks for taking the time to read this!

Each Task has 0 or more users (but the users must belong to the project to which the task belongs to).
I assume you're asking how to enforce this last constraint - it's not there in your current DB schema. I see two options:
You have to do it via SQL (e.g. when you insert a record into UsersTasks you issue a SELECT on UsersProjects JOIN Tasks to see if there's at least record matching)
You change the structure of the Tasks Primary Key. I.e. you create a PK to be the compound (Project_ID + Task_ID) and Task_ID is then no longer an identity field but starts with e.g. 1 for each new Project. Then the PK for UsersTasks also becomes User_ID + Project_ID + Task_ID and then you can reference a completely FK straight to UserProjects (User_ID + Project_ID). It's less intuitive than Solution 1 but works and requires no custom SQL for enforcing integrity.
For Option 2:
Tasks has got a slightly different constraint
...
CONSTRAINT [PK_Tasks] PRIMARY KEY ([project_id], [task_id])
UsersTasks
[user_id] INT NOT NULL,
[project_id] INT NOT NULL,
[task_id] INT NOT NULL,
[accepted] BIT NULL,
FOREIGN KEY ([user_id]) REFERENCES [dbo].[_Users] ([user_id]),
FOREIGN KEY ([project_id], [task_id]) REFERENCES [dbo].[_Tasks] ([project_id],[task_id]),
FOREIGN KEY ([user_id], [project_id]) REFERENCES [dbo].[_User_Projects] ([user_id],[project_id]),
CONSTRAINT [PK_UsersTasks] PRIMARY KEY ([user_id], [project_id], [task_id])

Related

recursive associations many to many and primary keys and foreign keys

Hi i have ER scheme with an recursive associations many to many with primary keys for example:
______(0,N)_________
ID | base \
\course preparatory
|___advanced_________/
(1,N)
where course is entity and preparatory is association nad course have ID primary key.
My question is:
course have ID primary key but, preparatory association?
I suppose that preparatory association have base and advanced primary keys and ID_course foreign key:
course primary key:ID
preparatory primary key:base,advanced,ID_course foreign key:ID_course
is it right?
In your model, I assume that preparatory represents all the courses that are required to be completed because you can start the principal course.
In that case you are correct, you will need a separate table to manage the multiple IDs, I would suggest something like this:
CREATE TABLE Course(
ID INT IDENTITY(1,1) NOT NULL,
CONSTRAINT Course_PK PRIMARY KEY (ID)
);
CREATE TABLE Preparatory(
ID INT IDENTITY(1,1) NOT NULL,
Advanced_CourseID INT NOT NULL,
Required_CourseID INT NOT NULL,
CONSTRAINT Preparatory_PK PRIMARY KEY (ID),
CONSTRAINT Preparatory_Advanced_Course_ID_FK
FOREIGN KEY (Advanced_CourseID)
REFERENCES Course (ID),
CONSTRAINT Preparatory_Required_Course_ID_FK
FOREIGN KEY (Required_CourseID)
REFERENCES Course (ID)
);
The expectation is that Advanced_CourseID represents the princial singular reference and there will be unique Required_CourseID for each Advanced_CourseID.
To further describe this I would suggest declaring a unique index on this relationship to enforce the desired structural integrity:
CREATE UNIQUE INDEX CONCURRENTLY Preparatory_Advanced_Required_Course_ID_UX
ON Preparatory(Advanced_CourseID,Advanced_CourseID);
ALTER TABLE Preparatory
ADD CONSTRAINT Unique_Preparatory_Advanced_Required_Course_ID
UNIQUE USING INDEX Preparatory_Advanced_Required_Course_ID_UX;

How to insert a composite primary key into another table?

I have a composite primary key that I would like to insert into another table.
create table courses_instructors
(
courseID int foreign key references Course(C_ID) not null,
instructorID int foreign key references Instructor(I_ID) not null,
primary key (courseID, instructorID), --coourseID and instructorID combined is the composite PK
courseTerm varchar(50) not null,
courseNumber int not null,
courseLocation varchar(50),
courseTime varchar(50),
courseMaxOccupancy int,
courseSeatAvailable int
)
create table courses_students
(
studentID int foreign key references student(S_ID) not null,
courseID int, -- foreign key -- I want this value to the be value that represents the composite PK from the courses_instructors
primary key(studentID, courseID), -- these 2 fields combined would make the composite PK, but with the courseID value I will be able to identify who is the instructor for a course and the other details from the course_instructor table
courseOutcome varchar(50)
)
All the course come from a course table which only contains the course name and the disciple along with a descrption. The course table has a primary key that identifies each course uniquely.
To refer composit primary key, Courses_Students table should be having both the columns CourseID and InstructorID.
And then
ALTER TABLE Courses_Students
ADD CONSTRAINT FK_Courses_Students
FOREIGN KEY(CourseID, InstructorID) REFERENCES Courses_Instructors(CourseID, InstructorID)
Or the table definitions should look like,
create table courses_instructors
(
courseID int foreign key references Course(C_ID) not null,
instructorID int foreign key references Instructor(I_ID) not null,
primary key (courseID, instructorID), --coourseID and instructorID combined is the composite PK
courseTerm varchar(50) not null,
courseNumber int not null,
courseLocation varchar(50),
courseTime varchar(50),
courseMaxOccupancy int,
courseSeatAvailable int
)
create table courses_students
(
studentID int foreign key references student(S_ID) not null,
courseID int,
instructorId int,
FOREIGN KEY(CourseID, InstructorID) REFERENCES Courses_Instructors(CourseID, InstructorID),
primary key(studentID, courseID, InstructorId),
courseOutcome varchar(50)
)
You can either refer two columns as already mentioned or add a surrogate key such as an identity column or GUID to the primary table and refer with to it - It usually performs better.
The course_instructors table is an intersection table implementing an m-m relationship between, as may be easily guessed, course entities and instructor entities. Almost invariably, I don't add a surrogate key to such a table for the simple reason that such a key would never be used. A typical user has a reference to one entity or the other and wishes to see all the other entities it relates to. Or sometimes the user has references to both entities and wished to get the details of their relationship.
This rule is not without exceptions, however, and your use case is just such an example. The table not just expresses a relationship between two entities but becomes an entity unto itself: a class offering. A student will select a class from a published schedule for the class and day/time desired. This will be identified by a class code number of some sort.
This code is what will be used to register for the desired class. In cases such as these, it makes sense to create a surrogate key for the intersection table -- which then becomes the class code printed in the catalog. Thus you would use this class code to refer to the relationship that defines the class offering and not use the composite key.
It looks like you already have such a composite key: the course_number field. You don't have it defined as unique but doesn't it uniquely identity the combination of course, instructor, location and time that makes up each class offering?

error: there is no unique constraint matching given keys for referenced table "incident"

I know that this question has been already answered a million of times, but I couldn't find any solution. Well I have these three tables on postgres sql.
CREATE TABLE user_account (
id SERIAL not null,
firstName VARCHAR(60) not null,
lastName VARCHAR(60) not null,
password VARCHAR(150) not null,
email VARCHAR(40) not null UNIQUE,
isVolunteer BOOLEAN,
complete BOOLEAN,
CONSTRAINT pk_user PRIMARY KEY (id));
CREATE TABLE incident (
id SERIAL not null,
patientId INTEGER not null,
incidentTime VARCHAR(10) not null,
latitude NUMERIC not null,
longitude NUMERIC not null,
city VARCHAR(60) not null,
state VARCHAR(60),
country VARCHAR(60),
complete BOOLEAN,
CONSTRAINT pk_incident PRIMARY KEY (id, patientId),
CONSTRAINT fk_incident FOREIGN KEY (patientId)
REFERENCES user_account (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE);
CREATE TABLE incident_has_volunteer (
incidentId INTEGER not null,
volunteerId INTEGER not null,
incidentTime VARCHAR(10) not null,
complete BOOLEAN,
CONSTRAINT pk_incident_has_volunteer PRIMARY KEY (incidentId, volunteerId),
CONSTRAINT fk_volunteer FOREIGN KEY (volunteerId)
REFERENCES user_account (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_incident FOREIGN KEY (incidentId)
REFERENCES incident (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE);
When I try to create the table incident_has_volunteer it throws the error there is no unique constraint matching given keys for referenced table "incident".
I tried to add on the third table and the patientId as a foreign key from table incident table but with no luck. I can't understand why it throws this error even if I have already set the primary keys on the incident table.
I'm not an expert in postgres, but I believe that the problem is while fk_incident is referencing incident.id, incident's primary key is made of id + patientId. Since incident.id is guaranteed to be unique only in combination with patientId, there's no way to ensure referential integrity.
I believe that if you add a unique constraint to incident.id (I'm assuming that it would be unique), your foreign key will be legal.
Very simply - one table of primary key acts as a foreign key for another table, so you must ensure that both key is referenced or not.
Simply you will not assign foreign key to the column of another table which does not have primary key. this is called as RDBMS.
Thanks

How to establish foreign key relationship

I got three tables.
User
Project
WorkFlow
In workflow ProjectId, UserId together should never repeat. Thats my
requirement.I mean the combination should never repeat.
And the ProjectId should be present in the Project table and UserId
should be present in the User table.
This is the requirement.
Steps i tried :
I made ProjectId, UserId as composite key in workFlow. But cant be able to maintain foreign key since two columns are not available in single table.
How to resolve this.
I am open to change my design also, since this is the initial stage of my development.
Main reuirement is
One table to store project (project table) related informations and
the other one(workFlow) hold the record which project is assigned to
which user.
Foreign keys do not control uniqueness; they only control referential integrity. For uniqueness, you need unique constraints:
create table dbo.Workflow (
Id int identity(1,1) primary key,
ProjectId int not null,
UserId int not null,
foreign key (ProjectId) references dbo.Project (Id),
foreign key (UserId) references dbo.[User] (Id),
unique (UserId, ProjectId)
);
EDIT: If you don't need a surrogate key in this table, and don't care much about its possible children, you can simplify the structure by switching from surrogate primary key to the natural one. With table becoming more narrow, it will increase performance in high load scenarios by reducing its disk footprint:
create table dbo.Workflow (
ProjectId int not null,
UserId int not null,
primary key (UserId, ProjectId)
foreign key (ProjectId) references dbo.Project (Id),
foreign key (UserId) references dbo.[User] (Id),
);
And yes, constraints should be uniquely named, it will make schema comparisons and updates much easier.

Filesystem like permissions in DBMS

Sorry for a big question, but I can't explain my situation with less information.
I designed a database system that is like this:
CREATE TABLE servers(
ID bigint primary key not null
/* other fields of the server */
);
CREATE TABLE producers(
ID bigint not null,
ServerID bigint not null,
/* other field of the producer */
constraint "PK_producers"
primary key (ID, ServerID),
constraint "FK_producer_servers"
foreign key("ServerID") references "servers"
);
CREATE TABLE records(
ID bigint primary key,
ServerID bigint not null,
ProducerID bigint not null
/* other fields of record */
constraint "FK_records_servers"
foreign key("ServerID") references "servers"
constraint "FK_records_producers"
foreign key("ServerID", "ProducerID") references "producers"
);
CREATE TABLE groups(
ID bigint not null primary key,
GroupName nvarchar(50) not null,
Permissions int not null
/* other fields of the group */
);
CREATE TABLE users(
ID bigint not null primary key,
UserName nvarchar(50) not null unique,
Permissions int not null
/* other fields of user */
);
CREATE TABLE users_in_groups(
UserID bigint not null,
GroupID bigint not null,
constraint "PK_users_in_groups" primary key (UserID, GroupID),
constraint "FK_uig_users" foreign key("UserID") references "users",
constraint "FK_uig_groups" foreign key("GroupID") references "groups"
);
Data will be added to records from producers and each producer may provide 500~2000 records per day and each server usually have 2~4 producer and it is completely normal to have 3~6 server. As you see records table is growing really fast(I periodically archive some data and shrink database but records table usually have > 100000 record).
Now my question is:
As you see users have different permissions, based on which group they currently belong and which permissions applied to them by administrators, now I have to advance this system in a way that each user may have different permissions to different items(server, producer and record) in a way that if a user have explicit permission to an item use that permission otherwise use permission from parent and at least global permissions of the user. For example user permissions to a record will be indicated from permissions that applied to that record, its producer, its server or permissions that defined for the user.
As first workaround I think I will implement relation tables that provide relation between user and various objects as follow:
CREATE TABLE user_server_permissions(
UserID bigint not null,
ServerID bigint not null,
Permissions int not null,
constraint "PK_usp" primary key ("UserID", "ServerID"),
constraint "FK_usp_users" foreign key ("UserID") references "users",
constraint "FK_usp_server" foreign key ("ServerID") references "servers"
);
CREATE TABLE user_producer_permissions(
UserID bigint not null,
ServerID bigint not null,
ProducerID bigint not null,
Permissions int not null,
constraint "PK_upp" primary key ("UserID", "ServerID", "ProducerID"),
constraint "FK_upp_users" foreign key ("UserID") references "users",
constraint "FK_upp_server" foreign key ("ServerID") references "servers"
constraint "FK_upp_producer" foreign key ("ServerID", "ProducerID") references "producers"
);
CREATE TABLE user_record_permissions(
UserID bigint not null,
RecordID bigint not null,
Permissions int not null,
constraint "PK_urp" primary key ("UserID", "ServerID"),
constraint "FK_urp_users" foreign key ("UserID") references "users",
constraint "FK_urp_record" foreign key ("RecordID") references "records"
);
But using this approach really decrease performance, because main table that I work with it is records and it is a rare condition that an administrator set special permission for a record and most records should use permissions from their producer but using this technique I should check multiple tables for each access to records table. For example a simple query like:
SELECT * FROM "records" WHERE /* Some condition */ AND record_is_accessible( "ID" )
Will kill my server that should be able to respond to multiple clients!
Most my clients use MSSQL as DBMS but there are few cases where they use MySQL or ORACLE.
Now can anyone direct me in a way to accomplish the task!?
At first glance, a quick solution would be
to not have 3 separate tables for permissions. Instead you would have this one
CREATE TABLE user_entity_permissions(
UserID bigint not null,
EntityID bigint not null,
EntityType int not null,
Permissions int not null,
constraint "PK_usp" primary key ("UserID", "EntityID"),
constraint "FK_usp_users" foreign key ("UserID") references "users"
--- Third FK constraint ---
);
Now you want to shape your Third Fk constraint accordingly (if needed)
For example lets say you give Servers an EntityType = 1, Producer --> 2, Record --> 3
Then EntityId and EntityType = 1 references to servers and so on...
This way you can also sort the priority of your permissions by using EntityType (e.g. 1 first, then 2, then 3)