Validating foreign key through tables in postgres - postgresql

I'm trying to validate a foreign key constraint on my table through another table, but I'm not sure how to go about it, or even the correct nomenclature to google.
I have a User table:
create table User ( id uuid primary key );
Each user can have multiple stores:
create table Store(
id uuid primary key,
user_id uuid foreign key store_user_id_fk references User (id)
);
and each user+store can have products
create table Product(
id uuid primary key,
user_id uuid foreign key product_user_id_fk references User (id),
store_id uuid foreign key product_sotre_id_fk references Store (id),
)
My question is: how can I write a constraint on Product such that any (user_id,store_id) combination also must have a valid entry in the Store table? The case I'm trying to prevent is an entry being added to Product where the store does not also belong to the user.
Is there some way of adding:
CHECK ( store_id == Store.id and user_id == Store.user_id )
To the product table?

Unless I'm misunderstanding your question, I'm pretty sure it would just be:
create table Product(
id uuid primary key,
user_id uuid foreign key product_user_id_fk references User (id),
store_id uuid foreign key product_sotre_id_fk references Store (id),
FOREIGN KEY (user_id, store_id) REFERENCES store(user_id, id)
);
This would indicate that the primary key of store should be (id, user_id) not just id or, at at minimum, it should have a UNIQUE constraint on (id, user_id).

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

What's the right way to use "on cascade delete" on a join table? [duplicate]

I have users, have offers, and a junction table users_offers.
Is there a setup I can carry out with foreign key relations that can ensure that junction data will be automatically deleted when I delete any users or offers?
Declare a referential action: ON DELETE CASCADE, for example:
CREATE TABLE user (
user_id int PRIMARY KEY
);
CREATE TABLE offer (
offer_id int PRIMARY KEY
);
CREATE TABLE user_offer (
user_id int,
offer_id int,
PRIMARY KEY (user_id, offer_id),
FOREIGN KEY (user_id) REFERENCES user (user_id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES offer (offer_id) ON DELETE CASCADE
);
[SQL Fiddle]
Interestingly enough, it seems that specifying referential action in "shorthand" foreign key syntax doesn't work (confirmed under MySQL 5.5.30, 5.6.6 m9). The following gets parsed, but when user is deleted, the corresponding user_offer doesn't get deleted:
CREATE TABLE user_offer (
user_id int REFERENCES user (user_id) ON DELETE CASCADE,
offer_id int REFERENCES offer (offer_id) ON DELETE CASCADE,
PRIMARY KEY (user_id, offer_id)
);
You can specify this within your model creation:
CREATE TABLE users_offers (user_id INT NOT NULL,
offer_id INT NOT NULL,
PRIMARY KEY (user_id, offer_id),
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE,
FOREIGN KEY (offer_id) REFERENCES offers(id)
ON DELETE CASCADE);
You can see a working example in this Fiddle.

Do i need index for composite primary key where each column is foreign key

When i create table user:
pk
id | int
And table friend:
pk, fk | pk, fk
user_id | friend_id
As i understand composite pk creates unique index user_id+friend_id internally and this index is used for user_id foreign key. But do i need to create index for friend_id because index on composite pk friend table will not work for friend_id (it's second in composite pk)?
SQL:
CREATE TABLE public."user"
(
id integer NOT NULL DEFAULT nextval('user_id_seq'::regclass),
)
CREATE TABLE public.user_friend
(
user_id integer NOT NULL,
friend_id integer NOT NULL,
CONSTRAINT user_friend_pkey PRIMARY KEY (user_id, friend_id),
CONSTRAINT user_friend_friend_id_fkey FOREIGN KEY (friend_id)
REFERENCES public."user" (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT user_friend_user_id_fkey FOREIGN KEY (user_id)
REFERENCES public."user" (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE CASCADE
)
CREATE INDEX fki_user_friend_friend_id_fkey
ON public.user_friend USING btree
(friend_id ASC NULLS LAST)
TABLESPACE pg_default;
As i understand composite pk creates unique index user_id+friend_id intenally
Yes.
and this index is used for user_id foreign key.
Not exactly, no.
There are two cases here:
an INSERT or UPDATE on the user_friend table. It will have to check the table's primary key of course (and will use the user_friend_pkey index for that). For the foreign key constraints, this will only have to check whether the user_id and friend_id values exist in the user table, and will use the index created by user's primary key for those.
an UPDATE or DELETE on the user table. It will have to check whether the old value was referenced in the user_friend table, and act according to the CASCADE or RESTRICT strategy. To find this row in user_friend, it will use the indices on user_id and on friend_id if they exist.
But do i need to create index for friend_id because index on composite pk friend table will not work for friend_id
Yes, looking up by friend_id is slow without the extra index. You only need this if you want to find users that have x as their friend, or when updates/deletes on user must not be slow.

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.

Postgresql - there is not unique constraint matching given keys for referenced table; referencing a key from own table

I created a table (team) which has a composite primary key composed of the id, project id, and a user id. Each primary key references a key in some other table except for the id. Now I want to add a key (team_id) that references the own id of the team because i want to cater a sub team and this team_id will contain the id of the parent team.
I encountered an error: 'there is no unique constraint matching given keys for referenced table "team"'.
Am I wrong to understand that each possible combination of each of the keys in the composite primary key is the team's unique constraint?
What am I missing?
CREATE TABLE team (
id varchar NOT NULL,
project_id varchar NOT NULL, -- team should always have associated project
user_id varchar NOT NULL, -- team member id
team_id varchar, -- parent team id
create_date date NOT NULL,
create_time time NOT NULL,
name varchar,
description text,
PRIMARY KEY (id, project_id, user_id),
FOREIGN KEY (project_id) REFERENCES project (id)
ON UPDATE CASCADE
ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES person (id)
ON UPDATE CASCADE
ON DELETE CASCADE,
FOREIGN KEY (team_id) REFERENCES team (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
parent_team_id varchar,
parent_team_project_id varchar,
parent_team_user_id varchar,
foreign key (
parent_team_id, parent_team_project_id, parent_team_user_id
) references team (id, project_id, user_id);
But the primary key is strange. Are you sure?