SQL trigger INSERT AFTER - sql

I am trying to setup a trigger in phpMyAdmin. have two tables, one parent, one child. Every time a new user in users is created I want to automaticly create a line in the permissions table with the (AI) generated id from users as user_id. (permissions.user_id is a foreigh key of users.id)
users:
id email password
--------------------------------------
1 eric#test.com sdgsdhdfhs
2 john#test.com dfgsdgdfhg
3 mary#test.com plkdfjvjvv
permissions:
index user_id allow_read allow_write allow_delete
-------------------------------------------------------------
1 1 1 0 1
2 2 1 1 1
3 3 0 0 0
I've tried (without success):
INSERT
INTO permissions
(user_id)
VALUES
(IDENT_CURRENT('users'))
and
INSERT
INTO permissions
(user_id)
VALUES
(##IDENTITY)

To access the data of the row that caused a trigger to be executed, you can use NEW and OLD aliases. For INSERT triggers only NEW is available. For DELETE triggers only OLD is available. In UPDATE triggers you can use both. They are used the same way as table aliases (e.g. NEW.id / OLD.id).
Given a parent and a child table as follows:
create table parent_table(
id int auto_increment primary key,
pdata varchar(50)
);
create table child_table(
id int auto_increment primary key,
parent_id int not null,
cdata varchar(50) default '',
foreign key (parent_id) references parent_table(id)
);
To insert a child row when a parent row is inserted:
create trigger insert_parent
after insert on parent_table
for each row
insert into child_table(parent_id)
values (new.id);
To delete all related child rows wehen a parent row is deleted:
create trigger delete_parent
before delete on parent_table
for each row
delete from child_table
where parent_id = old.id;
Demo: http://rextester.com/EOW74217
However a delete trigger is not necessary if you define your foreign key with ON DELETE CASCADE
foreign key (parent_id) references parent_table(id) on delete cascade
All related child rows will be deleted without a trigger when you delete the parent row.
Demo: http://rextester.com/CWB43482

Related

Cascade Delete Children not working as expected

I have two tables one of which is for the polymorphic relationship of different corporations and I've added foreign key references to ids to ensure that if I delete a parent all children will be deleted. With this table setup below if I delete a parent corporation the child corporation persists which is not what I expected. If I delete a corporation_relationship via the parent_id the parent and its children cascade delete and if I a delete the relationship via the child_id the parent and siblings are unaffected. My questions are what am I doing wrong and how can I ensure that by deleting a parent the children are also deleted without adding any new columns?
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TYPE "corporation_relationship_type" AS ENUM (
'campus',
'network'
);
CREATE TABLE "corporations" (
"id" uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
"name" varchar(255) NOT NULL
);
CREATE TABLE "corporation_relationships" (
"parent_id" uuid NOT NULL,
"child_id" uuid NOT NULL,
"type" corporation_relationship_type NOT NULL,
PRIMARY KEY ("parent_id", "child_id")
);
ALTER TABLE "corporation_relationships" ADD FOREIGN KEY ("parent_id") REFERENCES "corporations" ("id") ON DELETE CASCADE;
ALTER TABLE "corporation_relationships" ADD FOREIGN KEY ("child_id") REFERENCES "corporations" ("id") ON DELETE CASCADE;
Example queries:
If I add 2 corporations and then add a relationship to the two like so:
insert into corporations (id, name) values ('f9f8f7f6-f5f4f3f2-f1f0f0f0-f0f0f0f0', 'Father');
insert into corporations (id, name) values ('f9f8f7f6-f5f4f3f2-f1f0f0f0-f0f0f0f1', 'Son');
insert into corporation_relationships (parent_id, child_id) values ('f9f8f7f6-f5f4f3f2-f1f0f0f0-f0f0f0f0', 'f9f8f7f6-f5f4f3f2-f1f0f0f0-f0f0f0f1');
My output for select * from corporations; will be:
id | name
--------------------------------------+--------------------
f9f8f7f6-f5f4-f3f2-f1f0-f0f0f0f0f0f0 | Father
f9f8f7f6-f5f4-f3f2-f1f0-f0f0f0f0f0f1 | Son
(2 rows)
My output for select * from corporation_relationships; is:
parent_id | child_id | type
--------------------------------------+--------------------------------------+--------
f9f8f7f6-f5f4-f3f2-f1f0-f0f0f0f0f0f0 | f9f8f7f6-f5f4-f3f2-f1f0-f0f0f0f0f0f1 | campus
Now if I delete the 'father' by executing delete FROM corporations WHERE id = 'f9f8f7f6-f5f4-f3f2-f1f0-f0f0f0f0f0f0'; I would expect my output of select * from corporations; to be nothing but instead it is the following:
id | name
--------------------------------------+--------------------
f9f8f7f6-f5f4-f3f2-f1f0-f0f0f0f0f0f1 | Son
(1 row)
Also, it is noteworthy that the corporation_relationships table is empty after this delete as well but I would want the cascade to keep going past that table and delete the child entity as well.
Your second foreign key constraint in the corporation_relationships table, that references to the corporations table has nothing with with your expectations of cascade deletions of children rows in corporations. To clearify, this foreign key do cascade deletions when you delete a referenced row in the corporations table. But you need the opposite.
To make it work as you expect in your design, you should have a column in corporations that references a primary key in corporation_relationships.
So you need to
create a primary key column, e.g. id, in corporation_relationships (not those you already have, it's not a pk, it's a unique constraint).
create a column in corporations and add a foreign key constraint on it that references a created corporation_relationships pk.
Remove a child_id column from corporation_relationships, it's incorrect and useless at this point.
When you create a relation you should set it's id to the fk column of corresponding child row in corporations.
Now, if you delete a parent corporation, it would delete all relationships, those will delete corresponding children of corporation and so on recursively.
Meanwhile, in my opinion, your design is not correct.
To define a tree-like relations you do not need the transit table, i.e
corporation_relationships. You can define it in a single corporations table. For that you need just a one column parent_id, those would be a foreign key with cascade delete rule, that references a pk in this table. Top-parent corporations would have a null in parent_id, all children - parent's id value.
Also, type column in corporation_relationships is not an attribute of relation itself, it's an attribute of child.
Postgres doesn't mantain referential integrity with optional polymorphic relationships so I created a trigger to do this for me:
CREATE FUNCTION cascade_delete_children() RETURNS trigger AS $$
BEGIN
-- Check if the corporation is a parent
IF OLD.id IN (SELECT parent_id FROM corporation_relationships) THEN
-- Delete all of the corporation's children
DELETE FROM corporations WHERE id IN (SELECT child_id FROM corporation_relationships WHERE parent_id = OLD.id);
END IF;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE trigger cascade_delete_children BEFORE DELETE ON corporations
FOR EACH ROW EXECUTE PROCEDURE cascade_delete_children();

Get the count for deleted records when using cascade delete constraint

When using ON DELETE CASCADE constraint and deleting a "master" record is there a way to get the count of "child" records deleted?
For example using these table:
CREATE TABLE Master ( ID INT PRIMARY KEY (ID))
GO
CREATE TABLE Child ( ID INT, MasterID INT)
GO
ALTER TABLE Child ADD CONSTRAINT FK_Child_Master FOREIGN KEY(MasterID)
REFERENCES Master (ID)
ON DELETE CASCADE
GO
INSERT INTO Master (ID) VALUES (1);
INSERT INTO Child ( ID, MasterID) VALUES (1,1),(2,1),(3,1),(4,1),(5,1);
Now if I delete the master record like this:
DELETE FROM Master WHERE ID = 1;
SELECT ##ROWCOUNT;
The 5 rows in the Child table and the 1 row in the Master table are all deleted but the result is 1. It only counts the records deleted from the master table.
Is there a way to capture the number of rows deleted from the child table or do I need delete from the child table first, get the count, then delete from the master?
Yes you can use a stored procedure to do this like
create procedure spDeleteRecords
#id int
begin
select count(*) from child_table where id=#id
delete from parent_table where id=#id
end

PostgreSQL Cascade for columns (not foreign key)

create table parent (
child_type not null
child_id not null
);
create table child1(id not null);
create table child2(id not null);
create table child3(id not null);
And there's some rows in table parent like this:
child_type,child_id
"child1",1
"child1",2
"child2",1
"child3",1
I want to delete child row when I delete parent row.
Is there any way to make this trigger on delete cascade?
I presume that (child_type,child_id) is the primary key of parent (and this advice will only work if it is thus: if you want deleting of a parent row to trigger a delete in a child via a FK cascade, the parent must have a primary key)
You create associations like this:
create table child1(
child_type VARCHAR(20) DEFAULT 'child1',
id INT not null
FOREIGN KEY (child_type,id) REFERENCES parent(child_type, child_id) ON DELETE CASCADE
);
create table child2(
child_type VARCHAR(20) DEFAULT 'child2',
id INT not null
FOREIGN KEY (child_type,id) REFERENCES parent(child_type, child_id) ON DELETE CASCADE
);
create table child3(
child_type VARCHAR(20) DEFAULT 'child3',
id INT not null
FOREIGN KEY (child_type,id) REFERENCES parent(child_type, child_id) ON DELETE CASCADE
);
You can't have just id in the child references part of the composite PK in the parent; child has to have the same N columns with the same values as the parent PK has
FWIW that table structure is really wonky, and it will probably come around to bite you time and again.
Prefer something more normal, like:
create table parent (
id PRIMARY KEY
);
create table child1(id PRIMARY KEY, parent_id REFERENCES parent(id));
create table child2(id PRIMARY KEY, parent_id REFERENCES parent(id));
create table child3(id PRIMARY KEY, parent_id REFERENCES parent(id));
I hope this is a contrived situation for you actual problem, as it really is a terrible design. Assuming you actually "want to delete child row when I delete parent row". Unless you alter your data model and define FK constraints you require a delete trigger on table parent. You CANNOT cascade deletes without FK as that is where you define to Postgres to do so. BTW, your table definitions are invalid. Not Null is a constraint not a data type, you have not established a data type. After correcting that you can build a trigger which deletes the corresponding rows from the appropriate child table if your child_type column is understood to actually name the table in which the child resides. A very poor design leading to a extremely risky assumption, but:
-- setup
create table parent (
child_type text not null
,child_id integer not null
);
create table child1(id integer not null);
create table child2(id integer not null);
create table child3(id integer not null)
insert into parent(child_type, child_id)
values ('child1',1),('child1',2),('child2',1),('child3',1);
insert into child1(id) values (1),(2);
insert into child2(id) values (1);
insert into child3(id) values (1);
Now create the trigger function then 'attach' to parent table'
The trigger function now builds and dynamically executes the appropriate delete statement. Note I always generate a raise notice to display the actual statement before executing it, and do so here. You may consider it not necessary.
-- build trigger function.
create or replace function parent_adr()
returns trigger
language plpgsql
as $$
declare
base_del_lk constant text = 'delete from %s where id = %s';
sql_delete_stmt_l text;
begin
sql_delete_stmt_l = format(base_del_lk,old.child_type, old.child_id);
raise notice 'Running statement==%', sql_delete_stmt_l;
EXECUTE sql_delete_stmt_l;
return old;
end;
$$;
-- and define the trigger on the parent table.
create trigger parent_adr_trig
after delete
on parent
for each row
execute procedure parent_adr();
--- test.
delete from parent where child_type = 'child1';

Very slow SQL DELETE query on table with foreign key constraint

I have got some trouble with a SQL DELETE query.
I work on a database (postgres 9.3) with 2 tables (Parent and Child).
The child has a relation to the parent with a foreign key.
Parent Table
CREATE TABLE parent
(
id bigint NOT NULL,
...
CONSTRAINT parent_pkey PRIMARY KEY (id)
)
Child Table
CREATE TABLE child
(
id bigint NOT NULL,
parent_id bigint,
...
CONSTRAINT child_pkey PRIMARY KEY (id),
CONSTRAINT fk_adc9xan172ilseglcmi1hi0co FOREIGN KEY (parent_id)
REFERENCES parent (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I inserted in both tables 200'000 entries without any relation ( Child.parent_id = NULL).
But a DELETE query like below has a duration of more than 20 minutes.
And that even without a WHERE conditions.
DELETE FROM Parent;
If I don't add the relation constraints the execution time will be done in 400 ms.
What did I miss?
A workable solution is the example below. But I don't know if this is a good idea. Maybe anyone could tell me a better way to do that.
BEGIN WORK;
ALTER TABLE Parent DISABLE TRIGGER ALL;
DELETE FROM Parent;
ALTER TABLE Parent ENABLE TRIGGER ALL;
COMMIT WORK;
When you delete from Parent, the Child table needs to be queried by parent_id to ensure that no child row refers to the parent row you are about to delete.
To ensure that the child lookup runs quickly, you need to have an index on your parent_id column in the Child table.

INSERT statement conflicted with the FOREIGN KEY constraint even after deleting the records from the child table

I tired to delete the occurrences of the values which the insert stmt was trying to insert
in the dependent table.
delete from BSC_UR_RE_AN
where AN_ID in (1084,1083,1088,1087,1121,1122,1123,1094,5010,
1239,1242,70187,7001,7002,1284,1285)
But no luck, I got the following error:
The INSERT statement conflicted with the FOREIGN KEY constraint
"BSC_U_RA_AN_FK". The conflict occurred in database "DMDB", table
"dbo.BSC_AN", column 'AN_ID'.
That error means that ANAG_ID value you're trying to insert as FOREIGN KEY doesn't exists on the related table. So, you should not delete it, you should insert it if it's missing.
Edit:
You should have a clear view of your tables design, examining the constraint of the tables you're trying to insert into.
As example, a Foreign Key constraint usually refers to a Primary Key of another table.
The error you see is because you're trying to inserd an invalid value (eg. not exists in the master table).
An example:
CREATE TABLE MasterTable (
ID int NOT NULL PRIMARY KEY,
Name varchar(50)
)
CREATE TABLE MasterTableValues (
IDMasterTable INT REFERENCES MasterTable(ID), --FOREIGN KEY
Value varchar(50)
)
Let's assume you have this data in MasterTable:
1 FirstDate
2 SecondData
3 ThirdData
5 FifthData
Now, you can insert these values in MasterTableValues:
1 SomeValue
1 SomeOtherValue
3 SomeValue
But CANNOT insert this value:
4 SomeValue
because the key ID = 4 don't exist in MasterTable