Update or Delete Violates foreign key constraint - sql

I have two tables that look like the following.
CREATE TABLE book (
book_id BIGSERIAL PRIMARY KEY,
book_special_id character(10) NOT NULL DEFAULT random_string(10) UNIQUE,
author_id bigint REFERENCES author(author_id) ON DELETE RESTRICT,
category_id bigint REFERENCES category(category_id) ON DELETE RESTRICT,
title text NOT NULL,
subtitle text,
book_text text
);
CREATE TABLE booksubcategory (
booksubcategory_id BIGSERIAL PRIMARY KEY,
book_id BIGSERIAL REFERENCES book(book_id) ON DELETE CASCADE,
subcategory_id BIGSERIAL REFERENCES subcategory(subcategory_id) ON DELETE RESTRICT,
CONSTRAINT booksubcategory_book_id_subcategory_id_key UNIQUE (book_id, subcategory_id)
);
In this example the book_id column is the primary key in the book table, and the referenced foreign key in the booksubcategory table. When I try and run the following sql, I receive the error:
ERROR: update or delete on table "book" violates foreign key constraint "booksubcategory_book_id_fkey" on table "booksubcategory"
Detail: Key (book_id)=(888392) is still referenced from table "booksubcategory"
Here is what the SQL looks like.
INSERT INTO book (book_special_id, author_id, category_id, title, subtitle, book_text)
VALUES ("D4jOko2IP0",34, 4, "Book Example", "Book Subtitle", "Some lengthy text")
ON CONFLICT (book_special_id)
DO UPDATE SET author_id=EXCLUDED.author_id, book_id=EXCLUDED.book_id, category_id=EXCLUDED.category_id, title=EXCLUDED.title, subtitle=EXCLUDED.subtitle, book_text=EXCLUDED.book_text;
In this situation the sql should update the columns because the book_special_key already exists in the book table.
I'm familiar with the reason why updates and deletes on foreign key constraints can fail for integrity reasons, but in my case I'm not updating the book_id directly, just the columns in the book table. Also I have ON DELETE CASCADE set on the foreign key in the child table. Can someone tell my why I'm experiencing this issue?

The inserted row clashes on unique key special_book_id, then the conflict rule tries to update the duplicate row.
But what is the value book_id of a NEW row that was not yet inserted due to conflicts and is autogen? Well, either null or a new serial.
So, whatever the case, you are updating book_id to null or a new serial and it fails as the old book_id value, that is disappearing, has references.
Remove the update to column book_id and it should work.

Related

Add foreign key on delete cascade phpMyAdmin is not working

I have a main table called book with fields id_book (primary, unique, auto increment) and name.
I have a secondary table called tag with fields book_id and tag
Each book can have many tags. I want that when I delete a book, all the tags to be deleted as well.
I tried this with this:
ALTER TABLE tag
ADD FOREIGN KEY (book_id)
REFERENCES book(id_book)
ON UPDATE CASCADE
ON DELETE CASCADE
I know I used this block of SQL in the past on some other project and it worked but now it seems it is not working anymore and I don't know why. If I delete a book, the tags will not be deleted.
I don't see any problem with the SQL in itself, i don't think the problem reside in this particular foreign key.
Maybe the key didn't register or another foreign key is blocking the delete.
I've tested with this SQL file and got the wanted results
CREATE TABLE book (
id_book INT AUTO_INCREMENT,
name VARCHAR(10),
PRIMARY KEY (id_book)
);
CREATE TABLE tag (
id INT AUTO_INCREMENT,
book_id INT,
name VARCHAR(10),
PRIMARY KEY (id)
);
ALTER TABLE tag
ADD FOREIGN KEY (book_id)
REFERENCES book(id_book)
ON UPDATE CASCADE
ON DELETE CASCADE;
INSERT INTO book (`name`) VALUES ("test", "another", "and another");
INSERT INTO tag (`book_id`, `name`) VALUES (1 , "first"), (2, "first"), (1, "second");

Postgresql, references to unique constraint

I'm running PostgreSQL 9.4 and have the following table:
CREATE TABLE user_cars (
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
car CHARACTER VARYING(255) NOT NULL,
CONSTRAINT test UNIQUE (user_id, car)
);
The table allows a user to have multiple cars, but only use the car name once. But other users may have the same car name.
I would like to have another table with references to the unique constraint test, and have tried stuff like:
CREATE TABLE mappings (
other_id CHARACTER(9) REFERENCES other (id) ON DELETE CASCADE,
user_cars REFERENCES user_cards (test) ON DELETE CASCADE
);
But that fails "obviously". I would like to make sure that other_id only have a single references to a user_car entry.
So to explain, how can I in table mappings have a references to test from table user_cars.
This is the thing that fails currently:
user_cars REFERENCES user_cards (test) ON DELETE CASCADE
Don't use composite foreign key references, if you can avoid it. Just add a unique id to the table:
CREATE TABLE user_cars (
user_car_id serial primary key,
user_id int REFERENCES users (id) ON DELETE CASCADE,
car CHARACTER VARYING(255) NOT NULL,
CONSTRAINT test UNIQUE (user_id, car)
);
Then mappings is simply:
CREATE TABLE mappings (
mapping_id serial primary key,
user_car_id int references user_cars(user_car_id) on delete cascade,
other_id CHARACTER(9) REFERENCES other (id) ON DELETE CASCADE,
);
If car should be unique, add UNIQUE constrain only on car column.
If user should be unique, add UNIQUE constrain only on user column.
If you add UNIQUE constrain on combination, then there will be duplicate values in the table.
UPDATE:
You can add multiple constraints on single column. With Foreign key add UNIQUE constraint as well on user_cars column in mapping table.

Do foreign keys gets inserted when primary key is inserted?

A very basic question I'm confused about, similar to UPDATE and DELETE CASCADE, do foreign keys gets inserted automatically when a referenced table's primary key is inserted?
Or are they simply a constraint that tells to check the primray key that is being reference and if present add, else error.
As a example, I'm asking about something like this:
...
CONSTRAINT idFK
FOREIGN KEY(id)
REFERENCES Users(id)
ON DELETE CASCADE
ON UPDATE CASCADE
...
Here, will id in this table automatically get inserted when an id is inserted in Users table?
when you insert a record in "Users" table , nothing inserted in any other tables.
when you insert a record in a table with foreign key, the value of foreign key field just will check with the primary key value in parent(Here "Users") table.

PostgreSQL assigns foreign key automatically on the referencing side

So I've got Table ActorInMovies, which has 3 foreign keys.
CREATE TABLE ActorInMovie(
ID_ROLE bigserial REFERENCES Role(ID_ROLE) ON DELETE CASCADE,
ID_ACTOR bigserial REFERENCES Actor(ID_Actor) ON DELETE CASCADE,
ID_MOVIE bigserial REFERENCES Movie(ID_Movie) ON DELETE CASCADE,
CONSTRAINT ActorInMovie_pk PRIMARY KEY (ID_ROLE));
I assumed that when I try to insert something like:
INSERT INTO ActorInMovie (ID_ROLE, ID_ACTOR) values (1,1);
that it would result in an error as ID_MOVIE was not specified (null I supposed).. but it automatically starts assigning indexes staring from 1.
What am I doing wrong? As written here, I thought that "PostgreSQL automatically creates indexes on primary keys and unique constraints, but not on the referencing side of foreign key relationships."
I have a very hard time imagining a use case where a serial(or bigserial) column references another column. It's usually the other way round: the serial column should go on the other end of the foreign key constraint.
I have an equally hard time imagining a design where a movie_id needs to be bigint instead of just int. There aren't nearly enough movies on this planet.
Also, there is a good chance, a column called movie_id in a table called actor_in_movie should be defined as NOT NULL.
In short: I doubt your design flies at all. Maybe something like:
CREATE TABLE actor (actor_id serial PRIMARY KEY, actor text, ...);
CREATE TABLE movie (movie_id serial PRIMARY KEY, movie text, ...);
CREATE TABLE actor_in_movie(
role_id serial PRIMARY KEY
,actor_id int NOT NULL REFERENCES actor(actor_id) ON DELETE CASCADE
,movie_id int NOT NULL REFERENCES movie(movie_id) ON DELETE CASCADE
);
A NOT NULL constraint is redundant, while the column is included in the primary key.
You probably want indices on actor_id and on movie_id in actor_in_movie.
More details:
How to implement a many-to-many relationship in PostgreSQL?
This is simply bigserial working exactly as advertised. It has nothing to do with the foreign key constraint, or with an index.

Why is this a cyclical foreign key constraint?

I came upon this code, marked "error," in an application I'm to update. Running it on a test database gives a cyclical reference error:
The referential relationship will result in a cyclical reference that is not allowed (Constraint name = descriptions_fk_2)
I named the constraints to see which one caused the problem.
CREATE TABLE items (
id INT NOT NULL UNIQUE IDENTITY,
name NCHAR(100) NOT NULL UNIQUE,
PRIMARY KEY (id)
);
CREATE TABLE sources (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
CREATE TABLE descriptions (
id INT NOT NULL UNIQUE IDENTITY,
item_id INT NOT NULL,
source_id INT NOT NULL,
PRIMARY KEY (id),
CONSTRAINT descriptions_fk_1 FOREIGN KEY (item_id)
REFERENCES items(id) ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT descriptions_fk_2 FOREIGN KEY (source_id)
REFERENCES sources(id) ON UPDATE NO ACTION ON DELETE CASCADE
);
Why is this a cyclical reference? The descriptions table is linked to two separate tables, but none of them link back to descriptions.
It's not strictly cyclical - but there are multiple cascade paths. So you could cascade delete a row in items two ways:
1) description -> item
2) description -> source -> item
And, for that reason, it's disallowed.
I believe it's a performance concern, as PostGres will allow cycles like that and will just work it out, but deletes under those circumstances can be quite slow.
For some further reading about why it's disallowed, please see this answer.