Cascade delete on duplicate foreign keys? Is that possible? - sql-server

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

Related

Dependency in SQL

In SQL, I've to delete a data from table A which is dependent on table B.
The data to be deleted should satisfy two conditions WorkArea='123' and FileNo='45'.
Table B has WorkArea but it does not contain data for FileNo.
And Table A contains the record satisfying both the conditions.
There isn't any reference key. For more clarity, adding a query here:
Select * from table A where WorkArea='123' and FileNo='45';
This will generate the resulting record. But as it is dependent on Table B, I cannot delete it directly. Also, to delete it from table B isn't possible because data in WorkArea is a whole and it contains many files and I have to delete a specific File.
So how can I delete data from table A?
This is Table A with col1 and col2 as primary key.
This is Table B with col1 as a primary key.
If you have no Foreign Keys, the following sentence will work.
DELETE FROM [A] WHERE [WorkArea] = '123' AND [FileNo] = '45';
Then you can programmaticaly check if there are "orphans" on table B with the following request :
SELECT DISTINCT [B].[WorkArea]
FROM [B]
LEFT JOIN [A]
ON [A].[WorkArea] = [B].[WorkArea]
WHERE [A].[WorkArea] IS NULL
To enhance this last part and produce a DELETE sentence from it, just store the result of this request into a temporary table then use it as a WHERE statement with the IN keyword.

Delete empty rows in one-to-many relationship (postgresql)

Table book (book_id, title) is the main table.
Table book_version (version_id, book_id,publisher) is another table
book_id is primary key to the book and foreign key (on update cascade on delete cascade) to book_version. They have a one-to-many relationship, since there may be several versions of the same book
Obviously, to delete all versions of a book: DELETE FROM book WHERE book_id = $some_id
However, if I want to delete a specific version we have two cases:
There are several versions of this book, so just delete entry based on version_id: DELETE FROM book_version WHERE version_id = $some_id
There is one single version so far. Not only the row in the book_version has to be deleted, but also the row in the book table
I know it can be done using count (I count the number of rows in book_version. If it is more than one, go to case 1. Else go to case 2). But I'd like to make this happen in a single query, without including two different queries or (if possible) an if statement. Maybe I could do something in the database structure - it doesn't have to be because of the query. Any ideas are much appreciated.
It can be done in a single plain SQL query. SQL Fiddle
with dv as (
delete from book_version
where version_id = 2
returning book_id, version_id
)
delete from book b
using dv
where
b.book_id = dv.book_id
and
not exists (
select version_id
from book_version
where
book_id = dv.book_id
and
version_id != dv.version_id
)
Maybe a trigger could be used here.
Something like:
CREATE TRIGGER deleteIfLastVersionIsDeleted AFTER DELETE ON book_version ...

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.

Foreign Key fails to create

I want a foreign key between 2 tables , so i try it like i always do. Now the issue i'm having is he fails to create , and by the looks of it it fails to create because there is already a key but there isnt.
- Unable to create relationship
'FK_tbl_Paramed_RegistratieBehandelingen_Users'.
The ALTER TABLE statement conflicted with the
FOREIGN KEY constraint "FK_tbl_Paramed_RegistratieBehandelingen_Users".
The conflict occurred in database "Nestor_Server",
table "dbo.Users", column 'UserID'.
Ive checked if they have the same type , they do(bigint) so don't get why he won't create it
It is possible that you have records in RegistratieBehandelingen(Not sure about the table name) which is not present in Users Table.
select * from RegistratieBehandelingen a where UserID IS NULL or
not exists (select 1 from Users b where b.UserID= a.UserID)
This means that you have child data with no matching parent ID.
Run the following to see if you get any results:
SELECT *
FROM tbl_Paramed_RegistratieBehandelingen r
LEFT JOIN Users u on r.UserID = u.UserID
WHERE u.UserID IS NULL
(changing table and column names where appropriate)
If you get any results then it should show which records contains UserIDs that don't match to Users.
After the above query, you may want to delete non existing UserId from table tbl_Paramed_RegistratieBehandelingen or insert them in table Users .

Cascade Triggers in SQLite

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

Resources