See: SQLFiddle
I am having a many-to-many relationship as such:
CREATE TABLE IF NOT EXISTS store (
id BIGSERIAL PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS collection (
id BIGSERIAL PRIMARY KEY,
store_id bigint NOT NULL,
FOREIGN KEY (store_id) REFERENCES store (id)
ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS item (
id BIGSERIAL PRIMARY KEY,
store_id bigint NOT NULL,
FOREIGN KEY (store_id) REFERENCES store (id)
ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS collection_item (
id BIGSERIAL PRIMARY KEY,
collection_id bigint NOT NULL,
item_id bigint,
UNIQUE (collection_id, item_id),
FOREIGN KEY (collection_id) REFERENCES collection (id)
ON DELETE CASCADE,
FOREIGN KEY (item_id) REFERENCES item (id)
ON DELETE SET NULL
);
INSERT INTO store (id) VALUES (1);
INSERT INTO item (id, store_id) VALUES (1, 1);
INSERT INTO collection (id, store_id) VALUES (1, 1);
INSERT INTO collection_item (id, collection_id, item_id) VALUES (DEFAULT, 1, 1);
My problem is that deleting a store
DELETE FROM store WHERE store.id = 1;
will give the following error:
ERROR: update or delete on table "store" violates foreign key constraint
"collection_store_id_fkey" on table "collection"
Detail: Key (id)=(1) is still referenced from table "collection".
I understand the problem but I don't know how I can resolve this issue.
If an item gets deleted, the relation in collection_item should not be removed but the item_id should be set to NULL. On the other hand, if a collection gets deleted, all related collection_item records should be removed as well.
How can DELETE CASCADE work in such a setup or do I have to model my tables differently if I need this kind of behavior?
You can just add ON DELETE CASCADE where you have ON UPDATE CASCADE
CREATE TABLE IF NOT EXISTS collection (
id BIGSERIAL PRIMARY KEY,
store_id bigint NOT NULL,
FOREIGN KEY (store_id) REFERENCES store (id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS item (
id BIGSERIAL PRIMARY KEY,
store_id bigint NOT NULL,
FOREIGN KEY (store_id) REFERENCES store (id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
SQLFiddle
To resolve this issue you can add ON DELETE behavior to the store_id column of the collection table (and that of the item table). Basically you can just change the ON UPDATE to ON DELETE in both of these table definitions, since I doubt you'll be updating the id column for existing rows in the store table. You also mention the ON DELETE behavior for the item_id column of collection_item, if you want the column value to become NULL when an item is deleted, you can use ON DELETE SET NULL.
CREATE TABLE IF NOT EXISTS store (
id BIGSERIAL PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS collection (
id BIGSERIAL PRIMARY KEY,
store_id bigint NOT NULL,
FOREIGN KEY (store_id) REFERENCES store (id)
ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS item (
id BIGSERIAL PRIMARY KEY,
store_id bigint NOT NULL,
FOREIGN KEY (store_id) REFERENCES store (id)
ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS collection_item (
id BIGSERIAL PRIMARY KEY,
collection_id bigint NOT NULL,
item_id bigint NOT NULL,
UNIQUE (collection_id, item_id),
FOREIGN KEY (collection_id) REFERENCES collection (id)
ON DELETE CASCADE,
FOREIGN KEY (item_id) REFERENCES item (id)
ON DELETE SET NULL
);
On this case, you have to delete table refrences before to delete stores id.
Or to do the modifications above.
DELETE FROM item where store_id= 1;
DELETE FROM collection where store_id = 1;
DELETE FROM store where id = 1;
Related
I can not add a referential constraint, both tables has the two columns id and version as primary key. I will only check the integrity that the row is existing without checking version.
CREATE TABLE TABLEE
(ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
PRIMARY KEY (ID , VERSION);
CREATE TABLE CHAIR
(ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
ID_TABLEE INTEGER,
PRIMARY KEY (ID , VERSION);
ALTER Chair
ADD constraint MYC FOREIGN KEY (ID)
REFERENCES TABLEE (ID)
ON DELETE RESTRICT
Got this error
[Code: -573, SQL State: 42890] A column list specified in the references clause of constraint "MYC " does not identify a unique constraint of the parent table or nickname "TABLEE".. SQLCODE=-573, SQLSTATE=42890, DRIVER=4.28.11
The problem here is that Tablee its primary key consists of two columns id and version. But the integrity check should only be made on the referenced id, not version.
To establish a foreign key on a table the referenced column needs to:
Be a primary key on the other table.
...or at least act as one.
Since you already have a primary key on the referenced table, you can use the second approach and add a UNIQUE and NOT NULL constraints on ID.
For example:
CREATE TABLE TABLEE (
ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
PRIMARY KEY (ID , VERSION),
constraint uq1 unique (id) -- added UNIQUE constraint on ID
);
CREATE TABLE CHAIR (
ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
ID_TABLEE INTEGER,
PRIMARY KEY (ID , VERSION)
);
ALTER table Chair
ADD constraint MYC FOREIGN KEY (ID)
REFERENCES TABLEE (ID)
ON DELETE RESTRICT;
See running example at db<>fiddle.
Note: If you want ID to have repeated values over the table, then ID is not a key, and cannot be referenced as one.
I need to add a foreign keys for two tables each other. Can this be done ?
As an example:
CREATE SCHEMA IF NOT EXISTS schema1;
CREATE TABLE schema1.tableA
(
id serial NOT NULL,
tableB_id integer,
PRIMARY KEY (id),
FOREIGN KEY (tableB_id) REFERENCES schema1.tableB (id)
);
CREATE TABLE schema1.tableB
(
id serial NOT NULL,
tableA_id integer,
PRIMARY KEY (id),
FOREIGN KEY (tableA_id) REFERENCES schema1.tableA(id)
);
Above query causes an error !
ERROR: relation "schema1.tableb" does not exist SQL state: 42P01
Can this be done or is there any better solution?
I'am using PostgreSQL version 10.5 and pgAdmin 3.6
Add the foreign keys after creating both tables:
CREATE SCHEMA IF NOT EXISTS schema1;
CREATE TABLE schema1.tableA
(
id serial NOT NULL,
tableB_id integer,
PRIMARY KEY (id)
);
CREATE TABLE schema1.tableB
(
id serial NOT NULL,
tableA_id integer,
PRIMARY KEY (id)
);
ALTER TABLE schema1.tablea
add FOREIGN KEY (tableB_id) REFERENCES schema1.tableB (id);
ALTER TABLE schema1.tableb
add foreign key (tableA_id) REFERENCES schema1.tableA(id);
I have defined these tables:
CREATE TABLE "public".category (id BIGSERIAL NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id));
CREATE UNIQUE INDEX category_name ON "public".category (name);
CREATE TABLE "public".clusters (id BIGSERIAL NOT NULL, name varchar(255) NOT NULL, PRIMARY KEY (id));
CREATE INDEX clusters_name ON "public".clusters (name);
CREATE TABLE "public".keywords (id BIGSERIAL NOT NULL, text varchar(255) NOT NULL, category_id int8 NOT NULL, top_results int4, cluster_id int8, month_requests int4, click_cost float8, PRIMARY KEY (id));
CREATE INDEX keywords_text ON "public".keywords (text);
ALTER TABLE "public".keywords ADD CONSTRAINT FKkeywords488682 FOREIGN KEY (cluster_id) REFERENCES "public".clusters (id);
ALTER TABLE "public".keywords ADD CONSTRAINT FKkeywords446526 FOREIGN KEY (category_id) REFERENCES "public".category (id) ON UPDATE CASCADE ON DELETE CASCADE;
added one record to category table:
INSERT INTO "public".category(id, name) VALUES (1, 'Test');
And now when I try to add record to keyword table
insert into "public"."keywords" ( "category_id", "text") values ( 1, 'testkey')
I got error:
ERROR: constraint 16959 is not a foreign key constraint
When I do
select * FROM pg_constraint;
I can't see constraint with this id. I can't understand what is the cause of this problem.
To create table I use:
CREATE TABLE category
(
cat_id serial NOT NULL,
cat_name character varying NOT NULL,
parent_id integer NOT NULL,
CONSTRAINT cat_id PRIMARY KEY (cat_id)
)
WITH (
OIDS=FALSE
);
ALTER TABLE category
OWNER TO pgsql;
parent_id is a id to another category. Now I have a problem: how to cascade delete record with its children? I need to set parent_id as foreign key to cat_id.
I try this:
ALTER TABLE category
ADD CONSTRAINT cat_cat_id_fkey FOREIGN KEY (parent_id)
REFERENCES category (cat_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
But it falls with:
ERROR: insert or update on table "category" violates foreign key constraint "cat_cat_id_fkey"
DETAIL: Key (parent_id)=(0) is not present in table "category".
The problem you have - what would be the parent_id of a category at the top of the hierarchy?
If it will be null - it will break the NOT NULL constratint.
If it will be some arbitrary number like 0 - it will break the foreign key (like in your example).
The common solution - drop the NOT NULL constratint on the parent_id and set parent_id to null for top categories.
-- create some fake data for testing
--
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE category
(
cat_id serial NOT NULL,
cat_name character varying NOT NULL,
parent_id integer NOT NULL,
CONSTRAINT cat_id PRIMARY KEY (cat_id)
);
INSERT INTO category(cat_name,parent_id)
SELECT 'Name_' || gs::text
, gs % 3
FROM generate_series(0,9) gs
;
-- find the records with the non-existing parents
SELECT ca.parent_id , COUNT(*)
FROM category ca
WHERE NOT EXISTS (
SELECT *
FROM category nx
WHERE nx.cat_id = ca.parent_id
)
GROUP BY ca.parent_id
;
-- if all is well: proceed
-- make parent pointer nullable
ALTER TABLE category
ALTER COLUMN parent_id DROP NOT NULL
;
-- set non-existing parent pointers to NULL
UPDATE category ca
SET parent_id = NULL
WHERE NOT EXISTS (
SELECT *
FROM category nx
WHERE nx.cat_id = ca.parent_id
)
;
-- Finally, add the FK constraint
ALTER TABLE category
ADD CONSTRAINT cat_cat_id_fkey FOREIGN KEY (parent_id)
REFERENCES category (cat_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
;
This is quite simple.
Here the foreign key parent_id refers to cat_id.
Here a record with parent_id=0 exists but not a record with cat_id=0.
i´ve got a problem with foreign keys, it´s an strange problem.
First table:
CREATE TABLE adjunto
(
id serial NOT NULL,
codigo text,
descripcion text,
usuario integer,
file integer,
nombre text,
propiedades hstore,
CONSTRAINT adjunto_pkey PRIMARY KEY (id ),
CONSTRAINT adjunto_file_fkey FOREIGN KEY (file)
REFERENCES file (file_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
) WITH (
OIDS=FALSE
);
Second table:
CREATE TABLE adjunto_coleccion_privada
(
id serial NOT NULL,
adjunto integer,
coleccion integer,
CONSTRAINT adjunto_coleccion_privada_pkey PRIMARY KEY (id ),
CONSTRAINT adjunto_coleccion_privada_adjunto_fkey FOREIGN KEY (adjunto)
REFERENCES adjunto (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT adjunto_coleccion_privada_coleccion_fkey FOREIGN KEY (coleccion)
REFERENCES coleccion (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
WITH (
OIDS=FALSE
);
Command:
INSERT INTO adjunto_coleccion_privada (adjunto, coleccion)
VALUES (600, 2) RETURNING id
Values 600 and 2 exist in both tables, adjunto and colecion.
Detailed error:
Mensaje: ERROR: insert or update on table "adjunto_coleccion_privada"
violates foreign key
constraint "adjunto_coleccion_privada_adjunto_fkey"
Detail: Key (adjunto)=(600) is not present in table "adjunto".
I tested your code (I dropped the adjunto_coleccion_privada_coleccion_fkey constraint since referred table does not exist in your pasted code).
I see no problem at all.
Are you really sure that there is a record with id = 600 in the adjunto table?