SQL - deleting rows from other tables with on cascade not working - sql

I have 4 related tables: order_info, master_bill, master_bill_order_leg and order_leg.
MasterBill and OrderLeg have many to many relationship.
This is the quick documentation from IntelliJ for the DB tables.
Table master_bill:
create table master_bill
(
id bigint not null
primary key,
mb_no bigint not null,
created_by text not null,
updated_by text not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
Table order_info:
create table order_info
(
order_id bigint not null
primary key
references public.order_t
on delete cascade,
mb_id bigint not null
references master_air_waybill
on delete cascade,
created_by text not null,
updated_by text not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
Table master_bill_order_leg:
create table master_bill_order_leg
(
mb_id bigint
references master_bill
on delete cascade,
order_leg_id bigint
references order_leg
on delete cascade,
constraint master_bill_order_mb_id_order_leg_id_key
unique (mb_id, order_leg_id)
);
Table order_leg:
create table order_leg
(
id bigserial
primary key,
created_by text not null,
updated_by text not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null,
constraint order_leg_unique_c
unique (flight_id, flight_date, departure_iata, arrival_iata)
);
I have set the foreign keys and they look like this:
Table master_bill_order_leg
master_bill_order_leg_mb_id_fkey (mb_id) -> master_bill(id)
master_bill_order_leg_order_leg_id_fkey (order_leg_id) -> order_leg(id)
Table order_info
order_info_mb_id_fkey (mb_id) -> master_bill(id) ON DELETE CASCADE
order_info_order_id_fkey (order_id) -> order_t(id) ON DELETE CASCADE
I thought that if order from order_t table is deleted that all relevant rows from other tables would be deleted too, but that is not the case, only order_info row is deleted on cascade. So, I have tried with deleting a row from master_bill table:
DELETE
FROM master_bill
WHERE id = :mbId
But, then I get an error:
org.postgresql.util.PSQLException: ERROR: update or delete on table
"master_bill" violates foreign key constraint
"master_bill_order_leg_mb_id_fkey" on table
"master_bill_order_leg" Detail: Key (id)=(1076) is
still referenced from table "master_bill_order_leg".
I thought that this foreign key would delete on cascade from master_bill_order_leg table if I delete a row from master_bill, since master_bill is a parent table.
What am I doing wrong here?

You can use both foreign key and trigger to achieve this. (Result here)
create table master_bill
(
id bigint not null primary key,
mb_no bigint not null,
created_by text not null,
updated_by text not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
create table order_info
(
order_id bigint not null primary key,
mb_id bigint not null references master_bill(id) on delete cascade,
created_by text not null,
updated_by text not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
create table master_bill_order_leg
(
mb_id bigint references master_bill(id) on delete cascade,
order_leg_id bigint,
constraint master_bill_order_mb_id_order_leg_id_key unique (mb_id, order_leg_id)
);
create table order_leg
(
id bigint primary key,
created_by text not null,
updated_by text not null,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone default now() not null
);
CREATE OR REPLACE FUNCTION public.f_delete_order_leg()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
delete from order_leg where id in (select order_leg_id from old_table);
return null;
end;
$function$
;
create trigger t_delete_order_leg
after delete
on master_bill_order_leg
referencing old table as old_table
for each statement
execute function f_delete_order_leg();

The way a CASCADE DELETE works is when you delete a record in the parent table, the related records in the child table are deleted
When you are running
DELETE
FROM order_info
WHERE order_id = :id
this is a child table (its FK references a PK in a parent table) it doesn't have any foreign keys in other tables that reference it
A simple example of how cascade deletes works is a two table database of departments and employees - one department can have many employees and this is enforced by an FK / PK relationship:
CREATE TABLE Department
(
DeptId INT PRIMARY KEY,
DeptName VARCHAR(10)
);
CREATE TABLE Employee
(
EmployeeID INT ,
EmployeeName VARCHAR(10),
DepartmentID INT,
CONSTRAINT DepartmentID_FK foreign key (DepartmentID) references Department(DeptId) ON DELETE CASCADE
);
INSERT INTO Department VALUES (1,'IT'),(2,'HR');
INSERT INTO Employee VALUES (1,'John',2),(2,'Kim',2),(3,'Sam',1);
At this point, if I run
SELECT * FROM Employee
I get 3 records.
If I delete a department from the parent table:
DELETE FROM Department WHERE DeptID = 2
and I run the select again, I now have 1 record (Sam) as the cascade delete has deleted those employees in department ID 2 (john and Kim)

Related

PostgreSQL cyclic foreign keys to multiple tables

I have 3 tables.
chat_room
create table chat_room
(
id uuid default uuid_generate_v4() not null
constraint chat_room_pk
primary key
constraint fk__chat_room__group_chat_room
references group_chat_room
on update cascade on delete cascade
constraint fk__chat_room__private_chat_room
references private_chat_room
on update cascade on delete cascade,
name varchar(255) not null,
description varchar(255),
profile_pic varchar(128),
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone
);
group_chat_room
create table group_chat_room
(
id uuid not null
constraint group_chat_room_pk
primary key
constraint fk__group_chat_room___chat_room
references chat_room
on update cascade on delete cascade,
pus_code char(7) not null
constraint fk__group_chat_room__puskesmas
references puskesmas
on update cascade on delete cascade
);
create unique index group_chat_room_pus_code_uindex
on group_chat_room (pus_code);
private_chat_room
create table private_chat_room
(
id uuid not null
constraint private_chat_room_pk
primary key
constraint fk__private_chat_room__chat_room
references chat_room
on update cascade on delete cascade
);
As you can see chat_room has foreign key constraints that refer to group_chat_room and private_chat_room. Also both group_chat_room and private_chat_room have FK constraints that refer to chat_room.
When I want to INSERT a row into group_chat_room I would use this
with chat_room as (
insert into chat_room (id, name) values ('Some ID', 'Some Name')
)
insert into group_chat_room(id, pus_code) values ('Some ID', 'Some Code');
However because of those constraints this would produce an error
[23503] ERROR: insert or update on table "chat_room" violates foreign key constraint "fk__chat_room__private_chat_room" Detail: Key (id)=(cef8c655-d46a-4f63-bdc8-77113b1b74b4) is not present in table "private_chat_room".
How do I only insert to group_chat_room without having to insert it to private_chat_room?
The main problem here is creating multiple required bidirectional foreign keys. You can probably work around it, eventually. But it makes the data model more complicated and the code much more so. And it is completely unnecessary. Everything you have can be accomplished with just 1 table. If group_chat_room and private_chat_room are independently required then create a view for each. Further, as simple views, they are fully update-able.
You accomplish this by moving the column 'pus_code' to chat_room and adding 2 boolean values to indicate if this is a private or group room or both. Yes, strange as it sounds you can get a private_group_chat_room. (Note: there is nothing in your design preventing it and the error you are getting is because it is required). If you do want that then create a check constraint requiring at least 1 of the boolean columns be false.
create table chat_room
(
id integer generated always as identity
constraint chat_room_pk
primary key,
name varchar(255) not null,
description varchar(255),
profile_pic varchar(128),
is_private boolean not null default false,
is_group boolean not null default false,
pus_code varchar(7)
constraint fk__group_chat_room__puskesmas
references puskesmas
on update cascade on delete cascade,
created_at timestamp with time zone default now() not null,
updated_at timestamp with time zone,
constraint not_group_of_pus_code_check
check ( (not is_group and pus_code is null)
or (is_group and pus_code is not null)
)
);
-- create unique partial index
create unique index group_chat_room_pus_code_uindex on chat_room(pus_code)
where is_group;
-- group_chat_room
create view group_chat_room
( id
, name
, description
, profile_pic
, is_private
, pus_code
, created_at
, updated_at
) as
select id
, name
, description
, profile_pic
, is_private
, pus_code
, created_at
, updated_at
from chat_room
where is_group;
-- private_chat_room
create view private_chat_room
( id
, name
, description
, profile_pic
, is_group
, pus_code
, created_at
, updated_at
) as
select id
, name
, description
, profile_pic
, is_group
, pus_code
, created_at
, updated_at
from chat_room
where is_private;
See fiddle for full example and a few tests. Note: fiddle has an issue with generate_uuid_v4() (did not exist) so for demo I changed to identity. It will fine in an operational environment.

Inheritance PostgreSQL

I have 2 tables under PostgreSQL:
a table property that is the mother table (id, created_at ..) and
a habitation table (surface, room, etc...) that inherits property. On the mother table, I have a many-many relationship table. I have added results in the "habitation" table.
When I try to insert "property_table" with habitation IDs, I have a SQL error telling me that the property_id ID does not exist. It exists when I go to see the property or habitation table. I want to insert habitation IDs (so property) in property_tag , should I force insert? Help please
The SQL schemas :
CREATE TABLE "property"
(
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
address_id int NOT NULL,
permission int NOT NULL DEFAULT 6,
user_id int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (address_id) REFERENCES "address"(id),
FOREIGN KEY (user_id) REFERENCES "user"(id)
);
CREATE TABLE habitation (
total_surface float,
living_surface float,
ground_surface float,
ground_surface_unity character varying(15) DEFAULT 'm2',
room integer,
bedroom integer,
floor integer,
level integer,
year integer
) INHERITS (property);
CREATE TABLE property_tag
(
tag_id int NOT NULL,
property_id int NOT NULL,
PRIMARY KEY (tag_id, property_id),
FOREIGN KEY (tag_id) REFERENCES "tag"(id) ON DELETE CASCADE,
FOREIGN KEY (property_id) REFERENCES "property"(id) ON DELETE CASCADE
);
CREATE TABLE "tag"
(
id serial NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
name character varying(127) NOT NULL,
user_id int NOT NULL,
color character varying(7) NOT NULL DEFAULT 'A4A4A8',
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE
);
When you insert into table "habitation", the row exists physically only in table "habitation", but not in "property".
Your table "property_tag" references to table "property" (which have no rows, cause rows are in table "habitation") and therefore you are getting error during insert.
You have to change your reference in table "property_tag" from "property" to "habitation".

Foreign key Constraint on delete cascade does not work postgres

When I run DELETE FROM users WHERE id='some_id' the record on beta_keys table does not get deleted.
beta_keys table:
CREATE TABLE beta_keys (
id serial PRIMARY KEY,
key VARCHAR(60) UNIQUE NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP
);
users table:
CREATE TABLE users (
id serial PRIMARY KEY,
email VARCHAR (256) UNIQUE NOT NULL,
password VARCHAR (60) NOT NULL,
beta_key_id INTEGER,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP,
CONSTRAINT users_beta_key_id_fkey FOREIGN KEY (beta_key_id)
REFERENCES beta_keys (id) MATCH SIMPLE
ON DELETE CASCADE
);
users references beta_keys. delete cascade works by deleting referencing rows (users) when the referenced row (beta_keys) is deleted.
sqlfiddle: http://sqlfiddle.com/#!17/a7495/1

SQL insert while checking that one of keys is equal to a field in another table

I'm using sequelize and postgresql but I think this is a more generic SQL/table question.
I have a setup similar to:
CREATE TABLE "Mixtime" (
id bigint NOT NULL,
duration character varying(255) NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
spell_id bigint NOT NULL,
ingredient_id bigint NOT NULL,
user_id bigint NOT NULL
);
CREATE TABLE "Spell" (
id bigint NOT NULL,
instructions character varying(5000) NOT NULL,
created_at timestamp with time zone NOT NULL,
user_id bigint NOT NULL
);
CREATE TABLE "Ingredient" (
id bigint NOT NULL,
ing_name character varying(255) NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
user_id bigint NOT NULL
);
CREATE TABLE "Users" (
id bigint NOT NULL,
user_name character varying(255) NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
);
ALTER TABLE ONLY "Mixtime"
ADD CONSTRAINT "Mixtime_pkey" PRIMARY KEY (id);
ALTER TABLE ONLY "Spell"
ADD CONSTRAINT "Spell_pkey" PRIMARY KEY (id);
ALTER TABLE ONLY "Ingredient"
ADD CONSTRAINT "Ingredient_pkey" PRIMARY KEY (id);
ALTER TABLE ONLY "Users"
ADD CONSTRAINT "Users_pkey" PRIMARY KEY (id);
ALTER TABLE ONLY "Mixtime"
ADD CONSTRAINT "Mixtime_spell_id_fkey" FOREIGN KEY (spell_id) REFERENCES "Spell"(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY "Mixtime"
ADD CONSTRAINT "Mixtime_ingredient_id_fkey" FOREIGN KEY (ingredient_id) REFERENCES "Ingredient"(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY "Mixtime"
ADD CONSTRAINT "Mixtime_user_id_fkey" FOREIGN KEY (user_id) REFERENCES "Users"(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY "Spell"
ADD CONSTRAINT "Spell_user_id_fkey" FOREIGN KEY (user_id) REFERENCES "Users"(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY "Ingredient"
ADD CONSTRAINT "Ingredient_user_id_fkey" FOREIGN KEY (user_id) REFERENCES "Users"(id) ON UPDATE CASCADE ON DELETE CASCADE;
What I'd like to do is make sure when I insert into Mixtime that user_id matches Spell & Ingredients' user_id fields
Pseudo code:
If (newMixtime.userId != Spell.user_id || newMixtime.userId != Ingredient.user_id) {
failHere
} else {
insert newMixtime into M
}
Note that this is not a join table. All three tables need to be query-able by the user_id field and table Mixtime has specific extra fields, it's just referencing Spell & Ingredient.
I could (and am currently) validating at the ORM layer by querying the db first, but this seems like something that should be possible in the DB layer and would save me trips.
If you know how to map this into Sequlize's Model syntax, that'd be grand, but I can probably figure that out if I have a pure postgres/SQL solution.
A fully normalized set of tables wouldn't have this constraint. The simplest way to represent this information is for every row in every table to be "owned" by exactly one row of exactly one parent table, as represented by a single foreign key. Unless you have a pressing need to share ingredients among different steps/spells of one user but NOT share them among different users, you don't need a user_id field on the ingredient.
Having true multi-table constraints (other than foreign keys) is extremely hard to do accurately and reliably. You can use triggers, but they tend to be very engine-specific, and I don't recommend putting your logic into triggers unless there's really no alternative.

Oracle - How do I create a table that has an autoincrementing unique key for the ID

This is the first time that I've use oracle SQL and I'm having a problem creating tables with a unique key.
I don't understand why this auto-incrementing id is not working:
ID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
The next question I have is why I am getting an error in each of my statements:
ORA-00922: missing or invalid option
Here is my code:
--
-- Sequence for aout incrment
--
CREATE SEQUENCE IF NOT EXISTS AUTO_INC_SEQ
START WITH 1
INCREMENT BY 1;
--
-- Table Person
--
CREATE TABLE IF NOT EXISTS FITNESS_PERSON
(
ID NUMBER NOT NULL PRIMARY KEY,
FIRST_NAME VARCHAR NOT NULL,
LAST_NAME VARCHAR NOT NULL,
NICK_NAME VARCHAR NOT NULL,
DATE_BIRTH DATE NOT NULL,
PASSWORD VARCHAR NOT NULL,
CONSTRAINT UNIQUE(NICK_NAME)
);
--
-- Table BMR
--
CREATE TABLE IF NOT EXISTS FITNESS_BMR
(
ID NUMBER NOT NULL PRIMARY KEY,
VALUE FLOAT NOT NULL,
VALUE_DATE DATE NOT NULL
);
--
-- M:N for BMR and Person
--
CREATE TABLE IF NOT EXISTS FITNESS_BMR_PERSON
(
BMR_ID NUMBER NOT NULL,
PERSON_ID NUMBER NOT NULL,
FOREIGN KEY(BMR_ID) REFERENCES FITNESS_BMR(ID),
FOREIGN KEY(PERSON_ID) REFERENCES FITNESS_PERSON(ID),
CONSTRAINT BMR_PER PRIMARY KEY(BMR_ID, PERSON_ID)
);
What's the right way to do this (create a table and with an auto-incrementing key that is unique).
You can use a table, a sequence to generate unique ID values and a trigger.
For example:
Table:
CREATE Table FITNESS_BMR
(
ID NUMBER NOT NULL PRIMARY KEY,
VALUE FLOAT NOT NULL,
VALUE_DATE DATE NOT NULL
);
Sequence: create sequence t1_seq start with 1 increment by 1 nomaxvalue;
Trigger:
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON FITNESS_BMR
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT t1_seq.nextval INTO :NEW.ID FROM dual;
END;
/