Cascade Triggers in SQLite - database

I've the following DB structure in SQLite:
I want to create a trigger that whenever I delete a country all the related districts, municipalities and parishes are also deleted (like MySQL InnoDB), I've tried using SQLite triggers and came up with this:
Districts:
CREATE TRIGGER [delete_country]
BEFORE DELETE
ON [countries]
FOR EACH ROW
BEGIN
DELETE FROM districts WHERE districts.id_countries = id;
END
Municipalities:
CREATE TRIGGER [delete_district]
BEFORE DELETE
ON [districts]
FOR EACH ROW
BEGIN
DELETE FROM municipalities WHERE municipalities.id_districts = id;
END
Parishes:
CREATE TRIGGER [delete_municipality]
BEFORE DELETE
ON [municipalities]
FOR EACH ROW
BEGIN
DELETE FROM parishes WHERE parishes.id_municipalities = id;
END
I haven't yet tested the delete_district and delete_municipality triggers because I get a strange behavior on the delete_country trigger: when I delete a country only the first related district gets deleted, all the others related districts remain in the table. What am I doing wrong?

The trigger looks like it is deleting districts whose id equals id_countries, that is, the where clause is actually
WHERE districts.id_countries = districts.id
You need to reference the id from the countries table. In a delete trigger, use "old" to do this.
CREATE TRIGGER [delete_country]
BEFORE DELETE
ON [countries]
FOR EACH ROW
BEGIN
DELETE FROM districts WHERE districts.id_countries = old.id;
END
Also, I would suggest changing your schema naming convention. Usually, the table name is singular, and corresponds to the entity in a row. I would have a country table with columns id and name, a district table with id, country_id and name, etc.
country
-------
id
name
district
-------
id
country_id
name
municipality
------------
id
district_id
name
parish
-----
id
municipality_id
name
Then the trigger would be
CREATE TRIGGER [delete_country]
BEFORE DELETE
ON [country]
FOR EACH ROW
BEGIN
DELETE FROM district WHERE district.country_id = old.id;
END

PRAGMA foreign_keys = 1;
This will enable the enforcement of foreign keys just like in MySQL. If you define your table with 'ON DELETE CASCADE' clause like so:
CREATE TABLE "notification" (
"rowid" INTEGER NOT NULL,
"user" INTEGER NOT NULL,
"task" TEXT NOT NULL,
"payload" TEXT NOT NULL,
PRIMARY KEY("rowid"),
FOREIGN KEY("user") REFERENCES "user" ON UPDATE CASCADE ON DELETE CASCADE
);
The whole row will be deleted when (in this case) it's parent user row is deleted.
P.S: I know this is a dead thread, but I figured I put this here for the people viewing in 2019. ;D

Related

Using triggers for referential integrity

There are 2 tables in the database that contain the following columns:
department table with column dept_no (char(4), not null)
employee table with column dept_no (char(4), null)
The dept_no column needs to be defined as a primary key in the department table and a foreign key in the employee table using a trigger.
I thought this was the correct solution using the deleted and inserted virtual tables to update/delete the foreign key in the corresponding employee table:
CREATE TRIGGER trig_delete_dept_no
ON department
AFTER DELETE
AS
UPDATE employee
SET employee.dept_no = NULL
FROM deleted
WHERE employee.dept_no = deleted.dept_no
CREATE TRIGGER trig_update_dept_no
ON department
AFTER UPDATE
AS
UPDATE e
SET e.dept_no = i.dept_no
FROM employee e
INNER JOIN inserted i ON e.dept_no = i.dept_no
However, when I update the department dept_no row to a different value I do not see the corresponding dept_no update in the employee table:
UPDATE department
SET dept_no = 'd4'
WHERE dept_no = 'd3'
Deleting functions as expected. What am I doing wrong with the update trigger and how can I combine these two triggers into one trigger?
There is an issue in your design. The first thing is you should not use dept_no as PK (Primary Key). You need to have an IDENTITY or GUID column as Primary Key and refer to that column as FK (Foreign Key).
This way you won't need to worry about changing the dept_no.
The second point is you don't need trigger. you can use CASCADE option on DELETE action.
Find more information on CASCADE
Thank you FLICKER and SMor for helping me think through this. I do not believe the assignment wants us to modify the tables by adding IDENTITY or GUID columns and since we are to strictly use Triggers, this is the best solution I can come up with:
CREATE TRIGGER trig_delete_dept_no ON department AFTER DELETE AS
UPDATE employee
SET employee.dept_no = NULL
FROM deleted
WHERE employee.dept_no = deleted.dept_no
CREATE TRIGGER trig_update_dept_no ON department AFTER UPDATE AS
IF UPDATE(dept_no)
BEGIN
IF (SELECT employee.dept_no
FROM employee, inserted
WHERE employee.dept_no = inserted.dept_no) IS NULL
BEGIN
ROLLBACK TRANSACTION
RAISERROR ('Integrity constraint violation, TRIGGER:
trig_update_dept_no, TABLE: department',16,1)
END
ELSE PRINT 'Update successful'
END
This will allow updates to occur in the department as long as there are no orphaned records in the employee table.

Error on delete trigger

This trigger should delete a row from the parent table that is not deleted from the child table. The error is in the image below.
My code attempt:
CREATE TRIGGER ProductDeleted ON Product
for DELETE AS
BEGIN
DELETE FROM OrderItem
WHERE ProductID = (SELECT ProductID FROM DELETED)
END
help me please
You could simplify it by adding a CASCADE DELETE hint on a foreign key constraint such as
CREATE TABLE OrderItem
(
ID INT ,
ProductID NOT NULL UNIQUE
CONSTRAINT fk_Products
REFERENCES Products (ID) ON DELETE CASCADE
);
Since you already have a table, all you need to do is drop the constraint and recreate a new one.
ALTER TABLE OrderItem DROP
CONSTRAINT fk_ProductID;
ALTER TABLE OrderItem ADD
CONSTRAINT fk_ProductID
FOREIGN KEY (ID)
REFERENCES Product (ID)
ON DELETE CASCADE;
What this means, is that , any time you delete a record from the parent table (Product), child records from (OrderItem)will be deleted as well, so you dont have to use triggers, unless if you want to do some recording.
If you are really insisting on using triggers then you can tweak it a little bit like this :
ALTER TRIGGER ProductDeleted on Product
INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
/* First we are deleting referenced columns in OrderItem table */
DELETE FROM OrderItem
where ProductID IN (select deleted.ID /* Columns from product Table */ from deleted)
/* Now we are doing actual delete statement */
DELETE FROM Products where ID IN (select deleted.ID from deleted)
END
But once again you should consider using ON CASCADE DELETE, its much simpler to setup, easier to maintain and you can have only one INSTEAD OF trigger per table, so if you ever need to do something more meaningful you would have to change this one, and add extra overhead.
Add SET NOCOUNT ON as the first thing in the trigger's body (after BEGIN).

Create Trigger on Parent table that creates row in Child table and assigns new Parent PK to Child as FK

I have two tables:
Customer (customerID, firstName, lastName,....)
Account (accountID, currentBalance....customerID) customerID references CUSTOMER.
The tables have a 1:M (Customer:Account), Mandatory-Mandatory relationship.
I am trying to setup a trigger that automatically creates a child row when a parent is inserted, after researching Stack and elsewhere I have managed to create a trigger on the parent that creates a row in the child:
CREATE OR REPLACE TRIGGER CMustHaveAccount
AFTER INSERT ON CUSTOMER
FOR EACH ROW
BEGIN
INSERT INTO ACCOUNT (accountID)
SELECT SEQACCOUNTID.NEXTVAL
FROM dual;
END;
/
All my attempts to set the FK in Account as the new PK in Customer are failing, I tried a number of triggers, the most promising being:
CREATE OR REPLACE TRIGGER AMustHaveCustomer
AFTER INSERT ON CUSTOMER
FOR EACH ROW
BEGIN
INSERT INTO ACCOUNT (customerID)
SELECT :new.customerID
FROM CUSTOMER;
END;
/
This trigger throws back the error
ORA-04091: table .CUSTOMER is mutating, trigger/function may not see
it
.
If I change the trigger to BEFORE, it gives error ORA-01400: cannot insert NULL into ("ACCOUNT"."ACCOUNTID"). I am assuming because technically the insert has not been completed so the PK I am inserting into Customer does not yet exist.
I want to have a trigger(s) that inserts a value into Account, with a primary key from my sequence, when a row is created in Customer, and for the PK customerID to automatically be assigned to customerID in ACCOUNT as the foreign key.
I am just learning SQL and Databases, please excuse me if the answer is obvious.
Help greatly appreciated!
The foreign key of ACCOUNT is the primary key of CUSTOMER, so this should work for you. Note the :new keyword, which is how to reference values in the current record and so avoid the "mutating table" error.
CREATE OR REPLACE TRIGGER CMustHaveAccount
AFTER INSERT ON CUSTOMER
FOR EACH ROW
BEGIN
INSERT INTO ACCOUNT (accountID, currentBalance, customerID)
values ( SEQACCOUNTID.NEXTVAL, 0, :NEW.customerID);
END;
/

Best way to delete records in two tables having foreign key?

I created two tables like marks and users. I maintained foreign key relation between two tables, When I delete a row in marks table, I need to delete that particular user in user table based on uid that exists in both tables commonly.can anyone suggest me?
Use the ON DELETE CASCADE option if you want rows deleted in the child table when corresponding rows are deleted in the parent table.
But your case is reverse from it.There is no way to do it reverse
automatically.
You need to use delete trigger explicitly whenever record are delete
from child table.
BTW its not safe to do reverse as there might be many marks record for single user and if you delete any one of them then user is removed from user table.
I suggest to do it logically in sproc.
you can check in sproc that all record for user is deleted in mark table than remove user from user table.
Well for your case, I will recommend using on delete cascade
More about it :
A foreign key with cascade delete means that if a record in the parent table is deleted, then the corresponding records in the child table will automatically be deleted. This is called a cascade delete in SQL Server.
The syntax for creating a foreign key with cascade delete using a CREATE TABLE statement in SQL Server (Transact-SQL) is:
CREATE TABLE child_table
(
column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
...
CONSTRAINT fk_name
FOREIGN KEY (child_col1, child_col2, ... child_col_n)
REFERENCES parent_table (parent_col1, parent_col2, ... parent_col_n)
ON DELETE CASCADE
[ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ]
);
For more read this
In design just use on delete cascade
CREATE TABLE child_table
(
column1 datatype [ NULL | NOT NULL ],
column2 datatype [ NULL | NOT NULL ],
...
CONSTRAINT fk_name
FOREIGN KEY (child_col1, child_col2, ... child_col_n)
REFERENCES parent_table (parent_col1, parent_col2, ... parent_col_n)
ON DELETE CASCADE
[ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ]
);
Now when you delete parent . child will automatically deleted... you don't need to do any thing
check Link for detail
On delete cascade
As I don't like to DELETE any row from related tables, I suggest you this solution:
Add a status field with default value of 1 to your table(s).
Create a VIEW that shows only rows with status <> 0 and use this VIEW to show your valid data.
For parent-child or related tables just show rows with status <> 0 for both of parent and child table like parent.status * child.status <> 0.
[Optional & additional]* Create a log table or a journal for your database or your tables or just your important tables and store some actions like Create, Edit\Modify, Delete, Undelete and so on.
With this solution you can:
Support Undo and Redo.
Support Undelete action!
Be not worry about a child that has no parent.
*Found old data, changes of data and many other information.
And many other benefits and you just store more data that it is not concern with a good RDBMS.
I use DELETE just for a table that is at the end child point and its data is not so important.

Cascade delete on duplicate foreign keys? Is that possible?

My MSSQL DB design is as follows:
One table with Images, image_id = PK One table with Videos, video_id
= PK One table with Comments, comment_id = PK, and two fields that uniquely match the parent: item_id (equals either image_id or
video_id) and item_type which tells me who is the parent (either an
image or a video).
How can I add a Cascading Delete, so that when I delete an image, it automatically deletes the comments matching Images.image_id = Comments.item_id AND Comments.item_type = 'image'? As you see, for this to work I have to specify both the item type and item_id since another comment can exist on the same item_id but with different item_type attribute.
Use a TRIGGER, This allows you to include the necessary c.item_type = 'image' filter:
CREATE TRIGGER [dbo].[TrgImagesDelete] ON [dbo].[Images] FOR DELETE
AS
BEGIN
DELETE c
FROM
Comments c
JOIN Deleted d
ON c.item_id = d.image_id
AND c.item_type = 'image'
END
GO

Resources