Avoid Database Cursor in SQL Server - sql

I have a bit of a puzzle (at least for me) which I am hoping is mostly because I am not yet an SQL master of the universe. Basically I have three tables:
Table A, Table B, and Table C.
Table C has a FK (Foriegn Key) to Table B, which has FK to Table A. (Each of these is many to one)
I need to remove an entry from Table A and of course all of it's corresponding entries from Tables B and C. In the past I've used a cursor to do this, selecting all the entries in Table B and cycling through each one to delete all their corresponding entries in Table C. Now this works - and has been working fine, but I suspect/hope there is a better way to achieve this effect without the use of cursors. So that's my question - how can I do this without using a cursor, or can it be done?
(Please let me know if I haven't been clear - I'll try to fix up the question).

Declare your FOREIGN KEYs as ON DELETE CASCADE

You could do this a couple ways...
You could just use cascading deletes on your foreign keys.
CREATE TABLE TableB
(FKColumn INT,
CONSTRAINT MyFk FOREIGN KEY (FKColumn)
REFERENCES TableA(PKColumn) ON DELETE CASCADE)
You could use delete triggers on each table to delete the related records.
CREATE TRIGGER cascade_triggerA
ON TableA
FOR DELETE
AS
BEGIN
DELETE TableB
FROM TableB JOIN DELETED ON TableB.FKColumn = DELETED.PKColumn
END
CREATE TRIGGER cascade_triggerB
ON TableB
FOR DELETE
AS
BEGIN
DELETE TableC
FROM TableC JOIN DELETED ON TableC.FKColumn = DELETED.PKColumn
END
If you're using MS SQL server, you could also use INSTEAD OF DELETE triggers. In this case, you'd create the trigger just on TableA - and in the trigger put all of the logic to delete the records from all 3 tables.
In any of the above cases, you'd just delete the record from table A, and let the cascading and triggers take care of the rest.

The answers already given (Cascading Deletes and Triggers) are great, but you might work in an environment where these are not an option. If so, below is a purely SQL solution. The example is solely concerned with the DELETE syntax. In the real world you'd probably wrap this within a transaction and implement it as a stored procedure.
--
DECLARE #Param_PK_TableA int
SET #Param_PK_TableA = 1500
-------------------------------
-- TABLE C --------------------
DELETE TableC
FROM TableC
INNER JOIN TableB
ON TableB.TableB_ID = TableC.TableB_ID
INNER JOIN TableA
ON TableA.TableA_ID = TableB.TableA_ID
WHERE
(TableA.TableA_ID = #Param_PK_TableA)
-------------------------------
-- TABLE B --------------------
DELETE TableB
FROM TableB
INNER JOIN TableA
ON TableA.TableA_ID = TableB.TableA_ID
WHERE
(TableA.TableA_ID = #Param_PK_TableA)
-------------------------------
-- TABLE A --------------------
DELETE TableA
WHERE
(TableA.TableA_ID = #Param_PK_TableA)

When you create the foreign key relationship for both tables you can specify ON DELETE CASCADE and it will take care of this for you when you delete a record in A.

Related

I want to delete the records from a parent table using child table in postgresql

I have a table (A) having 200,000 records. I have created a backup table (B) from the same table (A) which contains around 100,000 records. Now I have to delete all the records which are present in table B from the parent table A.
I am using the below query:
delete from A where id in (select id from B);
The query which I am using is taking a lot of time, the delete is happening very slowly. Could someone please help me in reducing the time taken while deleting the records??
Any help will be appreciated.
How about creating a table with the records you want to keep, dropping the old A table, and renaming the new one back to A?
A query like:
Create table C
Select A.*
From A Left Outer Join B ON A.id=B.id
Where B.id is null
should perform well if id is indexed.

Update a database record rowversion by not touching the record with SQL UPDATE?

I have two tables TableA and TabelB in SQL Server, there is a foreign key relationship between them, and TableA defined a rowversion column to synchronize things.
It's required to update the rowversion of TableA if the corresponding record in TableB changed. Currently it's implemented with the following SQL code in the TableB update trigger:
UPDATE TableA
SET Id = Id
WHERE Id = #id
This change brought really bad performance to my app because of a Table Spool. And update other columns in TableB will trigger different behavior.
So I want to know if there is any other way to update the rowversion in TableA by not updating a TableA column? Any comment is much appreciated.

Trigger delete on many-to-many middle table deletion

There are two tables A and B. As they have a many-to-many relation, there's also table C.
A
------
id PK
B
------
id PK
C
------
id_A PK
id_B PK
Now, a row B only exists only exists when at least one row of A has a relation to it, and one B may contain a relation to two or more different rows of A.
My question is, how do i automatically delete a row from B if there isn't any foreign to it in C? My initial though was to set a trigger, but i'm not to sure about this and i'd want a second opinion in how to proceed. Thank you.
First, one assumes that the data is initially set up correctly. That is, the only b records are the ones that meet your condition.
Then, the solution involves triggers on table c. When a row is deleted, it would check:
Does id_b have any other rows in the table?
If not, then delete the row.
This can actually be a bit tricky. In general, you don't want to query the table being triggered. So, I might suggest an alternative approach:
Add a counter on b.
Add insert/update/delete triggers on c that increments or decrements the count in b.
If the counter is 0 (or 1 before decrementing), then delete the row.
Gosh, you might find that the counter itself is sufficient, and there is no need to actually delete the row. You can get that effect if you use a view:
create view v_b as
select b.*
from b
where ab_counter > 0;
You could also create a view on b and not have to deal with triggers at all:
create view v_b as
select b.*
from b
where exists (select 1 from c where c.b_id = b.id);
#Gordon's solution above is great, However a slight modification might help.
First, one assumes that the data is initially set up correctly. That is, the only b records are the ones that meet your condition.
Then, the solution involves triggers on table c. When a row is deleted, it would check:
Does id_b have any other rows in the table?
If not, then delete the
row.
This is a bit tricky because you have to check if other rows exist. This check can be automated by using,
FOREIGN KEY(id) REFERENCES B(id) ON DELETE RESTRICT
on table C. Now you only need to delete row from B in the trigger without any checks, since the restrict constraint will automatically check if row exists in table C and restrict the delete of a referenced row in table B else delete is successful.

Delete multiple rows of data from multiple tables in SQL

I need help of making a SQL query, when I delete the main category it should delete all the data of my subcategory and all the products related to that subcategory. Basically deleting data from 3 tables.
Can it be done in one query?
Cant give you code sample without a Database reference. But if all the tables have the same Primary key you can try using INNER JOIN.
DELETE table1, table2, table3
FROM table1
INNER JOIN table2 ON table2.key = table1.key
INNER JOIN table3 ON table3.key = table1.key
WHERE table1.key = value;
key value should be common accross all tables. Something like a "ID".
Instead of solving it via Sql, you could use foreign keys with the ON DELETE CASCADE setting enabled.
If you database type/version supports it.
F.e. ANSI SQL:2003, MySSQL, Oracle, PostgreSQL, MS SQL Server, SQLite
That way, when you delete from the main table. Then the records in the other table that reference to those deleted records, will automatically be deleted also.
For example in MS Sql Server:
ALTER TABLE SubCategory
ADD CONSTRAINT FK_SubCategory_MainCategoryID_Cascade
FOREIGN KEY (MainCategoryID)
REFERENCES MainCategory(ID) ON DELETE CASCADE;
ALTER TABLE Products
ADD CONSTRAINT FK_Products_SubCategoryID_Cascade
FOREIGN KEY (SubCategoryID)
REFERENCES SubCategory(ID) ON DELETE CASCADE;
It's a way to automatically maintain the referential integrity between them.
After that a delete from the MainCategory table will delete the related records from the SubCategory table. And also the Products that are related to the deleted SubCategory records.
And here is a db<>fiddle example for SQLite to demonstrate.
P.S. Personally I would prefere an ON DELETE SET NULL or an ON DELETE SET DEFAULT n, or even using triggers. Sure, that would require an extra cleanup of the unreferenced records afterwards, f.e. via a scheduled script. But it just feels less detrimental. Because then it's easier to fix when someone accidently deleted a MainCategory that shouldn't have been deleted.

SQL Oracle | How to delete records from a table when they match another table?

How would I delete records from a table where they match a delete table? As in, I have a table of record keys that say what need to be deleted from my main table. How would I write a delete to say "delete anything from my main table where this field matches a field in my delete table?"
If it's a small delete table:
delete from TableA A
where a.key in ( select key from deleteTable );
If it's a bigger table, you can try an EXISTs:
delete from TableA A
where exists ( select * from deleteTable d
where d.key = A.key );
All this depends of course, on your indexes and sizes of tables ... etc ....
(and if it gets really big, you might want to consider another option .. ie partitioning, rebuild table, etc.)