简体   繁体   中英

How to Delete data in Both Tables in SQL Server?

I have a table TableA, its PK is AId. I have another table TableB, its only column is BId. BId is actually is a subset of AId. Now, we can not use FK (because of this and that reason).

How can I Delete the TOP 1000 rows in TableB and its related rows (if the same id exists in table A) in table A?

A solution is to SELECT TOP 100) from tableB and save to temp table, then use it to delete the data in table A, then delete rows in tableB.

But it should have a more efficient way to do this.

I also tried:

DELETE TOP (1000) FROM tableA WHERE AId IN (SELECT BId FROM TableB)

But how can I make sure the top 1000 BId got deleted?

Thanks

One way to go would be to create a table variable, stored the 1000 ID's and reference that in both deletes. Of course I have no idea if they're integers or not, so change the data type if required. There are other ways, as I'm sure others will point out but this is my standard approach if I need consistency.

DECLARE @keyValues TABLE (keyValue INT);

INSERT INTO @keyValues
SELECT TOP 1000 FROM TABLEA WHERE AID IN (SELECT BID FROM TABLEB);

DELETE FROM TABLEB WHERE BID IN (SELECT keyValue FROM @keyValues);
DELETE FROM TABLEA WHERE AID IN (SELECT keyValue FROM @keyValues);

In general, one DELETE statement can change data only in one table. So, in general, you need two separate DELETE statements to delete rows from two tables.

Technically, you don't have to write BId to a temp table, just read them twice from TableB .

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

DELETE FROM TableA
WHERE TableA.AId IN
(
    SELECT TOP(1000) TableB.BId
    FROM TableB
    ORDER BY TableB.BId
)
;

DELETE FROM TableB
WHERE TableB.BId IN
(
    SELECT TOP(1000) TableB.BId
    FROM TableB
    ORDER BY TableB.BId
)
;

COMMIT TRANSACTION;

You need to make sure that the set of 1000 BId are the same in both queries, which means that you have to use ORDER BY with TOP . Also, you should do something to prevent changes to TableB by another process between two DELETE statements.

In the end, taking care of concurrency issues (for example, by setting the transaction isolation level to serializable) may incur more overhead than writing these 1000 Ids into a temp table.


On the other hand, you said "BId is actually is a subset of AId". So, TableA is master, TableB is detail.

Technically, you can define a Foreign Key with ON DELETE CASCADE option. It is not clear to me from the question why you can't use foreign key constraint.

ALTER TABLE TableB WITH CHECK ADD CONSTRAINT [FK_TableB_TableA] FOREIGN KEY(BId)
REFERENCES TableA(AId) ON DELETE CASCADE
GO

ALTER TABLE TableB CHECK CONSTRAINT [FK_TableB_TableA]
GO

Then you will delete only from TableA and child rows from TableB would be deleted by the foreign key constraint. Whether that would be more efficient than two explicit DELETEs is hard to tell - you need to test yourself on your hardware.

In this case one explicit DELETE is enough:

DELETE FROM TableA
WHERE TableA.AId IN
(
    SELECT TOP(1000) TableB.BId
    FROM TableB
    ORDER BY TableB.BId
)
;

This will take care of concurrency issues as well.

other possible ways:

1) DELETE TOP (1000) from TableB with Output clause (into temp table), and then Delete from TableA where AID is in that output table.

or

2) Implement DELETE trigger for TableB, so when any rows are deleted from TableB, they are also deleted from TableA automatically.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM