Join query to delete a record both in child class and parent class - sql

I need to delete a record in both child table and parent table with the reference to another column.The primary key of one table is equal to foreign key of another table.eg.table A.pkid=table B.fkid
How do I use join query or cascade to delete it.
I tried couple of queries but it shows invalid syntax.
delete from
table A table B where pkid=(SELECT fk_id from table B)
where name='SEP' from table B
delete from
table A join table B ON table A.fk_id=(SELECT pk_id FROM table B)
where name='SEP' from table B
delete from
table A join table B ON A.fk_id=B.pk_id
where name='SEP' from table B
Could you please refine my query or give me a link where I can get some help on this? Thanks a lot.

It looks like all you need is something simple:
BEGIN WORK;
DELETE FROM A WHERE fk_id IN (SELECT pk_id FROM B WHERE name = 'SEP');
DELETE FROM B WHERE name = 'SEP';
COMMIT WORK;
The alternative is to define the PK-FK relationship in the schema with the ON DELETE CASCADE option, and children will be automatically deleted on deletion of the parent.

Related

DELETE request on row with nested foreign keys

I have a Table A that is referenced to by Table B, and Table C has references to Table B.
Such that:
Table A (pk: id)
Table B (pk: id, fk: A_id)
Table C (fk: B_id)
Every foreign key has a constraint ON_DELETE:CASCADE, however if I attempted to delete Table A - it refuses to. This is happening because rows in Table B have foreign keys pointing to Table A, and Table C has rows pointing to Table B.
I'm confident that I can resolve this issue by deleting Table B first, and then deleting Table A. However, would someone explain why this DELETE isn't possible? Surely, it should be able to perform a function of going down a 'hierarchy' of foreign keys and deleting everything in order?
Thanks
In ON DELETE CASCADE operation, if a parent row is deleted, it will first delete the child row and then the parent row so as to ensure that no references are left as foreign key
As for your question, since Table B is referring to Table A 's primary key and Table C is referring to Table B's primary key, the relationship for which on delete cascade should work is just a parent child and not beyond.

On delete cascade for self-referencing table

I have a comment table that is self-referencing.
I tried to write on delete cascade but it take some exception
Introducing FOREIGN KEY constraint 'FK_Comments_Comments' on table 'Comments' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
and then try to write a trigger but it take exception again
CREATE TRIGGER [dbo].[T_comment_Trigger]
ON [dbo].[Comments]
FOR DELETE
AS
DELETE FROM Comments
WHERE ParentId =(SELECT deleted.id FROM deleted)
couldn't delete rows that have children
how can I do on delete cascade for my self-referencing table?
Assuming you're keeping your FOREIGN KEY constraint in place, you cannot fix the issue in a FOR DELETE trigger. FOR triggers (also known as AFTER triggers) fire after the activity has taken place. And a foreign key will prevent a row from being deleted if it has references. Foreign key checks occur before deletion.
What you need is an INSTEAD OF trigger. You also need to bear in mind that your current trigger only tried to deal with one "level" of referencing. (So, if row 3 references row 2 and row 2 references row 1, and you delete row 1, your trigger only tried to remove row 2)
So, something like:
CREATE TRIGGER [dbo].[T_comment_Trigger]
ON [dbo].[Comments]
INSTEAD OF DELETE
AS
;WITH IDs as (
select id from deleted
union all
select c.id
from Comments c
inner join
IDs i
on
c.ParentID = i.id
)
DELETE FROM Comments
WHERE id in (select id from IDs);
If there are other (non-self-referencing) cascading foreign key constraints, they all have to be replaced by actions in this trigger. In such a case, I'd recommend introducing a table variable to hold the list of all IDs that will eventually be deleted from the Comments table:
CREATE TRIGGER [dbo].[T_comment_Trigger]
ON [dbo].[Comments]
INSTEAD OF DELETE
AS
declare #deletions table (ID varchar(7) not null);
;WITH IDs as (
select id from deleted
union all
select c.id
from Comments c
inner join
IDs i
on
c.ParentID = i.id
)
insert into #deletions(ID)
select ID from IDs
-- Delete from FK referenced table
DELETE FROM OtherTable
WHERE CommentID in (select ID from #deletions)
--This delete comes last
DELETE FROM Comments
WHERE id in (select ID from #deletions);

SQL Server using triggers and geting rid of ON DELETE CASCADE

I have 2 tables, A and B.
Table B has a foreign key pointing to primary key of table A. The foreign key on table B has ON DELETE CASCADE, so the corresponding records from B is deleted when a record from A is deleted.
My requirement is to track all added/updated/deleted records in history tables. I have trigger on each table to insert the records into history tables(AHistories and BHistories tables).
I do not like the order ON DELETE CASCADE deletes the records. Trigger A is executed after trigger B, so I have to work around to get the ID of AHistory into BHistory record.
I am wanting to get rid of ON DELETE CASCADE and perform Delete on the records of B in trigger A then insert the deleted record of B into BHistories there.
To demonstrate the idea, I made the case simple, but I have a few more tables that have a foreign key pointing to the primary key in table A. Personally, I would like if I can specify the order and what I do on delete cascade.
Does this stink as an approach? Any comments are appreciated.
As bad as triggers are but sometimes they are the only way to implement complex business requirements. I would do something as follows in the following example PK_ID refers to Primary Key Column.
CREATE TRIGGER tr_Table_A_InsteadOfDelete
ON dbo.TableA
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
-- Insert into History Table from TableB
INSERT INTO TABLE_B_History
SELECT B.*
FROM TableB B INNER JOIN deleted d
ON B.PK_ID = d.PK_ID
-- Delete rows from TableB
DELETE FROM TableB
WHERE EXISTS (SELECT 1
FROM deleted
WHERE PK_ID = TableB.PK_ID)
-- Insert into History Table from TableA
INSERT INTO TABLE_A_History
SELECT A.*
FROM TableA A INNER JOIN deleted d
ON A.PK_ID = d.PK_ID
-- Delete rows from TableA
DELETE FROM TableA
WHERE EXISTS (SELECT 1
FROM deleted
WHERE PK_ID = TableA.PK_ID)
COMMIT TRANSACTION;
END
Using triggers for audit purposes is as obvious as it's wrong. If you have sufficient permissions, they can be disabled too easily.
Nevertheless, what you want to do can be achieved with the combination of:
INSTEAD OF DELETE triggers, where you can delete or move rows around in any order;
Disabling nested triggers. This might has unpleasant side effects, if you use triggers for more than just the audit, because it's a database-wide option.
Just don't forget that in such a trigger, you have to manually delete rows from its underlying table, as well.

sql query to delete by category

ok guys, may be it's not the best title for my problem. so this is the case
i have 2 table in my database,
Parent table
---------
id name
---------
0 A
1 B
2 C
Child table
-------------------------
id name parent_id
-------------------------
0 child_A 1
1 child_B 0
2 child_C 2
so if record A in parent table deleted, how can i delete all child who have parent_id 0 (A have id 0) ??
i hope you can understand my problem and help me .. :) thanks in adv .. :)
i think i've got the solution, first i try to get all field that not have parent anymore
by this query
SELECT * FROM folders C where NOT EXISTS (select * from folders as A INNER JOIN gis_folders as B on (A.parent_id = B.id) where A.id= C.id )
now i just have to delete all the rows on result from that query .. :)
This is called a cascading delete. When you set up the foreign keys you can specify that child rows are deleted.
See section 4.3 of http://www.sqlite.org/foreignkeys.html for more details.
Do this if you don't have a foreign key set up
DELETE parent, child
FROM parent LEFT JOIN child ON parent.id = child.parent_id
WHERE parent.id = 0
I think the best way is to create a trigger on deleted of parent A
and there you can get the deleted parent id and write query to delete from B
similarly create a trigger on delete of Parent B and there again get id from deleted row and delete all rows from C
Othere way could be create a procedure take a id of parent A as parameter
now writer query like
delete from C where Cid in (select Cid from c where CparentID in (select Bid from B where BparentID = aid))
delete from B where BparentID = aid
delete from A where aid = aid
you can also refere to this
http://msdn.microsoft.com/en-us/library/aa933119%28SQL.80%29.aspx
CREATE TABLE tblparent(
id INTEGER PRIMARY KEY auto_increment,
name VARCHAR(50)
)engine=innodb;
CREATE TABLE tblchild(
id INTEGER PRIMARY KEY auto_increment,
name VARCHAR(50),
parent_id INTEGER,
FOREIGN KEY(parent_id) REFERENCES tblparent(id) ON DELETE CASCADE
)engine=innodb;
In that case, when the parent row is deleted, all the rows on the referenced child table that referred on that id will also deleted.
That is called cascading delete.

SQL query to combine existence check and condition check

I have 2 tables, call A and B. A has a foreign key on B. call them A_ID and B_ID respectively. But the constraint not enforced in the design. I am not supposed to change the schema. I need to delete entries from table A based on 2 conditions.
1)If table B doesn't contain A_ID
2)If some condition on B is met.
I have formed a query something like this. But I dont think its optimal. Is there a better way of doing this?
delete from A where A_ID not in (select B_ID from B where status='x' )
or A_ID not in (select B_ID from B)
You could use not exists to delete rows without a matching entry in table B. This one treats status = 'x' as if no match was found, i.e. it will delete those rows:
delete A
where not exists
(
select *
from B
where B.B_ID = A.A_ID
and status <> 'x'
)
JustABitOfCode and UltraCommit told about omitting one part
furthermore, if it's a foreign key, you can say to keep deleting unwanted A in definition:
CREATE TABLE A
(
uniqeidentifire A_ID
, FOREIGN KEY (A_ID) REFERENCES B(B_ID) ON DELETE CASCADE
);
This will Automatically delete each A that does not have a B match
and this is more efficient
As just explained from JustABitOfCode, please remove the condition:
(select B_ID from B where status='x')
because it is redundant: the result set of the previous select, is a SUBSET of the result set of the following select:
(select B_ID from B)