Create a foreign key from two different tables - postgresql

I have two tables in PostgreSQL:
create Table student (
studentID integer primary key,
studentname text
);
create Table courses (
courseID text primary key,
schoolname text
);
I want to create a third table schoolstudent that has a foreign key (studentID, schoolname) where studentID references the primary key of the student table and schoolname references the schoolname key in the courses table.
How can I create a foreign key from two different tables in PostgreSQL 9.4 or 9.5?

A FK constraint requires a UNIQUE or PK constraint on the target column(s), which schoolname obviously cannot provide. You need another table with unique rows per school:
CREATE TABLE school(
school_id serial PRIMARY KEY,
schoolname text NOT NULL
);
CREATE TABLE student(
student_id serial PRIMARY KEY,
studentname text
);
CREATE TABLE schoolstudent(
school_id int REFERENCES school,
student_id int REFERENCES student,
PRIMARY KEY (school_id, student_id)
);
CREATE TABLE course(
course_id text PRIMARY KEY,
school_id int REFERENCES school
);
Using short syntax for foreign key constraints. Details in the manual.
How to implement a many-to-many relationship in PostgreSQL?
If you really need schoolname in the schoolstudent table (I seriously doubt that, looks like a design error), you can just add it. To enforce referential integrity you can include it in the foreign key, but you need a (redundant) matching UNIQUE constraint on school(school_id, schoolname), too.
CREATE TABLE schoolstudent(
school_id int,
student_id int REFERENCES student,
schoolname text,
PRIMARY KEY (school_id, student_id),
CONSTRAINT schoolstudent_combo_fk FOREIGN KEY (school_id, schoolname)
REFERENCES school (school_id, schoolname) ON UPDATE CASCADE
);
Using explicit syntax in this case. And I suggest to cascade updates.
Or if schoolname is actually guaranteed to be UNIQUE (again, my doubts) you can replace school_id completely and just use schoolname as PK and FK column. Long text columns are not very efficient for the purpose, though - if performance matters. And schoolnames change, which is not ideal for PK columns.
You still need a separate school table in any case.

you can set many to many relation only if both the fields are Unique(probably Primary Keys).if above condition is Fulfilled you can use
CREATE TABLE Schoolstudent(
ID INT references student(studentID),
Schoolname CHAR(50) references courses(Schoolname),
);
But schoolname in table courses should be unique or PK.

Related

there is no unique constraint matching given keys for referenced table "category"?

I'm trying to define more than one foreign key in the process table. but I am getting the error that the columns I am trying to define as foreign key are not 'unique value'.
For this, I wanted to define id and name columns as primary keys in category and subject tables. However, when I want to create the process table, I still get this error." there is no unique constraint matching given keys for referenced table "category"
I have researched and continue to do so on Stackoverflow and many more. but I couldn't figure it out with solutions or viewpoints of the issues that got the same error I was facing. Maybe there is something I'm not seeing.
first table;
CREATE TABLE category(
category_id INT GENERATED ALWAYS AS IDENTITY,
category_name VARCHAR(210),
category_description TEXT,
CONSTRAINT category_pk PRIMARY KEY(category_id,category_name)
);
second table;
CREATE TABLE subject(
subject_id INT GENERATED ALWAYS AS IDENTITY,
subject_name VARCHAR(210),
subject_description TEXT,
CONSTRAINT subject_pk PRIMARY KEY(subject_id,subject_name)
);
I tried that too but I keep getting the same error
ALTER TABLE category ADD CONSTRAINT some_constraint PRIMARY KEY(category_id,category_name);
third table;
CREATE TABLE process(
process_id INT GENERATED ALWAYS AS IDENTITY,
fk_category_id INTEGER,
fk_subject_id INTEGER,
FOREIGN KEY(fk_category_id) REFERENCES category(category_id) ON DELETE CASCADE ON UPDATE
CASCADE,
FOREIGN KEY(fk_subject_id) REFERENCES subject(subject_id) ON DELETE CASCADE ON UPDATE
CASCADE
);
In your FOREIGN KEY declaration either:
Include both the columns that make up the PRIMARY KEY on category and subject e.g. ... REFERENCES category(category_id, category_name) ...
OR
Do not refer to any column and let the FK pick up the PK automatically e.g. ... REFERENCES category ON DELETE ....
I am going to say that what you are really after is:
CREATE TABLE category(
category_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
category_name VARCHAR(210) UNIQUE,
category_description TEXT
);
CREATE TABLE subject(
subject_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
subject_name VARCHAR(210) UNIQUE,
subject_description TEXT
);
CREATE TABLE process(
process_id INT GENERATED ALWAYS AS IDENTITY,
fk_category_id INTEGER,
fk_subject_id INTEGER,
FOREIGN KEY(fk_category_id) REFERENCES category ON DELETE CASCADE ON UPDATE
CASCADE,
FOREIGN KEY(fk_subject_id) REFERENCES subject ON DELETE CASCADE ON UPDATE
CASCADE
);
An identity column isn't automatically a primary key. So your tables category and subjectdon't have any primary keys and thus can't be referenced by a foreign key.
You need to add PRIMARY KEY to the columns of the tables category and subject

Dropping unique constraint on foreign key reference of junction table

I'm creating a database in PostgreSQL and want to include a many-to-many relationship between the tables. The two tables I want to include are as follows:
CREATE TABLE "meter" (
"id" integer PRIMARY KEY,
"nmi" integer,
"next_scheduled_read_date" timestamp
);
CREATE TABLE "register" (
"id" text PRIMARY KEY,
"value" text
);
The many-to-many relationship I want to have is between meter id and register id. I have then created the junction table below:
CREATE TABLE "meter_registers" (
"meter_id" integer NOT NULL,
"register_id" text NOT NULL,
PRIMARY KEY ("meter_id", "register_id"),
FOREIGN KEY ("meter_id") REFERENCES "meter" ("id") ON UPDATE CASCADE,
FOREIGN KEY ("register_id") REFERENCES "register" ("id") ON UPDATE CASCADE
);
I then want to create a table that references the meter_id and register_id values from the junction table above which is structured as follows:
CREATE TABLE "demand_data" (
"upload_id" integer PRIMARY KEY,
"nmi" integer,
"meter" integer,
"register" text,
"start" timestamp,
"end" timestamp,
"duration" Time,
"demand" double precision
);
Where the meter and register reference the corresponding junction table columns. However, as the values of the junction table will not be unique I can't simply add a foreign key for the meter and register columns so I run into an error when I run the following:
ALTER TABLE "demand_data" ADD FOREIGN KEY ("meter") REFERENCES "meter_registers" ("meter_id");
ALTER TABLE "demand_data" ADD FOREIGN KEY ("register") REFERENCES "meter_registers" ("register_id");
ERROR: there is no unique constraint matching given keys for referenced table "meter_registers"
Is there a way to possibly reference the junction table columns in the demand_data table without the foreign key constraint? I know it's possible to do with a separate query once some data has been added using inner joins however, is it possible to do it through database table set up?
Yes. A compound foreign key.
ALTER TABLE "demand_data"
ADD FOREIGN KEY ("meter","register")
REFERENCES "meter_registers"( "meter_id", "register_id");
Note: Not directly related you should avoid those dreaded double quotes.
If demand_data references meter_registers, it should reference its primary key. So add a single foreign key constraint on both columns.
If you want two separate foreign keys,you should probably reference meter and register directly.

How to use constraints on ranges with a junction table?

Based on the documentation it's pretty straightforward how to prevent any overlapping reservations in the table at the same time.
CREATE EXTENSION btree_gist;
CREATE TABLE room_reservation (
room text,
during tsrange,
EXCLUDE USING GIST (room WITH =, during WITH &&)
);
However, when you have multiple resources that can be reserved by users, what is the best approach to check for overlappings? You can see below that I want to have users reserve multiple resources. That's why I'm using the junction table Resources_Reservations. Is there any way I can use EXCLUDE in order to check that no resources are reserved at the same time?
CREATE TABLE Users(
id serial primary key,
name text
);
CREATE TABLE Resources(
id serial primary key,
name text
);
CREATE TABLE Reservations(
id serial primary key,
duration tstzrange,
user_id serial,
FOREIGN KEY (user_id) REFERENCES Users(id)
);
CREATE TABLE Resources_Reservations(
resource_id serial,
reservation_id serial,
FOREIGN KEY (resource_id) REFERENCES Resources(id),
FOREIGN KEY (reservation_id) REFERENCES Reservations(id),
PRIMARY KEY (resource_id, reservation_id)
);
I think what you want is doable with a slight model change.
But first let's correct a misconception. You have foreign key columns (user_id, resource_id, etc) defined as SERIAL. This is incorrect, they should be INTEGER. This is because SERIAL is not actually a data type. It is a psuedo-data type that is actually a shortcut for: creating a sequence, creating a column of type integer, and defining the sequence created as the default value. With that out of the way.
I think your Resources_Reservations is redundant. A reservation is by a user, but a reservation without something reserved would just be user information. Bring the resource_id into Reservation. Now a Reservation is by a user for a resource with a duration. Everything your current model contains but less complexity.
Assuming you don't have data that needs saving, then:
create table users(
id serial primary key,
name text
);
create table resources(
id serial primary key,
name text
);
create table reservations(
user_id integer
resource_id integer
duration tstzrange,
foreign key (user_id) references users(id)
foreign key (resource_id) references resources(id),
primary key (resource_id, user_id)
);
You should now be able to create your GIST exclusion.

How to make a Foreigne key that references NOT Primary or Unique column from another table?

I have made classical Employee - Employer table that has one primary key and one foreign key. Foreign key references primary so it is a self referencing table:
CREATE TABLE Worker
(
OIB NUMERIC(2,0),
Name NVARCHAR(10),
Surname NVARCHAR(20),
DateOfEmployment DATETIME2 NOT NULL,
Adress NVARCHAR(20),
City NVARCHAR(10),
SUPERIOR NUMERIC(2,0) UNIQUE,
Constraint PK_Worker PRIMARY KEY(OIB),
CONSTRAINT FK_Worker FOREIGN KEY (Superior) REFERENCES Worker(OIB)
);
Second table that should keep points for all my employees is made like this:
CREATE TABLE Point
(
OIB_Worker NUMERIC(2,0) NOT NULL,
OIB_Superior NUMERIC(2,0) NOT NULL,
Pt_To_Worker tinyint,
Pt_To_Superior tinyint,
Month_ INT NOT NULL,
Year_ INT NOT NULL,
CONSTRAINT FK_Point_Worker FOREIGN KEY (OIB_Worker) References Worker(OIB),
CONSTRAINT FK_Point_Worker_2 FOREIGN KEY (OIB_Superior) References Worker(Superior),
CONSTRAINT PK_Point PRIMARY KEY(OIB_Worker,OIB_Superior,Month_,Year_)
);
It should enable storing grades per month for every employee.
That is, I have two foreign keys, one for Worker.OIB and one for Worker.Superior. Also, I have a composite primary key made of columns Point.OIB_Worker, Point.OIB_superior, Point.Month_ and Point.Year_. Key is composite because it needs to disable entering grades more then once a month.
My question is:
How to make a foreign key from Point to Worker so that any superior can have more then one employee assigned to him?
If you look closely, my implementation works but it can have only one employee per manager.
That is because of a fact that a foreign key has to reference either the primary or the unique column from other table. And my Worker.Superior is UNIQUE, so it can have only unique values (no repetition).
I think many people will find this example interesting as it is a common problem when making a new database.
I think your FK_Point_Worker_2 should also have References Worker(OIB), and you should remove the UNIQUE constraint from Worker.Superior. That way a superior can have more than one worker assigned to him.
Think about it. You have unique constraint on SUPERIOR and you are confused as to why two employees cannot have the same SUPERIOR. That is what a unique constraint does - not allow duplicates.
A FK can only reference a unique column or columns.
A FK_Point_Worker_2 with a References Worker(OIB) does not assure OIB is a Superior.
I would add a unique constraint on Worker on (OIB, SUPERIOR)
and remove the unique constraint on SUPERIOR.
It will always be unique as OIB is unique.
Then have composite FK relationship.
This is an example of a composite FK relationship
ALTER TABLE [dbo].[wfBchFolder] WITH CHECK ADD CONSTRAINT [FK_wfBchFolder_wfBch] FOREIGN KEY([wfID], [bchID])
REFERENCES [dbo].[WFbch] ([wfID], [ID])
GO

T-SQL: foreign key that's not referencing a primary key

I have the following database:
CREATE TABLE ContentNodes
(
Id UNIQUEIDENTIFIER NOT NULL,
Revision INT IDENTITY(1,1) NOT NULL,
ParentId UNIQUEIDENTIFIER NULL
PRIMARY KEY (Id, Revision)
)
How do I limit ParentId to only contain values from the Id column. Trying to make ParentId a foreign key gives me:
PRINT 'FK_ContentNodes_ParentId_ContentNodes';
ALTER TABLE ContentNodes
ADD CONSTRAINT FK_ContentNodes_ParentId_ContentNodes FOREIGN KEY (ParentId) REFERENCES ContentNodes(Id);
GO
Error:
There are no primary or candidate keys in the referenced table
'ContentNodes' that match the
referencing column list in the foreign
key
'FK_ContentNodes_ParentId_ContentNodes'.
Since you have a compound primary key (Id, Revision) on your ContentNodes, you have to use both columns in a foreign key relation.
You cannot reference only parts of a primary key - simply cannot be done.
You have to either introduce a surrogate primary key into your table which is just a simple INT IDENTITY and then you can self-reference that single PK column, or you can (if it's possible in your data model) put a UNIQUE INDEX on that one column you want to reference:
CREATE UNIQUE NONCLUSTERED INDEX UIX_ID
ON ContentNodes(Id)
Once you have a UNIQUE INDEX on that column, then you can use it as a FK reference.